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/advanced/examples/any_network.rs b/examples/advanced/examples/any_network.rs index 10b83398..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, @@ -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..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}, @@ -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..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}, @@ -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..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}, @@ -50,11 +49,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..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, @@ -39,11 +38,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..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,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.clone()).on_http(rpc_url); // Deploy the contract Alice will authorize. let contract = Log::deploy(&provider).await?; diff --git a/examples/wallets/examples/ethereum_wallet.rs b/examples/wallets/examples/ethereum_wallet.rs new file mode 100644 index 00000000..02a168d3 --- /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/examples/wallets/examples/keystore_signer.rs b/examples/wallets/examples/keystore_signer.rs index 10af9b2b..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, @@ -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..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, @@ -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..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}, @@ -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..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, @@ -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..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, @@ -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). 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 )"