Skip to content

Commit

Permalink
Merge pull request #9 from alloy-rs/zerosnacks/middleware
Browse files Browse the repository at this point in the history
feat(layers): Port `middleware` (layers) from `ethers-rs`
  • Loading branch information
zerosnacks authored Mar 25, 2024
2 parents 364eafe + e225469 commit e82c1e6
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 10 deletions.
13 changes: 3 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,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
Expand Down
18 changes: 18 additions & 0 deletions examples/layers/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[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.workspace = true

eyre.workspace = true
reqwest.workspace = true
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
67 changes: 67 additions & 0 deletions examples/layers/examples/nonce_layer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//! Example of using the `ManagedNonceLayer` in the provider.
use alloy::{
network::{EthereumSigner, TransactionBuilder},
node_bindings::Anvil,
primitives::{address, U256},
providers::{layers::ManagedNonceLayer, Provider, ProviderBuilder},
rpc::{client::RpcClient, types::eth::request::TransactionRequest},
signers::wallet::LocalWallet,
};
use eyre::Result;

/// 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<()> {
// Spin up a local Anvil node.
// Ensure `anvil` is available in $PATH
let anvil = Anvil::new().try_spawn()?;

// Set up the wallets.
let wallet: LocalWallet = anvil.keys()[0].clone().into();
let from = wallet.address();

// Create a provider with the signer.
let http = anvil.endpoint().parse()?;
let provider = ProviderBuilder::new()
// Add the `ManagedNonceLayer` to the provider
.layer(ManagedNonceLayer)
.signer(EthereumSigner::from(wallet))
.on_client(RpcClient::new_http(http));

// Create an EIP-1559 type transaction.
let tx = TransactionRequest::default()
.with_from(from)
.with_to(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into())
.with_value(U256::from(100))
.with_gas_limit(U256::from(21000))
.with_max_fee_per_gas(U256::from(20e9))
.with_max_priority_fee_per_gas(U256::from(1e9))
.with_chain_id(anvil.chain_id());

// Send the transaction, the nonce (0) is automatically managed by the provider.
let builder = provider.send_transaction(tx.clone()).await?;
let node_hash = *builder.tx_hash();
let pending_transaction = provider.get_transaction_by_hash(node_hash).await?;
assert_eq!(pending_transaction.nonce, 0);

println!("Transaction sent with nonce: {}", pending_transaction.nonce);

// Send the transaction, the nonce (1) is automatically managed by the provider.
let builder = provider.send_transaction(tx).await?;
let node_hash = *builder.tx_hash();
let pending_transaction = provider.get_transaction_by_hash(node_hash).await?;
assert_eq!(pending_transaction.nonce, 1);

println!("Transaction sent with nonce: {}", pending_transaction.nonce);

Ok(())
}
68 changes: 68 additions & 0 deletions examples/layers/examples/signer_layer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//! Example of using the `SignerLayer` in the provider.
use alloy::{
network::{EthereumSigner, TransactionBuilder},
node_bindings::Anvil,
primitives::{address, b256, U256},
providers::{Provider, ProviderBuilder},
rpc::{client::RpcClient, types::eth::request::TransactionRequest},
signers::wallet::LocalWallet,
};
use eyre::Result;

#[tokio::main]
async fn main() -> Result<()> {
// Spin up a local Anvil node.
// Ensure `anvil` is available in $PATH
let anvil = Anvil::new().try_spawn()?;

// Set up the wallets.
let wallet: LocalWallet = anvil.keys()[0].clone().into();
let from = wallet.address();

// Create a provider with the signer.
let http = anvil.endpoint().parse()?;
let provider = ProviderBuilder::new()
// Add the `SignerLayer` to the provider
.signer(EthereumSigner::from(wallet))
.on_client(RpcClient::new_http(http));

// Create a legacy type transaction.
let tx = TransactionRequest::default()
.with_nonce(0)
.with_from(from)
.with_to(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into())
.with_value(U256::from(100))
.with_gas_price(U256::from(20e9))
.with_gas_limit(U256::from(21000));

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;
assert_eq!(receipt_hash, node_hash);

println!("Transaction receipt hash matches node hash: {}", receipt_hash == node_hash);

Ok(())
}

0 comments on commit e82c1e6

Please sign in to comment.