diff --git a/Cargo.toml b/Cargo.toml index b0a0f51..7c437b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -98,7 +98,6 @@ needless_return = "allow" alloy = { version = "0.14", features = [ "eips", "full", - "hyper", "json-rpc", "node-bindings", "rpc-client", diff --git a/README.md b/README.md index 59ba32c..e28d611 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ This repository contains the following examples: - [x] [Multicall Builder](./examples/providers/examples/multicall.rs) - [x] [WS with authentication](./examples/providers/examples/ws_with_auth.rs) - [x] [JSON-RPC Batch Request](./examples/providers/examples/batch_rpc.rs) + - [x] [Consensus x RPC types unification](./examples/providers/examples/embed_consensus_rpc.rs) - [x] Queries - [x] [Query contract storage](./examples/queries/examples/query_contract_storage.rs) - [x] [Query contract deployed bytecode](./examples/queries/examples/query_deployed_bytecode.rs) diff --git a/examples/layers/Cargo.toml b/examples/layers/Cargo.toml index f1b05e5..a75b616 100644 --- a/examples/layers/Cargo.toml +++ b/examples/layers/Cargo.toml @@ -13,7 +13,7 @@ repository.workspace = true workspace = true [dev-dependencies] -alloy.workspace = true +alloy = { workspace = true, features = ["hyper"] } eyre.workspace = true tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } diff --git a/examples/providers/examples/embed_consensus_rpc.rs b/examples/providers/examples/embed_consensus_rpc.rs new file mode 100644 index 0000000..efaf1e5 --- /dev/null +++ b/examples/providers/examples/embed_consensus_rpc.rs @@ -0,0 +1,76 @@ +//! This example demonstrates how alloy's RPC types and consensus types are tied together. +//! +//! Consensus types are used in Ethereum execution layer consensus and communication. These include +//! transactions, headers, blocks, [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) envelopes, [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930), [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844), and more. +//! +//! The RPC types are used to communicate with Ethereum nodes. +//! +//! In the case of alloy, the consensus types are embedded into the RPC types unlocking a ton of +//! simplications across these two categories of types and also preventing accidental divergence +//! between the two. +//! This has been achieved without altering the resultant serialized JSON-RPC representations. +//! +//! One can easily convert the RPC types to their consensus counterparts using the `into_consensus`, +//! `into_inner` or in the case of transactions `into_recovered` methods. +//! +//! See: +//! +//! - [Embed consensus `Header` into RPC type](https://github.com/alloy-rs/alloy/pull/1573) +//! - [Embed `TxEnvelope` into RPC `Transaction`](https://github.com/alloy-rs/alloy/pull/1460) +//! - [Embed consenus `Log` and `Receipt` into respective RPC types](https://github.com/alloy-rs/alloy/pull/396) + +use alloy::{ + eips::BlockId, + primitives::b256, + providers::{Provider, ProviderBuilder}, +}; +use eyre::OptionExt; + +#[tokio::main] +async fn main() -> eyre::Result<()> { + let provider = ProviderBuilder::new().connect("https://reth-ethereum.ithaca.xyz/rpc").await?; + + // Get the latest block from the RPC. + let block = provider.get_block(BlockId::latest()).await?.ok_or_eyre("Block not found")?; + // The immediate type returned is the RPC `Block` type which consists of the relevant consensus + // types. + assert!(matches!(block, alloy::rpc::types::Block { .. })); + // This rpc block type contains the RPC `Header` type which encapsulates the consensus `Header` + // type. + // Easily access the consensus `Header` type without having to rebuild into another type. + // If one needs to map the header or transaction to a different type, the following mapping + // methods can be used: `try_map_header`, `map_header`, `try_map_transactions`, + // `map_transactions`. + assert!(matches!(block.header.inner, alloy::consensus::Header { .. })); + // One can use the `into_consensus` method to get the consensus representation of the block. + let consensus = block.into_consensus(); + assert!(matches!(consensus, alloy::consensus::Block { .. })); + + // Similarly, the RPC `Transaction` and `TransactionReceipt` types encapsulate their + // corresponding consensus types. + + // Get a transaction by hash + // + let tx_hash = b256!("0x5b470467985bfd34f18979b5438ffce4f2a309a32bcc857fcbf48c4e4253ce16"); + let tx = + provider.get_transaction_by_hash(tx_hash).await?.ok_or_eyre("Transaction not found")?; + assert!(matches!(tx, alloy::rpc::types::Transaction { .. })); + // The RPC `Transaction` type wraps a `Recovered` containing the consensus `Transaction` + // type. + // The `Recovered` consists of the signer recovered from the signature. + // Unifying these reduces verbosity and allows for any one of the types using simple helper + // methods as shown below. + let recovered_tx = tx.into_recovered(); + assert!(matches!(recovered_tx, alloy::consensus::transaction::Recovered { .. })); + assert!(matches!(recovered_tx.inner(), alloy::consensus::EthereumTxEnvelope::Eip1559(_))); + + let receipt = provider + .get_transaction_receipt(tx_hash) + .await? + .ok_or_eyre("Transaction receipt not found")?; + assert!(matches!(receipt, alloy::rpc::types::TransactionReceipt { .. })); + // The `TransactionReceipt` type contains the consensus `ReceiptEnvelope` type. + assert!(matches!(receipt.inner, alloy::consensus::ReceiptEnvelope::Eip1559(_))); + + Ok(()) +}