diff --git a/Cargo.lock b/Cargo.lock index af9328a22583..dfc5ffee96eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1257,6 +1257,7 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" name = "cast" version = "0.2.0" dependencies = [ + "alloy-consensus", "alloy-contract", "alloy-dyn-abi", "alloy-json-abi", diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index e97ae59c5241..1f5cbadba89c 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -33,6 +33,8 @@ alloy-rlp.workspace = true alloy-providers.workspace = true alloy-rpc-types.workspace = true alloy-signer.workspace = true +alloy-contract.workspace = true +alloy-consensus.workspace = true ethers-core.workspace = true ethers-providers.workspace = true diff --git a/crates/cast/bin/cmd/access_list.rs b/crates/cast/bin/cmd/access_list.rs index 3bb215c7d721..1c49ac67009b 100644 --- a/crates/cast/bin/cmd/access_list.rs +++ b/crates/cast/bin/cmd/access_list.rs @@ -1,14 +1,15 @@ +use alloy_primitives::Address; use alloy_providers::provider::TempProvider; +use alloy_rpc_types::BlockId; use cast::{Cast, TxBuilder}; use clap::Parser; -use ethers_core::types::{BlockId, NameOrAddress}; use ethers_providers::Middleware; use eyre::{Result, WrapErr}; use foundry_cli::{ opts::{EthereumOpts, TransactionOpts}, utils, }; -use foundry_common::types::ToEthers; +use foundry_common::ens::NameOrAddress; use foundry_config::{Chain, Config}; use std::str::FromStr; @@ -65,10 +66,18 @@ impl AccessListArgs { let chain = utils::get_chain(config.chain, &provider).await?; let sender = eth.wallet.sender().await; + let to = match to { + Some(NameOrAddress::Name(name)) => { + Some(NameOrAddress::Name(name).resolve(&alloy_provider).await?) + } + Some(NameOrAddress::Address(addr)) => Some(addr), + None => None, + }; + access_list( &provider, alloy_provider, - sender.to_ethers(), + sender, to, sig, args, @@ -84,16 +93,11 @@ impl AccessListArgs { } #[allow(clippy::too_many_arguments)] -async fn access_list< - M: Middleware, - P: TempProvider, - F: Into, - T: Into, ->( +async fn access_list( provider: M, alloy_provider: P, - from: F, - to: Option, + from: Address, + to: Option
, sig: Option, args: Vec, data: Option, @@ -105,7 +109,7 @@ async fn access_list< where M::Error: 'static, { - let mut builder = TxBuilder::new(&provider, from, to, chain, tx.legacy).await?; + let mut builder = TxBuilder::new(&alloy_provider, from, to, chain, tx.legacy).await?; builder .gas(tx.gas_limit) .gas_price(tx.gas_price) @@ -124,7 +128,7 @@ where let builder_output = builder.peek(); - let cast = Cast::new(&provider, alloy_provider); + let cast = Cast::new(&provider, &alloy_provider); let access_list: String = cast.access_list(builder_output, block, to_json).await?; diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index 1c6c5b1de566..894e9c1aa99b 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -1,23 +1,19 @@ use alloy_primitives::U256; +use alloy_providers::provider::TempProvider; +use alloy_rpc_types::BlockId; use cast::{Cast, TxBuilder}; use clap::Parser; -use ethers_core::types::{BlockId, NameOrAddress}; use eyre::{Result, WrapErr}; use foundry_cli::{ opts::{EthereumOpts, TransactionOpts}, utils::{self, handle_traces, parse_ether_value, TraceResult}, }; -use foundry_common::{ - runtime_client::RuntimeClient, - types::{ToAlloy, ToEthers}, -}; +use foundry_common::{ens::NameOrAddress, types::ToAlloy}; use foundry_compilers::EvmVersion; use foundry_config::{find_project_root_path, Config}; use foundry_evm::{executors::TracingExecutor, opts::EvmOpts}; use std::str::FromStr; -type Provider = ethers_providers::Provider; - /// CLI arguments for `cast call`. #[derive(Debug, Parser)] pub struct CallArgs { @@ -120,8 +116,15 @@ impl CallArgs { let chain = utils::get_chain(config.chain, &provider).await?; let sender = eth.wallet.sender().await; - let mut builder: TxBuilder<'_, Provider> = - TxBuilder::new(&provider, sender.to_ethers(), to, chain, tx.legacy).await?; + let to = match to { + Some(NameOrAddress::Name(name)) => { + Some(NameOrAddress::Name(name).resolve(&alloy_provider).await?) + } + Some(NameOrAddress::Address(addr)) => Some(addr), + None => None, + }; + + let mut builder = TxBuilder::new(&alloy_provider, sender, to, chain, tx.legacy).await?; builder .gas(tx.gas_limit) @@ -196,7 +199,7 @@ impl CallArgs { } }; - let builder_output = builder.build(); + let builder_output = builder.build_alloy(); println!("{}", Cast::new(provider, alloy_provider).call(builder_output, block).await?); Ok(()) @@ -204,8 +207,8 @@ impl CallArgs { } /// fills the builder from create arg -async fn fill_create( - builder: &mut TxBuilder<'_, Provider>, +async fn fill_create( + builder: &mut TxBuilder<'_, P>, value: Option, code: String, sig: Option, @@ -226,8 +229,8 @@ async fn fill_create( } /// fills the builder from args -async fn fill_tx( - builder: &mut TxBuilder<'_, Provider>, +async fn fill_tx( + builder: &mut TxBuilder<'_, P>, value: Option, sig: Option, args: Vec, diff --git a/crates/cast/bin/cmd/estimate.rs b/crates/cast/bin/cmd/estimate.rs index c963cfcacdae..3ae6ad9cc1a7 100644 --- a/crates/cast/bin/cmd/estimate.rs +++ b/crates/cast/bin/cmd/estimate.rs @@ -1,12 +1,12 @@ use alloy_primitives::U256; use cast::{Cast, TxBuilder}; use clap::Parser; -use ethers_core::types::NameOrAddress; use eyre::Result; use foundry_cli::{ opts::{EtherscanOpts, RpcOpts}, utils::{self, parse_ether_value}, }; +use foundry_common::ens::NameOrAddress; use foundry_config::{figment::Figment, Config}; use std::str::FromStr; @@ -87,7 +87,16 @@ impl EstimateArgs { let chain = utils::get_chain(config.chain, &provider).await?; let api_key = config.get_etherscan_api_key(Some(chain)); - let mut builder = TxBuilder::new(&provider, from, to, chain, false).await?; + let from = from.resolve(&alloy_provider).await?; + let to = match to { + Some(NameOrAddress::Name(name)) => { + Some(NameOrAddress::Name(name).resolve(&alloy_provider).await?) + } + Some(NameOrAddress::Address(addr)) => Some(addr), + None => None, + }; + + let mut builder = TxBuilder::new(&alloy_provider, from, to, chain, false).await?; builder.etherscan_api_key(api_key); match command { @@ -110,7 +119,7 @@ impl EstimateArgs { }; let builder_output = builder.peek(); - let gas = Cast::new(&provider, alloy_provider).estimate(builder_output).await?; + let gas = Cast::new(&provider, &alloy_provider).estimate(builder_output).await?; println!("{gas}"); Ok(()) } diff --git a/crates/cast/bin/cmd/logs.rs b/crates/cast/bin/cmd/logs.rs index fc195b63fcf7..5021b7a3ffa6 100644 --- a/crates/cast/bin/cmd/logs.rs +++ b/crates/cast/bin/cmd/logs.rs @@ -1,4 +1,4 @@ -use alloy_rpc_types::BlockHashOrNumber; +use alloy_rpc_types::BlockId; use cast::Cast; use clap::Parser; use ethers_core::{ @@ -23,13 +23,13 @@ pub struct LogsArgs { /// /// Can also be the tags earliest, finalized, safe, latest, or pending. #[clap(long)] - from_block: Option, + from_block: Option, /// The block height to stop query at. /// /// Can also be the tags earliest, finalized, safe, latest, or pending. #[clap(long)] - to_block: Option, + to_block: Option, /// The contract address to filter on. #[clap( diff --git a/crates/cast/bin/cmd/send.rs b/crates/cast/bin/cmd/send.rs index e208b7ed39e4..0b8f68750dde 100644 --- a/crates/cast/bin/cmd/send.rs +++ b/crates/cast/bin/cmd/send.rs @@ -1,7 +1,7 @@ +use alloy_primitives::Address; use alloy_providers::provider::TempProvider; use cast::{Cast, TxBuilder}; use clap::Parser; -use ethers_core::types::NameOrAddress; use ethers_middleware::MiddlewareBuilder; use ethers_providers::Middleware; use ethers_signers::Signer; @@ -12,6 +12,7 @@ use foundry_cli::{ }; use foundry_common::{ cli_warn, + ens::NameOrAddress, types::{ToAlloy, ToEthers}, }; use foundry_config::{Chain, Config}; @@ -119,6 +120,14 @@ impl SendTxArgs { let chain = utils::get_chain(config.chain, &provider).await?; let api_key = config.get_etherscan_api_key(Some(chain)); + let to = match to { + Some(NameOrAddress::Name(name)) => { + Some(NameOrAddress::Name(name).resolve(&alloy_provider).await?) + } + Some(NameOrAddress::Address(addr)) => Some(addr), + None => None, + }; + // Case 1: // Default to sending via eth_sendTransaction if the --unlocked flag is passed. // This should be the only way this RPC method is used as it requires a local node @@ -155,7 +164,7 @@ impl SendTxArgs { cast_send( provider, alloy_provider, - config.sender.to_ethers(), + config.sender, to, code, (sig, args), @@ -199,7 +208,7 @@ corresponds to the sender, or let foundry automatically detect it by not specify cast_send( provider, alloy_provider, - from, + from.to_alloy(), to, code, (sig, args), @@ -216,11 +225,11 @@ corresponds to the sender, or let foundry automatically detect it by not specify } #[allow(clippy::too_many_arguments)] -async fn cast_send, T: Into>( +async fn cast_send( provider: M, alloy_provider: P, - from: F, - to: Option, + from: Address, + to: Option
, code: Option, args: (String, Vec), tx: TransactionOpts, @@ -235,7 +244,7 @@ where { let (sig, params) = args; let params = if !sig.is_empty() { Some((&sig[..], params)) } else { None }; - let mut builder = TxBuilder::new(&provider, from, to, chain, tx.legacy).await?; + let mut builder = TxBuilder::new(&alloy_provider, from, to, chain, tx.legacy).await?; builder .etherscan_api_key(etherscan_api_key) .gas(tx.gas_limit) diff --git a/crates/cast/bin/cmd/storage.rs b/crates/cast/bin/cmd/storage.rs index e1a033a0f463..a7047f52dc6e 100644 --- a/crates/cast/bin/cmd/storage.rs +++ b/crates/cast/bin/cmd/storage.rs @@ -1,10 +1,10 @@ use crate::opts::parse_slot; -use alloy_primitives::{B256, U256}; +use alloy_primitives::{Address, B256, U256}; +use alloy_providers::provider::TempProvider; +use alloy_rpc_types::BlockId; use cast::Cast; use clap::Parser; use comfy_table::{presets::ASCII_MARKDOWN, Table}; -use ethers_core::types::{BlockId, NameOrAddress}; -use ethers_providers::Middleware; use eyre::Result; use foundry_block_explorers::Client; use foundry_cli::{ @@ -14,8 +14,7 @@ use foundry_cli::{ use foundry_common::{ abi::find_source, compile::{etherscan_project, ProjectCompiler}, - provider::ethers::RetryProvider, - types::{ToAlloy, ToEthers}, + ens::NameOrAddress, }; use foundry_compilers::{ artifacts::StorageLayout, Artifact, ConfigurableContractArtifact, Project, Solc, @@ -80,20 +79,20 @@ impl StorageArgs { let config = Config::from(&self); let Self { address, slot, block, build, .. } = self; - - let provider = utils::get_provider(&config)?; - let alloy_provider = utils::get_alloy_provider(&config)?; + let provider = utils::get_alloy_provider(&config)?; + let ethers_provider = utils::get_provider(&config)?; + let address = address.resolve(&provider).await?; // Slot was provided, perform a simple RPC call if let Some(slot) = slot { - let cast = Cast::new(provider, alloy_provider); - println!("{}", cast.storage(address, slot.to_ethers(), block).await?); + let cast = Cast::new(ethers_provider, provider); + println!("{}", cast.storage(address, slot, block).await?); return Ok(()); } // No slot was provided // Get deployed bytecode at given address - let address_code = provider.get_code(address.clone(), block).await?.to_alloy(); + let address_code = provider.get_code_at(address, block).await?; if address_code.is_empty() { eyre::bail!("Provided address has no deployed code and thus no storage"); } @@ -108,8 +107,7 @@ impl StorageArgs { artifact.get_deployed_bytecode_bytes().is_some_and(|b| *b == address_code) }); if let Some((_, artifact)) = artifact { - return fetch_and_print_storage(provider, address.clone(), block, artifact, true) - .await; + return fetch_and_print_storage(provider, address, block, artifact, true).await; } } @@ -121,14 +119,10 @@ impl StorageArgs { eyre::bail!("You must provide an Etherscan API key if you're fetching a remote contract's storage."); } - let chain = utils::get_chain(config.chain, &provider).await?; + let chain = utils::get_chain(config.chain, ðers_provider).await?; let api_key = config.get_etherscan_api_key(Some(chain)).unwrap_or_default(); let client = Client::new(chain, api_key)?; - let addr = address - .as_address() - .ok_or_else(|| eyre::eyre!("Could not resolve address"))? - .to_alloy(); - let source = find_source(client, addr).await?; + let source = find_source(client, address).await?; let metadata = source.items.first().unwrap(); if metadata.is_vyper() { eyre::bail!("Contract at provided address is not a valid Solidity contract") @@ -210,9 +204,9 @@ impl StorageValue { } } -async fn fetch_and_print_storage( - provider: RetryProvider, - address: NameOrAddress, +async fn fetch_and_print_storage( + provider: P, + address: Address, block: Option, artifact: &ConfigurableContractArtifact, pretty: bool, @@ -227,18 +221,17 @@ async fn fetch_and_print_storage( } } -async fn fetch_storage_slots( - provider: RetryProvider, - address: NameOrAddress, +async fn fetch_storage_slots( + provider: P, + address: Address, block: Option, layout: &StorageLayout, ) -> Result> { let requests = layout.storage.iter().map(|storage_slot| async { let slot = B256::from(U256::from_str(&storage_slot.slot)?); - let raw_slot_value = - provider.get_storage_at(address.clone(), slot.to_ethers(), block).await?.to_alloy(); + let raw_slot_value = provider.get_storage_at(address, slot.into(), block).await?; - let value = StorageValue { slot, raw_slot_value }; + let value = StorageValue { slot, raw_slot_value: raw_slot_value.into() }; Ok(value) }); @@ -267,7 +260,7 @@ fn print_storage(layout: StorageLayout, values: Vec, pretty: bool) storage_type.map_or("?", |t| &t.label), &slot.slot, &slot.offset.to_string(), - &storage_type.map_or("?", |t| &t.number_of_bytes), + (storage_type.map_or("?", |t| &t.number_of_bytes)), &converted_value.to_string(), &value.to_string(), &slot.contract, diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 96d3a30ad440..ceadb96d1a20 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -5,15 +5,14 @@ use alloy_primitives::{keccak256, Address, B256}; use cast::{Cast, SimpleCast, TxBuilder}; use clap::{CommandFactory, Parser}; use clap_complete::generate; -use ethers_core::types::{BlockId, BlockNumber::Latest, NameOrAddress}; -use ethers_providers::{Middleware, Provider}; +use ethers_core::types::{BlockId, BlockNumber::Latest}; +use ethers_providers::Middleware; use eyre::Result; use foundry_cli::{handler, prompt, stdin, utils}; use foundry_common::{ abi::get_event, fmt::format_tokens, fs, - runtime_client::RuntimeClient, selectors::{ decode_calldata, decode_event_topic, decode_function_selector, decode_selectors, import_selectors, parse_signatures, pretty_calldata, ParsedSignatures, SelectorImportData, @@ -209,23 +208,14 @@ async fn main() -> Result<()> { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; let alloy_provider = utils::get_alloy_provider(&config)?; + let account_addr = who.resolve(&alloy_provider).await?; match erc20 { Some(token) => { let chain = utils::get_chain(config.chain, &provider).await?; - let mut builder: TxBuilder<'_, Provider> = TxBuilder::new( - &provider, - NameOrAddress::Address(Address::ZERO.to_ethers()), - Some(NameOrAddress::Address(token.to_ethers())), - chain, - true, - ) - .await?; - - let account_addr = match who { - NameOrAddress::Name(ens_name) => provider.resolve_name(&ens_name).await?, - NameOrAddress::Address(addr) => addr, - }; + let mut builder = + TxBuilder::new(&alloy_provider, Address::ZERO, Some(token), chain, true) + .await?; builder .set_args( @@ -233,14 +223,15 @@ async fn main() -> Result<()> { vec![format!("{account_addr:#x}")], ) .await?; - let builder_output = builder.build(); + let builder_output = builder.build_alloy(); println!( "{}", - Cast::new(provider, alloy_provider).call(builder_output, block).await? + Cast::new(&provider, &alloy_provider).call(builder_output, block).await? ); } None => { - let value = Cast::new(provider, alloy_provider).balance(who, block).await?; + let value = + Cast::new(&provider, &alloy_provider).balance(account_addr, block).await?; if ether { println!("{}", SimpleCast::from_wei(&value.to_string(), "eth")?); } else { @@ -298,6 +289,7 @@ async fn main() -> Result<()> { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; let alloy_provider = utils::get_alloy_provider(&config)?; + let who = who.resolve(&alloy_provider).await?; println!( "{}", Cast::new(provider, alloy_provider).code(who, block, disassemble).await? @@ -307,6 +299,7 @@ async fn main() -> Result<()> { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; let alloy_provider = utils::get_alloy_provider(&config)?; + let who = who.resolve(&alloy_provider).await?; println!("{}", Cast::new(provider, alloy_provider).codesize(who, block).await?); } CastSubcommand::ComputeAddress { address, nonce, rpc } => { @@ -359,18 +352,21 @@ async fn main() -> Result<()> { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; let alloy_provider = utils::get_alloy_provider(&config)?; + let who = who.resolve(&alloy_provider).await?; println!("{}", Cast::new(provider, alloy_provider).implementation(who, block).await?); } CastSubcommand::Admin { block, who, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; let alloy_provider = utils::get_alloy_provider(&config)?; + let who = who.resolve(&alloy_provider).await?; println!("{}", Cast::new(provider, alloy_provider).admin(who, block).await?); } CastSubcommand::Nonce { block, who, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; let alloy_provider = utils::get_alloy_provider(&config)?; + let who = who.resolve(&alloy_provider).await?; println!("{}", Cast::new(provider, alloy_provider).nonce(who, block).await?); } CastSubcommand::Proof { address, slots, rpc, block } => { @@ -578,7 +574,7 @@ async fn main() -> Result<()> { let tx = stdin::unwrap_line(tx)?; let (tx, sig) = SimpleCast::decode_raw_transaction(&tx)?; - // Serialize tx, sig and constructed a merged json string + // Serialize tx, sig and construct a merged json string let mut tx = serde_json::to_value(&tx)?; let tx_map = tx.as_object_mut().unwrap(); serde_json::to_value(sig)?.as_object().unwrap().iter().for_each(|(k, v)| { diff --git a/crates/cast/bin/opts.rs b/crates/cast/bin/opts.rs index 2f09c2534830..ac63684b588a 100644 --- a/crates/cast/bin/opts.rs +++ b/crates/cast/bin/opts.rs @@ -4,10 +4,12 @@ use crate::cmd::{ rpc::RpcArgs, run::RunArgs, send::SendTxArgs, storage::StorageArgs, wallet::WalletSubcommands, }; use alloy_primitives::{Address, B256, U256}; +use alloy_rpc_types::BlockId as AlloyBlockId; use clap::{Parser, Subcommand, ValueHint}; use ethers_core::types::{BlockId, NameOrAddress}; use eyre::Result; use foundry_cli::opts::{EtherscanOpts, RpcOpts}; +use foundry_common::ens::NameOrAddress as EnsNameOrAddress; use std::{path::PathBuf, str::FromStr}; const VERSION_MESSAGE: &str = concat!( @@ -516,11 +518,11 @@ pub enum CastSubcommand { /// /// Can also be the tags earliest, finalized, safe, latest, or pending. #[clap(long, short = 'B')] - block: Option, + block: Option, /// The address to get the nonce for. - #[clap(value_parser = NameOrAddress::from_str)] - who: NameOrAddress, + #[clap(value_parser = EnsNameOrAddress::from_str)] + who: EnsNameOrAddress, #[clap(flatten)] rpc: RpcOpts, @@ -533,11 +535,11 @@ pub enum CastSubcommand { /// /// Can also be the tags earliest, finalized, safe, latest, or pending. #[clap(long, short = 'B')] - block: Option, + block: Option, /// The address to get the nonce for. - #[clap(value_parser = NameOrAddress::from_str)] - who: NameOrAddress, + #[clap(value_parser = EnsNameOrAddress::from_str)] + who: EnsNameOrAddress, #[clap(flatten)] rpc: RpcOpts, @@ -613,11 +615,11 @@ pub enum CastSubcommand { /// /// Can also be the tags earliest, finalized, safe, latest, or pending. #[clap(long, short = 'B')] - block: Option, + block: Option, /// The account to query. - #[clap(value_parser = NameOrAddress::from_str)] - who: NameOrAddress, + #[clap(value_parser = EnsNameOrAddress::from_str)] + who: EnsNameOrAddress, /// Format the balance in ether. #[clap(long, short)] @@ -651,11 +653,11 @@ pub enum CastSubcommand { /// /// Can also be the tags earliest, finalized, safe, latest, or pending. #[clap(long, short = 'B')] - block: Option, + block: Option, /// The contract address. - #[clap(value_parser = NameOrAddress::from_str)] - who: NameOrAddress, + #[clap(value_parser = EnsNameOrAddress::from_str)] + who: EnsNameOrAddress, /// Disassemble bytecodes into individual opcodes. #[clap(long, short)] @@ -672,11 +674,11 @@ pub enum CastSubcommand { /// /// Can also be the tags earliest, finalized, safe, latest, or pending. #[clap(long, short = 'B')] - block: Option, + block: Option, /// The contract address. - #[clap(value_parser = NameOrAddress::from_str)] - who: NameOrAddress, + #[clap(value_parser = EnsNameOrAddress::from_str)] + who: EnsNameOrAddress, #[clap(flatten)] rpc: RpcOpts, @@ -763,11 +765,11 @@ pub enum CastSubcommand { /// /// Can also be the tags earliest, finalized, safe, latest, or pending. #[clap(long, short = 'B')] - block: Option, + block: Option, /// The address to get the nonce for. - #[clap(value_parser = NameOrAddress::from_str)] - who: NameOrAddress, + #[clap(value_parser = EnsNameOrAddress::from_str)] + who: EnsNameOrAddress, #[clap(flatten)] rpc: RpcOpts, diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 8467fd8de637..c4e5f26ac57a 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -6,14 +6,11 @@ use alloy_primitives::{ }; use alloy_providers::provider::TempProvider; use alloy_rlp::Decodable; -use alloy_rpc_types::{BlockHashOrNumber, BlockNumberOrTag}; +use alloy_rpc_types::{BlockId as AlloyBlockId, BlockNumberOrTag}; use base::{Base, NumberWithBase, ToBase}; use chrono::NaiveDateTime; use ethers_core::{ - types::{ - transaction::eip2718::TypedTransaction, BlockId, Filter, NameOrAddress, Signature, H160, - H256, - }, + types::{transaction::eip2718::TypedTransaction, BlockId, Filter, Signature, H256}, utils::rlp, }; use ethers_providers::{Middleware, PendingTransaction, PubsubClient}; @@ -23,7 +20,6 @@ use foundry_block_explorers::Client; use foundry_common::{ abi::{encode_function_args, get_func}, fmt::*, - types::{ToAlloy, ToEthers}, TransactionReceiptWithRevertReason, }; use foundry_config::Chain; @@ -36,7 +32,7 @@ use std::{ sync::atomic::{AtomicBool, Ordering}, }; use tokio::signal::ctrl_c; -use tx::{TxBuilderOutput, TxBuilderPeekOutput}; +use tx::{TxBuilderAlloyOutput, TxBuilderOutput, TxBuilderPeekOutput}; pub use foundry_evm::*; pub use rusoto_core::{ @@ -71,10 +67,12 @@ where /// ``` /// use cast::Cast; /// use ethers_providers::{Http, Provider}; + /// use foundry_common::provider::alloy::get_http_provider; /// /// # async fn foo() -> eyre::Result<()> { /// let provider = Provider::::try_from("http://localhost:8545")?; - /// let cast = Cast::new(provider); + /// let alloy_provider = get_http_provider("http://localhost:8545"); + /// let cast = Cast::new(provider, alloy_provider); /// # Ok(()) /// # } /// ``` @@ -90,10 +88,12 @@ where /// use cast::{Cast, TxBuilder}; /// use ethers_core::types::Address; /// use ethers_providers::{Http, Provider}; + /// use foundry_common::provider::alloy::get_http_provider; /// use std::str::FromStr; /// /// # async fn foo() -> eyre::Result<()> { /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let alloy_provider = get_http_provider("http://localhost:8545"); /// let to = Address::from_str("0xB3C95ff08316fb2F2e3E52Ee82F8e7b605Aa1304")?; /// let sig = "function greeting(uint256 i) public returns (string)"; /// let args = vec!["5".to_owned()]; @@ -101,7 +101,7 @@ where /// TxBuilder::new(&provider, Address::zero(), Some(to), Chain::Mainnet, false).await?; /// builder.set_args(sig, args).await?; /// let builder_output = builder.build(); - /// let cast = Cast::new(provider); + /// let cast = Cast::new(provider, alloy_provider); /// let data = cast.call(builder_output, None).await?; /// println!("{}", data); /// # Ok(()) @@ -109,11 +109,11 @@ where /// ``` pub async fn call<'a>( &self, - builder_output: TxBuilderOutput, - block: Option, + builder_output: TxBuilderAlloyOutput, + block: Option, ) -> Result { let (tx, func) = builder_output; - let res = self.provider.call(&tx, block).await?; + let res = self.alloy_provider.call(tx.clone(), block).await?; let mut decoded = vec![]; @@ -125,8 +125,8 @@ where // ensure the address is a contract if res.is_empty() { // check that the recipient is a contract that can be called - if let Some(NameOrAddress::Address(addr)) = tx.to() { - if let Ok(code) = self.provider.get_code(*addr, block).await { + if let Some(addr) = tx.to { + if let Ok(code) = self.alloy_provider.get_code_at(addr, block).await { if code.is_empty() { eyre::bail!("contract {addr:?} does not have any code") } @@ -177,18 +177,18 @@ where pub async fn access_list( &self, builder_output: TxBuilderPeekOutput<'_>, - block: Option, + block: Option, to_json: bool, ) -> Result { let (tx, _) = builder_output; - let access_list = self.provider.create_access_list(tx, block).await?; + let access_list = self.alloy_provider.create_access_list(tx.clone(), block).await?; let res = if to_json { serde_json::to_string(&access_list)? } else { let mut s = vec![format!("gas used: {}", access_list.gas_used), "access list:".to_string()]; for al in access_list.access_list.0 { - s.push(format!("- address: {}", &al.address.to_alloy().to_checksum(None))); + s.push(format!("- address: {}", &al.address.to_checksum(None))); if !al.storage_keys.is_empty() { s.push(" keys:".to_string()); for key in al.storage_keys { @@ -202,12 +202,8 @@ where Ok(res) } - pub async fn balance + Send + Sync>( - &self, - who: T, - block: Option, - ) -> Result { - Ok(self.provider.get_balance(who, block).await?.to_alloy()) + pub async fn balance(&self, who: Address, block: Option) -> Result { + Ok(self.alloy_provider.get_balance(who, block).await?) } /// Sends a transaction to the specified address @@ -305,9 +301,9 @@ where pub async fn estimate(&self, builder_output: TxBuilderPeekOutput<'_>) -> Result { let (tx, _) = builder_output; - let res = self.provider.estimate_gas(tx, None).await?; + let res = self.alloy_provider.estimate_gas(tx.clone(), None).await?; - Ok::<_, eyre::Error>(res.to_alloy()) + Ok::<_, eyre::Error>(res) } /// # Example @@ -494,12 +490,8 @@ where /// # Ok(()) /// # } /// ``` - pub async fn nonce + Send + Sync>( - &self, - who: T, - block: Option, - ) -> Result { - Ok(self.provider.get_transaction_count(who, block).await?.to_alloy().to()) + pub async fn nonce(&self, who: Address, block: Option) -> Result { + Ok(self.alloy_provider.get_transaction_count(who, block).await?.to()) } /// # Example @@ -519,15 +511,15 @@ where /// # Ok(()) /// # } /// ``` - pub async fn implementation + Send + Sync>( + pub async fn implementation( &self, - who: T, - block: Option, + who: Address, + block: Option, ) -> Result { let slot = - H256::from_str("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc")?; - let value = self.provider.get_storage_at(who, slot, block).await?; - let addr: H160 = value.into(); + B256::from_str("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc")?; + let value = self.alloy_provider.get_storage_at(who, slot.into(), block).await?; + let addr = Address::from_word(value.into()); Ok(format!("{addr:?}")) } @@ -548,15 +540,11 @@ where /// # Ok(()) /// # } /// ``` - pub async fn admin + Send + Sync>( - &self, - who: T, - block: Option, - ) -> Result { + pub async fn admin(&self, who: Address, block: Option) -> Result { let slot = - H256::from_str("0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103")?; - let value = self.provider.get_storage_at(who, slot, block).await?; - let addr: H160 = value.into(); + B256::from_str("0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103")?; + let value = self.alloy_provider.get_storage_at(who, slot.into(), block).await?; + let addr = Address::from_word(value.into()); Ok(format!("{addr:?}")) } @@ -578,8 +566,7 @@ where /// # } /// ``` pub async fn compute_address(&self, address: Address, nonce: Option) -> Result
{ - let unpacked = - if let Some(n) = nonce { n } else { self.nonce(address.to_ethers(), None).await? }; + let unpacked = if let Some(n) = nonce { n } else { self.nonce(address, None).await? }; Ok(address.create(unpacked)) } @@ -600,17 +587,17 @@ where /// # Ok(()) /// # } /// ``` - pub async fn code + Send + Sync>( + pub async fn code( &self, - who: T, - block: Option, + who: Address, + block: Option, disassemble: bool, ) -> Result { if disassemble { - let code = self.provider.get_code(who, block).await?.to_vec(); + let code = self.alloy_provider.get_code_at(who, block).await?.to_vec(); Ok(format_operations(disassemble_bytes(code)?)?) } else { - Ok(format!("{}", self.provider.get_code(who, block).await?)) + Ok(format!("{}", self.alloy_provider.get_code_at(who, block).await?)) } } @@ -631,12 +618,8 @@ where /// # Ok(()) /// # } /// ``` - pub async fn codesize + Send + Sync>( - &self, - who: T, - block: Option, - ) -> Result { - let code = self.provider.get_code(who, block).await?.to_vec(); + pub async fn codesize(&self, who: Address, block: Option) -> Result { + let code = self.alloy_provider.get_code_at(who, block).await?.to_vec(); Ok(format!("{}", code.len())) } @@ -789,13 +772,16 @@ where /// # Ok(()) /// # } /// ``` - pub async fn storage + Send + Sync>( + pub async fn storage( &self, - from: T, - slot: H256, - block: Option, + from: Address, + slot: B256, + block: Option, ) -> Result { - Ok(format!("{:?}", self.provider.get_storage_at(from, slot, block).await?)) + Ok(format!( + "{:?}", + B256::from(self.alloy_provider.get_storage_at(from, slot.into(), block).await?) + )) } pub async fn filter_logs(&self, filter: Filter, to_json: bool) -> Result { @@ -850,15 +836,14 @@ where /// ``` pub async fn convert_block_number( &self, - block: Option, + block: Option, ) -> Result, eyre::Error> { match block { Some(block) => match block { - BlockHashOrNumber::Number(block_number) => { - Ok(Some(BlockNumberOrTag::Number(block_number))) - } - BlockHashOrNumber::Hash(hash) => { - let block = self.alloy_provider.get_block_by_hash(hash, false).await?; + AlloyBlockId::Number(block_number) => Ok(Some(block_number)), + AlloyBlockId::Hash(hash) => { + let block = + self.alloy_provider.get_block_by_hash(hash.block_hash, false).await?; Ok(block .map(|block| block.header.number.unwrap().to::()) .map(BlockNumberOrTag::from)) diff --git a/crates/cast/src/tx.rs b/crates/cast/src/tx.rs index 1bfd0c40ff67..b20d24563c8b 100644 --- a/crates/cast/src/tx.rs +++ b/crates/cast/src/tx.rs @@ -1,21 +1,23 @@ use crate::errors::FunctionSignatureError; use alloy_json_abi::Function; -use alloy_primitives::{Address, U256}; +use alloy_primitives::{Address, Bytes, U256, U64}; +use alloy_providers::provider::TempProvider; +use alloy_rpc_types::request::{TransactionInput, TransactionRequest as AlloyTransactionRequest}; use ethers_core::types::{ - transaction::eip2718::TypedTransaction, Eip1559TransactionRequest, NameOrAddress, - TransactionRequest, + transaction::eip2718::TypedTransaction, Eip1559TransactionRequest, TransactionRequest, }; -use ethers_providers::Middleware; -use eyre::{eyre, Result}; +use eyre::Result; use foundry_common::{ abi::{encode_function_args, get_func, get_func_etherscan}, - types::{ToAlloy, ToEthers}, + ens::NameOrAddress, + types::ToEthers, }; use foundry_config::Chain; use futures::future::join_all; pub type TxBuilderOutput = (TypedTransaction, Option); -pub type TxBuilderPeekOutput<'a> = (&'a TypedTransaction, &'a Option); +pub type TxBuilderAlloyOutput = (AlloyTransactionRequest, Option); +pub type TxBuilderPeekOutput<'a> = (&'a AlloyTransactionRequest, &'a Option); /// Transaction builder /// @@ -23,62 +25,77 @@ pub type TxBuilderPeekOutput<'a> = (&'a TypedTransaction, &'a Option); /// /// ``` /// # async fn foo() -> eyre::Result<()> { -/// # use alloy_primitives::U256; +/// # use alloy_primitives::{Address, U256}; /// # use cast::TxBuilder; /// # use foundry_config::NamedChain; -/// let provider = ethers_providers::test_provider::MAINNET.provider(); -/// let mut builder = -/// TxBuilder::new(&provider, "a.eth", Some("b.eth"), NamedChain::Mainnet, false).await?; +/// # use std::str::FromStr; +/// let provider = foundry_common::provider::alloy::get_http_provider("http://localhost:8545"); +/// let from = Address::from_str("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045").unwrap(); +/// let to = Address::from_str("0xb8c2c29ee19d8307cb7255e1cd9cbde883a267d5").unwrap(); +/// let mut builder = TxBuilder::new(&provider, from, Some(to), NamedChain::Mainnet, false).await?; /// builder.gas(Some(U256::from(1))); /// let (tx, _) = builder.build(); /// # Ok(()) /// # } /// ``` -pub struct TxBuilder<'a, M: Middleware> { +#[derive(Debug)] +pub struct TxBuilder<'a, P: TempProvider> { to: Option
, chain: Chain, tx: TypedTransaction, + alloy_tx: AlloyTransactionRequest, func: Option, etherscan_api_key: Option, - provider: &'a M, + provider: &'a P, } -impl<'a, M: Middleware> TxBuilder<'a, M> { +impl<'a, P: TempProvider> TxBuilder<'a, P> { /// Create a new TxBuilder /// `provider` - provider to use /// `from` - 'from' field. Could be an ENS name /// `to` - `to`. Could be a ENS /// `chain` - chain to construct the tx for /// `legacy` - use type 1 transaction - pub async fn new, T: Into>( - provider: &'a M, - from: F, - to: Option, + pub async fn new( + provider: &'a P, + from: Address, + to: Option
, chain: impl Into, legacy: bool, - ) -> Result> { + ) -> Result> { let chain = chain.into(); - let from_addr = resolve_ens(provider, from).await?; - let mut tx: TypedTransaction = if chain.is_legacy() || legacy { - TransactionRequest::new().from(from_addr.to_ethers()).chain_id(chain.id()).into() + let (mut tx, mut alloy_tx): (TypedTransaction, AlloyTransactionRequest) = if chain + .is_legacy() || + legacy + { + ( + TransactionRequest::new().from(from.to_ethers()).chain_id(chain.id()).into(), + AlloyTransactionRequest::default().from(from).transaction_type(0), + ) } else { - Eip1559TransactionRequest::new().from(from_addr.to_ethers()).chain_id(chain.id()).into() + ( + Eip1559TransactionRequest::new().from(from.to_ethers()).chain_id(chain.id()).into(), + AlloyTransactionRequest::default().from(from).transaction_type(2), + ) }; let to_addr = if let Some(to) = to { - let addr = resolve_ens(provider, to).await?; - tx.set_to(addr.to_ethers()); - Some(addr) + tx.set_to(to.to_ethers()); + Some(to) } else { None }; - Ok(Self { to: to_addr, chain, tx, func: None, etherscan_api_key: None, provider }) + + alloy_tx.to = to_addr; + + Ok(Self { to: to_addr, chain, tx, alloy_tx, func: None, etherscan_api_key: None, provider }) } /// Set gas for tx pub fn set_gas(&mut self, v: U256) -> &mut Self { self.tx.set_gas(v.to_ethers()); + self.alloy_tx.gas = Some(v); self } @@ -93,6 +110,7 @@ impl<'a, M: Middleware> TxBuilder<'a, M> { /// Set gas price pub fn set_gas_price(&mut self, v: U256) -> &mut Self { self.tx.set_gas_price(v.to_ethers()); + self.alloy_tx.gas_price = Some(v); self } @@ -109,6 +127,11 @@ impl<'a, M: Middleware> TxBuilder<'a, M> { if let TypedTransaction::Eip1559(tx) = &mut self.tx { tx.max_priority_fee_per_gas = Some(v.to_ethers()) } + + if let Some(2) = self.alloy_tx.transaction_type.map(|v| v.to::()) { + self.alloy_tx.max_priority_fee_per_gas = Some(v); + } + self } @@ -123,6 +146,7 @@ impl<'a, M: Middleware> TxBuilder<'a, M> { /// Set value pub fn set_value(&mut self, v: U256) -> &mut Self { self.tx.set_value(v.to_ethers()); + self.alloy_tx.value = Some(v); self } @@ -137,6 +161,7 @@ impl<'a, M: Middleware> TxBuilder<'a, M> { /// Set nonce pub fn set_nonce(&mut self, v: U256) -> &mut Self { self.tx.set_nonce(v.to_ethers()); + self.alloy_tx.nonce = Some(v.to::()); self } @@ -163,7 +188,8 @@ impl<'a, M: Middleware> TxBuilder<'a, M> { } pub fn set_data(&mut self, v: Vec) -> &mut Self { - self.tx.set_data(v.into()); + self.tx.set_data(v.clone().into()); + self.alloy_tx.input = TransactionInput::new(Bytes::from(v.clone())); self } @@ -215,9 +241,10 @@ impl<'a, M: Middleware> TxBuilder<'a, M> { &mut self, sig: &str, args: Vec, - ) -> Result<&mut TxBuilder<'a, M>> { + ) -> Result<&mut TxBuilder<'a, P>> { let (data, func) = self.create_args(sig, args).await?; - self.tx.set_data(data.into()); + self.tx.set_data(data.clone().into()); + self.alloy_tx.input = TransactionInput::new(Bytes::from(data)); self.func = Some(func); Ok(self) } @@ -226,7 +253,7 @@ impl<'a, M: Middleware> TxBuilder<'a, M> { pub async fn args( &mut self, value: Option<(&str, Vec)>, - ) -> Result<&mut TxBuilder<'a, M>> { + ) -> Result<&mut TxBuilder<'a, P>> { if let Some((sig, args)) = value { return self.set_args(sig, args).await } @@ -238,30 +265,22 @@ impl<'a, M: Middleware> TxBuilder<'a, M> { (self.tx, self.func) } - /// Non-consuming build: peek into the tx content - pub fn peek(&self) -> TxBuilderPeekOutput { - (&self.tx, &self.func) + /// Consuming build: returns alloy transaction and optional function call + pub fn build_alloy(self) -> TxBuilderAlloyOutput { + (self.alloy_tx, self.func) } -} -async fn resolve_ens>( - provider: &M, - addr: T, -) -> Result
{ - let from_addr = match addr.into() { - NameOrAddress::Name(ref ens_name) => provider.resolve_name(ens_name).await, - NameOrAddress::Address(addr) => Ok(addr), + pub fn peek(&self) -> TxBuilderPeekOutput { + (&self.alloy_tx, &self.func) } - .map_err(|x| eyre!("Failed to resolve ENS name: {x}"))?; - Ok(from_addr.to_alloy()) } -async fn resolve_name_args(args: &[String], provider: &M) -> Vec { +async fn resolve_name_args(args: &[String], provider: &P) -> Vec { join_all(args.iter().map(|arg| async { if arg.contains('.') { - let addr = provider.resolve_name(arg).await; + let addr = NameOrAddress::Name(arg.to_string()).resolve(provider).await; match addr { - Ok(addr) => format!("{addr:?}"), + Ok(addr) => addr.to_string(), Err(_) => arg.to_string(), } } else { @@ -275,63 +294,22 @@ async fn resolve_name_args(args: &[String], provider: &M) -> Vec< mod tests { use crate::TxBuilder; use alloy_primitives::{Address, U256}; - use async_trait::async_trait; - use ethers_core::types::{transaction::eip2718::TypedTransaction, NameOrAddress, H160}; - use ethers_providers::{JsonRpcClient, Middleware, ProviderError}; + use ethers_core::types::{transaction::eip2718::TypedTransaction, NameOrAddress}; use foundry_common::types::ToEthers; use foundry_config::NamedChain; - use serde::{de::DeserializeOwned, Serialize}; - use std::str::FromStr; - - const ADDR_1: &str = "0000000000000000000000000000000000000001"; - const ADDR_2: &str = "0000000000000000000000000000000000000002"; - - #[derive(Debug)] - struct MyProvider {} - - #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] - #[cfg_attr(not(target_arch = "wasm32"), async_trait)] - impl JsonRpcClient for MyProvider { - type Error = ProviderError; - - async fn request( - &self, - _method: &str, - _params: T, - ) -> Result { - Err(ProviderError::CustomError("There is no request".to_string())) - } - } - #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] - #[cfg_attr(not(target_arch = "wasm32"), async_trait)] - impl Middleware for MyProvider { - type Error = ProviderError; - type Provider = MyProvider; - type Inner = MyProvider; - - fn inner(&self) -> &Self::Inner { - self - } - async fn resolve_name(&self, ens_name: &str) -> Result { - match ens_name { - "a.eth" => Ok(H160::from_str(ADDR_1).unwrap()), - "b.eth" => Ok(H160::from_str(ADDR_2).unwrap()), - _ => unreachable!("don't know how to resolve {ens_name}"), - } - } - } + const ADDR_1: Address = Address::with_last_byte(1); + const ADDR_2: Address = Address::with_last_byte(2); + #[tokio::test(flavor = "multi_thread")] async fn builder_new_non_legacy() -> eyre::Result<()> { - let provider = MyProvider {}; + // Instanciate a local provider although it'll do nothing. + let provider = foundry_common::provider::alloy::get_http_provider("http://localhost:8545"); let builder = - TxBuilder::new(&provider, "a.eth", Some("b.eth"), NamedChain::Mainnet, false).await?; + TxBuilder::new(&provider, ADDR_1, Some(ADDR_2), NamedChain::Mainnet, false).await?; let (tx, args) = builder.build(); - assert_eq!(*tx.from().unwrap(), Address::from_str(ADDR_1).unwrap().to_ethers()); - assert_eq!( - *tx.to().unwrap(), - NameOrAddress::Address(Address::from_str(ADDR_2).unwrap().to_ethers()) - ); + assert_eq!(*tx.from().unwrap(), ADDR_1.to_ethers()); + assert_eq!(*tx.to().unwrap(), NameOrAddress::Address(ADDR_2.to_ethers())); assert_eq!(args, None); match tx { @@ -345,9 +323,9 @@ mod tests { #[tokio::test(flavor = "multi_thread")] async fn builder_new_legacy() -> eyre::Result<()> { - let provider = MyProvider {}; + let provider = foundry_common::provider::alloy::get_http_provider("http://localhost:8545"); let builder = - TxBuilder::new(&provider, "a.eth", Some("b.eth"), NamedChain::Mainnet, true).await?; + TxBuilder::new(&provider, ADDR_1, Some(ADDR_2), NamedChain::Mainnet, true).await?; // don't check anything other than the tx type - the rest is covered in the non-legacy case let (tx, _) = builder.build(); match tx { @@ -361,9 +339,9 @@ mod tests { #[tokio::test(flavor = "multi_thread")] async fn builder_fields() -> eyre::Result<()> { - let provider = MyProvider {}; + let provider = foundry_common::provider::alloy::get_http_provider("http://localhost:8545"); let mut builder = - TxBuilder::new(&provider, "a.eth", Some("b.eth"), NamedChain::Mainnet, false) + TxBuilder::new(&provider, ADDR_1, Some(ADDR_2), NamedChain::Mainnet, false) .await .unwrap(); builder @@ -385,9 +363,9 @@ mod tests { #[tokio::test(flavor = "multi_thread")] async fn builder_args() -> eyre::Result<()> { - let provider = MyProvider {}; + let provider = foundry_common::provider::alloy::get_http_provider("http://localhost:8545"); let mut builder = - TxBuilder::new(&provider, "a.eth", Some("b.eth"), NamedChain::Mainnet, false) + TxBuilder::new(&provider, ADDR_1, Some(ADDR_2), NamedChain::Mainnet, false) .await .unwrap(); builder.args(Some(("what_a_day(int)", vec![String::from("31337")]))).await?;