Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(layers): Port middleware (layers) from ethers-rs #9

Merged
merged 17 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(())
}
Loading