From ac43ca2ba3b6aaebf2ccbc956dffb6a8a8cc27d1 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Fri, 21 Mar 2025 18:10:10 +0530 Subject: [PATCH 1/5] chore: use signers instead of `EthereumWallet` --- examples/advanced/examples/any_network.rs | 3 +-- examples/fillers/examples/wallet_filler.rs | 3 +-- examples/providers/examples/builder.rs | 3 +-- examples/subscriptions/examples/event_multiplexer.rs | 3 +-- examples/subscriptions/examples/poll_logs.rs | 3 +-- examples/transactions/examples/send_eip7702_transaction.rs | 3 +-- examples/wallets/examples/keystore_signer.rs | 3 +-- examples/wallets/examples/ledger_signer.rs | 3 +-- examples/wallets/examples/private_key_signer.rs | 3 +-- examples/wallets/examples/trezor_signer.rs | 3 +-- examples/wallets/examples/yubi_signer.rs | 3 +-- 11 files changed, 11 insertions(+), 22 deletions(-) diff --git a/examples/advanced/examples/any_network.rs b/examples/advanced/examples/any_network.rs index 10b83398..5370a858 100644 --- a/examples/advanced/examples/any_network.rs +++ b/examples/advanced/examples/any_network.rs @@ -45,11 +45,10 @@ async fn main() -> Result<()> { // [RISK WARNING! Writing a private key in the code file is insecure behavior.] // The following code is for testing only. Set up signer from private key, be aware of danger. let signer: PrivateKeySigner = "".parse().expect("should parse private key"); - let wallet = EthereumWallet::from(signer); // Create a provider with the Arbitrum Sepolia network and the wallet. let rpc_url = "https://sepolia-rollup.arbitrum.io/rpc".parse()?; - let provider = ProviderBuilder::new().network::().wallet(wallet).on_http(rpc_url); + let provider = ProviderBuilder::new().network::().wallet(signer).on_http(rpc_url); // Create a contract instance. let contract = Counter::new(COUNTER_CONTRACT_ADDRESS, &provider); diff --git a/examples/fillers/examples/wallet_filler.rs b/examples/fillers/examples/wallet_filler.rs index 9ff04b92..1c0195da 100644 --- a/examples/fillers/examples/wallet_filler.rs +++ b/examples/fillers/examples/wallet_filler.rs @@ -18,13 +18,12 @@ async fn main() -> Result<()> { // Set up signer from the first default Anvil account (Alice). let signer: PrivateKeySigner = anvil.keys()[0].clone().into(); - let wallet = EthereumWallet::from(signer); // Create a provider with the wallet. let rpc_url = anvil.endpoint_url(); let provider = ProviderBuilder::new() // Add the `WalletFiller` to the provider - .wallet(wallet) + .wallet(signer) .on_http(rpc_url); // Build an EIP-1559 type transaction to send 100 wei to Vitalik. diff --git a/examples/providers/examples/builder.rs b/examples/providers/examples/builder.rs index 9060fd1f..825955f3 100644 --- a/examples/providers/examples/builder.rs +++ b/examples/providers/examples/builder.rs @@ -18,7 +18,6 @@ async fn main() -> Result<()> { // Set up signer from the first default Anvil account (Alice). let signer: PrivateKeySigner = anvil.keys()[0].clone().into(); - let wallet = EthereumWallet::from(signer.clone()); // Create two users, Alice and Bob. let alice = signer.address(); @@ -26,7 +25,7 @@ async fn main() -> Result<()> { // Set up the HTTP provider with the `reqwest` crate. let rpc_url = anvil.endpoint_url(); - let provider = ProviderBuilder::new().wallet(wallet).on_http(rpc_url); + let provider = ProviderBuilder::new().wallet(signer).on_http(rpc_url); // Create a transaction. let tx = TransactionRequest::default().with_to(bob).with_value(U256::from(100)); diff --git a/examples/subscriptions/examples/event_multiplexer.rs b/examples/subscriptions/examples/event_multiplexer.rs index 238fa91d..07a7f0d1 100644 --- a/examples/subscriptions/examples/event_multiplexer.rs +++ b/examples/subscriptions/examples/event_multiplexer.rs @@ -50,11 +50,10 @@ async fn main() -> Result<()> { let anvil = Anvil::new().block_time(1).try_spawn()?; let pk: PrivateKeySigner = anvil.keys()[0].clone().into(); - let wallet = EthereumWallet::from(pk); // Create a provider. let ws = WsConnect::new(anvil.ws_endpoint()); - let provider = ProviderBuilder::new().wallet(wallet).on_ws(ws).await?; + let provider = ProviderBuilder::new().wallet(pk).on_ws(ws).await?; // Deploy the `EventExample` contract. let contract = EventMultiplexer::deploy(provider).await?; diff --git a/examples/subscriptions/examples/poll_logs.rs b/examples/subscriptions/examples/poll_logs.rs index 7d11a9a4..db5aace7 100644 --- a/examples/subscriptions/examples/poll_logs.rs +++ b/examples/subscriptions/examples/poll_logs.rs @@ -39,11 +39,10 @@ async fn main() -> Result<()> { // Ensure `anvil` is available in $PATH. let anvil = Anvil::new().block_time(1).try_spawn()?; let pk: PrivateKeySigner = anvil.keys()[0].clone().into(); - let wallet = EthereumWallet::new(pk); // Create a WebSocket provider. let ws = WsConnect::new(anvil.ws_endpoint()); - let provider = ProviderBuilder::new().wallet(wallet).on_ws(ws).await?; + let provider = ProviderBuilder::new().wallet(pk).on_ws(ws).await?; // Deploy the `Counter` contract. let contract = Counter::deploy(provider.clone()).await?; diff --git a/examples/transactions/examples/send_eip7702_transaction.rs b/examples/transactions/examples/send_eip7702_transaction.rs index 0e738b81..96ff422d 100644 --- a/examples/transactions/examples/send_eip7702_transaction.rs +++ b/examples/transactions/examples/send_eip7702_transaction.rs @@ -45,8 +45,7 @@ async fn main() -> Result<()> { // Create a provider with the wallet for only Bob (not Alice). let rpc_url = anvil.endpoint_url(); - let wallet = EthereumWallet::from(bob.clone()); - let provider = ProviderBuilder::new().wallet(wallet).on_http(rpc_url); + let provider = ProviderBuilder::new().wallet(bob).on_http(rpc_url); // Deploy the contract Alice will authorize. let contract = Log::deploy(&provider).await?; diff --git a/examples/wallets/examples/keystore_signer.rs b/examples/wallets/examples/keystore_signer.rs index 10af9b2b..1d5c7bc3 100644 --- a/examples/wallets/examples/keystore_signer.rs +++ b/examples/wallets/examples/keystore_signer.rs @@ -21,11 +21,10 @@ async fn main() -> Result<()> { let keystore_file_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR")?).join("examples/keystore/alice.json"); let signer = LocalSigner::decrypt_keystore(keystore_file_path, password)?; - let wallet = EthereumWallet::from(signer); // Create a provider with the wallet. let provider = - ProviderBuilder::new().wallet(wallet).on_anvil_with_config(|anvil| anvil.block_time(1)); + ProviderBuilder::new().wallet(signer).on_anvil_with_config(|anvil| anvil.block_time(1)); // Build a transaction to send 100 wei from Alice to Vitalik. // The `from` field is automatically filled to the first signer's address (Alice). diff --git a/examples/wallets/examples/ledger_signer.rs b/examples/wallets/examples/ledger_signer.rs index 8f121a3c..2168928a 100644 --- a/examples/wallets/examples/ledger_signer.rs +++ b/examples/wallets/examples/ledger_signer.rs @@ -13,11 +13,10 @@ use eyre::Result; async fn main() -> Result<()> { // Instantiate the application by acquiring a lock on the Ledger device. let signer = LedgerSigner::new(HDPath::LedgerLive(0), Some(1)).await?; - let wallet = EthereumWallet::from(signer); // Create a provider with the wallet. let rpc_url = "https://eth.merkle.io".parse()?; - let provider = ProviderBuilder::new().wallet(wallet).on_http(rpc_url); + let provider = ProviderBuilder::new().wallet(signer).on_http(rpc_url); // Build a transaction to send 100 wei from Alice to Vitalik. // The `from` field is automatically filled to the first signer's address (Alice). diff --git a/examples/wallets/examples/private_key_signer.rs b/examples/wallets/examples/private_key_signer.rs index c3625024..5efbdc6a 100644 --- a/examples/wallets/examples/private_key_signer.rs +++ b/examples/wallets/examples/private_key_signer.rs @@ -21,11 +21,10 @@ async fn main() -> Result<()> { // The following code is for testing only. Set up signer from private key, be aware of danger. // let signer: PrivateKeySigner = "".parse().expect("should parse private key"); let signer: PrivateKeySigner = anvil.keys()[0].clone().into(); - let wallet = EthereumWallet::from(signer); // Create a provider with the wallet. let rpc_url = anvil.endpoint_url(); - let provider = ProviderBuilder::new().wallet(wallet).on_http(rpc_url); + let provider = ProviderBuilder::new().wallet(signer).on_http(rpc_url); // Build a transaction to send 100 wei from Alice to Vitalik. // The `from` field is automatically filled to the first signer's address (Alice). diff --git a/examples/wallets/examples/trezor_signer.rs b/examples/wallets/examples/trezor_signer.rs index 198b6aa9..0bdcc528 100644 --- a/examples/wallets/examples/trezor_signer.rs +++ b/examples/wallets/examples/trezor_signer.rs @@ -13,11 +13,10 @@ use eyre::Result; async fn main() -> Result<()> { // Instantiate the application by acquiring a lock on the Trezor device. let signer = TrezorSigner::new(HDPath::TrezorLive(0), Some(1)).await?; - let wallet = EthereumWallet::from(signer); // Create a provider with the wallet. let rpc_url = "https://eth.merkle.io".parse()?; - let provider = ProviderBuilder::new().wallet(wallet).on_http(rpc_url); + let provider = ProviderBuilder::new().wallet(signer).on_http(rpc_url); // Build a transaction to send 100 wei from Alice to Vitalik. // The `from` field is automatically filled to the first signer's address (Alice). diff --git a/examples/wallets/examples/yubi_signer.rs b/examples/wallets/examples/yubi_signer.rs index a2847b66..7f7a437e 100644 --- a/examples/wallets/examples/yubi_signer.rs +++ b/examples/wallets/examples/yubi_signer.rs @@ -22,11 +22,10 @@ async fn main() -> Result<()> { // `from_key` method to upload a key you already have, or the `new` method // to generate a new keypair. let signer = YubiSigner::connect(connector, Credentials::default(), 0); - let wallet = EthereumWallet::from(signer); // Create a provider with the wallet. let rpc_url = "https://eth.merkle.io".parse()?; - let provider = ProviderBuilder::new().wallet(wallet).on_http(rpc_url); + let provider = ProviderBuilder::new().wallet(signer).on_http(rpc_url); // Build a transaction to send 100 wei from Alice to Vitalik. // The `from` field is automatically filled to the first signer's address (Alice). From 7ed5032e3c95b323030c5129ace4603f76601f9f Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Wed, 2 Apr 2025 16:35:06 +0530 Subject: [PATCH 2/5] nits --- examples/advanced/examples/any_network.rs | 2 +- examples/wallets/examples/keystore_signer.rs | 2 +- examples/wallets/examples/ledger_signer.rs | 2 +- examples/wallets/examples/private_key_signer.rs | 2 +- examples/wallets/examples/trezor_signer.rs | 2 +- examples/wallets/examples/yubi_signer.rs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/advanced/examples/any_network.rs b/examples/advanced/examples/any_network.rs index 5370a858..2ed9023e 100644 --- a/examples/advanced/examples/any_network.rs +++ b/examples/advanced/examples/any_network.rs @@ -5,7 +5,7 @@ //! of Arbitrum's transaction receipts. use alloy::{ - network::{AnyNetwork, EthereumWallet}, + network::AnyNetwork, primitives::{address, Address, U128, U256, U64}, providers::ProviderBuilder, signers::local::PrivateKeySigner, diff --git a/examples/wallets/examples/keystore_signer.rs b/examples/wallets/examples/keystore_signer.rs index 1d5c7bc3..fdbd2218 100644 --- a/examples/wallets/examples/keystore_signer.rs +++ b/examples/wallets/examples/keystore_signer.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use alloy::{ - network::{EthereumWallet, TransactionBuilder}, + network::TransactionBuilder, primitives::{address, U256}, providers::{Provider, ProviderBuilder}, rpc::types::TransactionRequest, diff --git a/examples/wallets/examples/ledger_signer.rs b/examples/wallets/examples/ledger_signer.rs index 2168928a..a03da059 100644 --- a/examples/wallets/examples/ledger_signer.rs +++ b/examples/wallets/examples/ledger_signer.rs @@ -1,7 +1,7 @@ //! Example of signing and sending a transaction using a Ledger device. use alloy::{ - network::{EthereumWallet, TransactionBuilder}, + network::TransactionBuilder, primitives::{address, U256}, providers::{Provider, ProviderBuilder}, rpc::types::TransactionRequest, diff --git a/examples/wallets/examples/private_key_signer.rs b/examples/wallets/examples/private_key_signer.rs index 5efbdc6a..b8ea78f9 100644 --- a/examples/wallets/examples/private_key_signer.rs +++ b/examples/wallets/examples/private_key_signer.rs @@ -1,7 +1,7 @@ //! Example of using a local wallet to sign and send a transaction. use alloy::{ - network::{EthereumWallet, TransactionBuilder}, + network::TransactionBuilder, node_bindings::Anvil, primitives::{address, U256}, providers::{Provider, ProviderBuilder}, diff --git a/examples/wallets/examples/trezor_signer.rs b/examples/wallets/examples/trezor_signer.rs index 0bdcc528..40e08a12 100644 --- a/examples/wallets/examples/trezor_signer.rs +++ b/examples/wallets/examples/trezor_signer.rs @@ -1,7 +1,7 @@ //! Example of signing and sending a transaction using a Trezor device. use alloy::{ - network::{EthereumWallet, TransactionBuilder}, + network::TransactionBuilder, primitives::{address, U256}, providers::{Provider, ProviderBuilder}, rpc::types::TransactionRequest, diff --git a/examples/wallets/examples/yubi_signer.rs b/examples/wallets/examples/yubi_signer.rs index 7f7a437e..687a5d90 100644 --- a/examples/wallets/examples/yubi_signer.rs +++ b/examples/wallets/examples/yubi_signer.rs @@ -1,7 +1,7 @@ //! Example of signing and sending a transaction using a Yubi device. use alloy::{ - network::{EthereumWallet, TransactionBuilder}, + network::TransactionBuilder, primitives::{address, U256}, providers::{Provider, ProviderBuilder}, rpc::types::TransactionRequest, From 29fb1a4b66c61a7453f7e247985b66f7292ae07b Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Wed, 2 Apr 2025 17:10:03 +0530 Subject: [PATCH 3/5] feat: EthereumWallet example --- README.md | 1 + examples/wallets/examples/ethereum_wallet.rs | 74 ++++++++++++++++++++ scripts/run.sh | 1 + 3 files changed, 76 insertions(+) create mode 100644 examples/wallets/examples/ethereum_wallet.rs diff --git a/README.md b/README.md index ebdb3a17..78e6e1f5 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,7 @@ This repository contains the following examples: - [x] [Ledger signer](./examples/wallets/examples/ledger_signer.rs) - [x] [Private key signer](./examples/wallets/examples/private_key_signer.rs) - [x] [Mnemonic signer](./examples/wallets/examples/mnemonic_signer.rs) + - [x] [Ethereum Wallet](./examples/wallets/examples/ethereum_wallet.rs) - [x] [Sign message](./examples/wallets/examples/sign_message.rs) - [x] [Verify message](./examples/wallets/examples/verify_message.rs) - [x] [Sign permit hash](./examples/wallets/examples/sign_permit_hash.rs) diff --git a/examples/wallets/examples/ethereum_wallet.rs b/examples/wallets/examples/ethereum_wallet.rs new file mode 100644 index 00000000..98bd91a3 --- /dev/null +++ b/examples/wallets/examples/ethereum_wallet.rs @@ -0,0 +1,74 @@ +//! Demonstrates how EthereumWallet can use multiple different types of signers. + +use alloy::{ + network::{EthereumWallet, TransactionBuilder, TxSigner}, + node_bindings::Anvil, + primitives::{address, U256}, + providers::{Provider, ProviderBuilder}, + rpc::types::TransactionRequest, + signers::{ + aws::AwsSigner, + ledger::{HDPath, LedgerSigner}, + local::PrivateKeySigner, + }, +}; +use aws_config::BehaviorVersion; + +#[tokio::main] +async fn main() -> eyre::Result<()> { + // Spin up a local Anvil node. + // Ensure `anvil` is available in $PATH. + let anvil = Anvil::new().try_spawn()?; + + // Initialize your signers. + + // Set up a local signer. + let pk_signer: PrivateKeySigner = + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".parse()?; + let pk_addr = pk_signer.address(); + + // Set up a hardware wallet signer + let ledger = LedgerSigner::new(HDPath::LedgerLive(0), Some(1)).await?; + let ledger_addr = ledger.address(); + + // AWS KMS signer + let key_id = std::env::var("AWS_KEY_ID").expect("AWS_KEY_ID not set in .env file"); + let config = aws_config::load_defaults(BehaviorVersion::latest()).await; + let client = aws_sdk_kms::Client::new(&config); + let aws = AwsSigner::new(client, key_id, Some(1)).await?; + let _aws_addr = aws.address(); + + // Initialize `EthereumWallet`. + // `pk_signer` is set as the default signer. + // This signer is used to sign `TransactionRequest` and `TypedTransaction` objects that do + // not specify a signer address in the `from` field. + let mut wallet = EthereumWallet::new(pk_signer); + // Add aws and ledger signers to the wallet + wallet.register_signer(aws); + wallet.register_signer(ledger); + + // Create a provider with the `WalletFiller`. + let provider = ProviderBuilder::new().wallet(wallet).on_http(anvil.endpoint_url()); + + // Note that the `from` field hasn't been specified. + // The wallet filler in the provider will set to it the default signer's address, which is + // `pk_signer`. + let tx = TransactionRequest::default() + .with_to(address!("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")) + .with_value(U256::from(100)); + + let receipt = provider.send_transaction(tx).await?.get_receipt().await?; + assert_eq!(pk_addr, receipt.from); + + // One can hint the wallet filler about which signer to use by specifying the `from` field. + // In this case, the `ledger` signer will be used to sign the transaction. + let tx = TransactionRequest::default() + .with_from(ledger_addr) + .with_to(address!("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")) + .with_value(U256::from(100)); + + let receipt = provider.send_transaction(tx).await?.get_receipt().await?; + assert_eq!(ledger_addr, receipt.from); + + Ok(()) +} diff --git a/scripts/run.sh b/scripts/run.sh index 74b3deaa..ffd8a678 100755 --- a/scripts/run.sh +++ b/scripts/run.sh @@ -37,6 +37,7 @@ function main () { -e 'foundry_fork_db' \ -e 'reth_db_layer' \ -e 'reth_db_provider' \ + -e 'ethereum_wallet' \ | xargs -n1 echo )" From 5d251f3383e479cebf37eedbaa89711fecad2dd9 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Wed, 2 Apr 2025 17:16:55 +0530 Subject: [PATCH 4/5] nit --- examples/wallets/examples/ethereum_wallet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/wallets/examples/ethereum_wallet.rs b/examples/wallets/examples/ethereum_wallet.rs index 98bd91a3..02a168d3 100644 --- a/examples/wallets/examples/ethereum_wallet.rs +++ b/examples/wallets/examples/ethereum_wallet.rs @@ -1,4 +1,4 @@ -//! Demonstrates how EthereumWallet can use multiple different types of signers. +//! Demonstrates how `EthereumWallet` can use multiple different types of signers. use alloy::{ network::{EthereumWallet, TransactionBuilder, TxSigner}, From a2155867e473799a953a8e03f90a3548892ebf53 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Wed, 2 Apr 2025 17:37:19 +0530 Subject: [PATCH 5/5] clippy --- examples/fillers/examples/wallet_filler.rs | 2 +- examples/providers/examples/builder.rs | 2 +- examples/subscriptions/examples/event_multiplexer.rs | 1 - examples/subscriptions/examples/poll_logs.rs | 1 - examples/transactions/examples/send_eip7702_transaction.rs | 4 ++-- 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/fillers/examples/wallet_filler.rs b/examples/fillers/examples/wallet_filler.rs index 1c0195da..a200eec1 100644 --- a/examples/fillers/examples/wallet_filler.rs +++ b/examples/fillers/examples/wallet_filler.rs @@ -1,7 +1,7 @@ //! Example of using the `WalletFiller` in the provider. use alloy::{ - network::{EthereumWallet, TransactionBuilder}, + network::TransactionBuilder, node_bindings::Anvil, primitives::{address, b256, U256}, providers::{Provider, ProviderBuilder}, diff --git a/examples/providers/examples/builder.rs b/examples/providers/examples/builder.rs index 825955f3..4f0340bb 100644 --- a/examples/providers/examples/builder.rs +++ b/examples/providers/examples/builder.rs @@ -1,7 +1,7 @@ //! Example of using the `ProviderBuilder` to create a provider with a signer and network. use alloy::{ - network::{EthereumWallet, TransactionBuilder}, + network::TransactionBuilder, node_bindings::Anvil, primitives::U256, providers::{Provider, ProviderBuilder}, diff --git a/examples/subscriptions/examples/event_multiplexer.rs b/examples/subscriptions/examples/event_multiplexer.rs index 07a7f0d1..9b57ec79 100644 --- a/examples/subscriptions/examples/event_multiplexer.rs +++ b/examples/subscriptions/examples/event_multiplexer.rs @@ -3,7 +3,6 @@ use std::str::FromStr; use alloy::{ - network::EthereumWallet, node_bindings::Anvil, primitives::I256, providers::{ProviderBuilder, WsConnect}, diff --git a/examples/subscriptions/examples/poll_logs.rs b/examples/subscriptions/examples/poll_logs.rs index db5aace7..a0bbf9da 100644 --- a/examples/subscriptions/examples/poll_logs.rs +++ b/examples/subscriptions/examples/poll_logs.rs @@ -1,7 +1,6 @@ //! Example of watching and polling for contract events by `WebSocket` subscription. use alloy::{ - network::EthereumWallet, node_bindings::Anvil, providers::{ProviderBuilder, WsConnect}, signers::local::PrivateKeySigner, diff --git a/examples/transactions/examples/send_eip7702_transaction.rs b/examples/transactions/examples/send_eip7702_transaction.rs index 96ff422d..309a580a 100644 --- a/examples/transactions/examples/send_eip7702_transaction.rs +++ b/examples/transactions/examples/send_eip7702_transaction.rs @@ -2,7 +2,7 @@ use alloy::{ eips::eip7702::Authorization, - network::{EthereumWallet, TransactionBuilder, TransactionBuilder7702}, + network::{TransactionBuilder, TransactionBuilder7702}, node_bindings::Anvil, primitives::U256, providers::{Provider, ProviderBuilder}, @@ -45,7 +45,7 @@ async fn main() -> Result<()> { // Create a provider with the wallet for only Bob (not Alice). let rpc_url = anvil.endpoint_url(); - let provider = ProviderBuilder::new().wallet(bob).on_http(rpc_url); + let provider = ProviderBuilder::new().wallet(bob.clone()).on_http(rpc_url); // Deploy the contract Alice will authorize. let contract = Log::deploy(&provider).await?;