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 4 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 @@ -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
Expand Down
25 changes: 25 additions & 0 deletions examples/layers/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[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

eyre.workspace = true
reqwest.workspace = true
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
64 changes: 64 additions & 0 deletions examples/layers/examples/nonce_layer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//! 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 eyre::Result;
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<()> {
// 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::<Client>::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()
};
zerosnacks marked this conversation as resolved.
Show resolved Hide resolved

let pending = provider.send_transaction(tx.clone()).await?;
let tx_hash = pending.watch().await?;
zerosnacks marked this conversation as resolved.
Show resolved Hide resolved
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(())
}
67 changes: 67 additions & 0 deletions examples/layers/examples/signer_layer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//! 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 eyre::Result;
use reqwest::Client;

#[tokio::main]
async fn main() -> Result<()> {
// 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::<Client>::new(anvil.endpoint().parse()?);
let provider = ProviderBuilder::new()
// Add the `SignerLayer` to the provider
.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(())
}
Loading