From db7d30c51501f0e394002ad81bf79b9a286cc812 Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Fri, 15 Mar 2024 09:33:15 +0000 Subject: [PATCH] add nonce_layer and signer_layer --- README.md | 13 ++--- examples/layers/Cargo.toml | 24 +++++++++ examples/layers/examples/nonce_layer.rs | 63 +++++++++++++++++++++++ examples/layers/examples/signer_layer.rs | 65 ++++++++++++++++++++++++ 4 files changed, 155 insertions(+), 10 deletions(-) create mode 100644 examples/layers/Cargo.toml create mode 100644 examples/layers/examples/nonce_layer.rs create mode 100644 examples/layers/examples/signer_layer.rs diff --git a/README.md b/README.md index 3d3de244..7a8f10b6 100644 --- a/README.md +++ b/README.md @@ -38,16 +38,9 @@ cargo run --example mnemonic_signer - [ ] Events - [ ] Logs and filtering - [ ] Solidity topics -- [ ] Middleware - - [ ] Builder - - [ ] Create custom middleware - - [ ] Gas escalator - - [ ] Gas oracle - - [ ] Nonce manager - - [ ] Policy - - [ ] Signer - - [ ] Time lag - - [ ] Transformer +- [x] Layers + - [x] [Nonce manager](./examples/layers/examples/nonce_layer.rs) + - [x] [Signature manager](./examples/layers/examples/signer_layer.rs) - [ ] Providers - [ ] Http - [ ] IPC diff --git a/examples/layers/Cargo.toml b/examples/layers/Cargo.toml new file mode 100644 index 00000000..a15ec2dc --- /dev/null +++ b/examples/layers/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "examples-layers" + +publish.workspace = true +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[dev-dependencies] +alloy-network.workspace = true +alloy-node-bindings.workspace = true +alloy-primitives.workspace = true +alloy-provider.workspace = true +alloy-rpc-client.workspace = true +alloy-rpc-types.workspace = true +alloy-signer-wallet.workspace = true +alloy-transport-http.workspace = true + +reqwest.workspace = true +tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } diff --git a/examples/layers/examples/nonce_layer.rs b/examples/layers/examples/nonce_layer.rs new file mode 100644 index 00000000..d8914a2a --- /dev/null +++ b/examples/layers/examples/nonce_layer.rs @@ -0,0 +1,63 @@ +//! Example of using the `ManagedNonceLayer` in the provider. + +use alloy_network::EthereumSigner; +use alloy_node_bindings::Anvil; +use alloy_primitives::{address, U256, U64}; +use alloy_provider::{layers::ManagedNonceLayer, Provider, ProviderBuilder, RootProvider}; +use alloy_rpc_client::RpcClient; +use alloy_rpc_types::request::TransactionRequest; +use alloy_signer_wallet::LocalWallet; +use alloy_transport_http::Http; +use reqwest::Client; + +/// In Ethereum, the nonce of a transaction is a number that represents the number of transactions +/// that have been sent from a particular account. The nonce is used to ensure that transactions are +/// processed in the order they are intended, and to prevent the same transaction from being +/// processed multiple times. +/// +/// The nonce manager in Alloy is a layer that helps you manage the nonce +/// of transactions by keeping track of the current nonce for a given account and automatically +/// incrementing it as needed. This can be useful if you want to ensure that transactions are sent +/// in the correct order, or if you want to avoid having to manually manage the nonce yourself. +#[tokio::main] +async fn main() -> Result<(), Box> { + // Spin up a local Anvil node. + // Ensure `anvil` is available in $PATH + let anvil = Anvil::new().spawn(); + + // Set up the wallets. + let wallet: LocalWallet = anvil.keys()[0].clone().into(); + let from = wallet.address(); + + // Create a provider with a signer and the network. + let http = Http::::new(anvil.endpoint().parse()?); + let provider = ProviderBuilder::new() + .layer(ManagedNonceLayer) + .signer(EthereumSigner::from(wallet)) + .provider(RootProvider::new(RpcClient::new(http, true))); + + let tx = TransactionRequest { + from: Some(from), + value: Some(U256::from(100)), + to: address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into(), + gas_price: Some(U256::from(20e9)), + gas: Some(U256::from(21000)), + ..Default::default() + }; + + let pending = provider.send_transaction(tx.clone()).await?; + let tx_hash = pending.watch().await?; + let mined_tx = provider.get_transaction_by_hash(tx_hash).await?; + assert_eq!(mined_tx.nonce, U64::from(0)); + + println!("Transaction sent with nonce: {}", mined_tx.nonce); + + let pending = provider.send_transaction(tx).await?; + let tx_hash = pending.watch().await?; + let mined_tx = provider.get_transaction_by_hash(tx_hash).await?; + assert_eq!(mined_tx.nonce, U64::from(1)); + + println!("Transaction sent with nonce: {}", mined_tx.nonce); + + Ok(()) +} diff --git a/examples/layers/examples/signer_layer.rs b/examples/layers/examples/signer_layer.rs new file mode 100644 index 00000000..16a93364 --- /dev/null +++ b/examples/layers/examples/signer_layer.rs @@ -0,0 +1,65 @@ +//! Example of using the `SignerLayer` in the provider. + +use alloy_network::EthereumSigner; +use alloy_node_bindings::Anvil; +use alloy_primitives::{address, b256, U256, U64}; +use alloy_provider::{Provider, ProviderBuilder, RootProvider}; +use alloy_rpc_client::RpcClient; +use alloy_rpc_types::request::TransactionRequest; +use alloy_signer_wallet::LocalWallet; +use alloy_transport_http::Http; +use reqwest::Client; + +#[tokio::main] +async fn main() -> Result<(), Box> { + // Spin up a local Anvil node. + // Ensure `anvil` is available in $PATH + let anvil = Anvil::new().spawn(); + + // Set up the wallets. + let wallet: LocalWallet = anvil.keys()[0].clone().into(); + + // Create a provider with a signer and the network. + let http = Http::::new(anvil.endpoint().parse()?); + let provider = ProviderBuilder::new() + .signer(EthereumSigner::from(wallet)) + .provider(RootProvider::new(RpcClient::new(http, true))); + + let tx = TransactionRequest { + nonce: Some(U64::from(0)), + value: Some(U256::from(100)), + to: address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into(), + gas_price: Some(U256::from(20e9)), + gas: Some(U256::from(21000)), + ..Default::default() + }; + + let builder = provider.send_transaction(tx).await?; + let node_hash = *builder.tx_hash(); + + println!( + "Node hash matches expected hash: {}", + node_hash == b256!("eb56033eab0279c6e9b685a5ec55ea0ff8d06056b62b7f36974898d4fbb57e64") + ); + + let pending = builder.register().await?; + let pending_transaction_hash = *pending.tx_hash(); + + println!( + "Pending transaction hash matches node hash: {}", + pending_transaction_hash == node_hash + ); + + let transaction_hash = pending.await?; + assert_eq!(transaction_hash, node_hash); + + println!("Transaction hash matches node hash: {}", transaction_hash == node_hash); + + let receipt = + provider.get_transaction_receipt(transaction_hash).await.unwrap().expect("no receipt"); + let receipt_hash = receipt.transaction_hash.expect("no receipt hash"); + + println!("Receipt hash matches node hash: {}", receipt_hash == node_hash); + + Ok(()) +}