diff --git a/Cargo.lock b/Cargo.lock index e4cd6be63..77dda0023 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -639,55 +639,12 @@ dependencies = [ "winapi", ] -[[package]] -name = "anstream" -version = "0.6.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - [[package]] name = "anstyle" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" -[[package]] -name = "anstyle-parse" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" -dependencies = [ - "anstyle", - "windows-sys 0.52.0", -] - [[package]] name = "anyhow" version = "1.0.86" @@ -1281,11 +1238,11 @@ checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", "bitflags 1.3.2", - "clap_derive 3.2.25", + "clap_derive", "clap_lex 0.2.4", "indexmap 1.9.3", "once_cell", - "strsim 0.10.0", + "strsim", "termcolor", "textwrap", ] @@ -1297,7 +1254,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", - "clap_derive 4.5.5", ] [[package]] @@ -1306,13 +1262,8 @@ version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ - "anstream", "anstyle", "clap_lex 0.7.1", - "strsim 0.11.1", - "terminal_size", - "unicase", - "unicode-width", ] [[package]] @@ -1328,18 +1279,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "clap_derive" -version = "4.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.66", -] - [[package]] name = "clap_lex" version = "0.2.4" @@ -2481,6 +2420,7 @@ dependencies = [ name = "foundry-common" version = "0.3.8" dependencies = [ + "alloy-chains", "alloy-consensus", "alloy-contract", "alloy-dyn-abi", @@ -2498,13 +2438,11 @@ dependencies = [ "alloy-transport-ipc", "alloy-transport-ws", "async-trait", - "clap 4.5.7", "comfy-table", "dunce", "eyre", "foundry-block-explorers", "foundry-compilers", - "foundry-config", "foundry-linking", "foundry-macros", "glob", @@ -3528,12 +3466,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "is_terminal_polyfill" -version = "1.70.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" - [[package]] name = "itertools" version = "0.10.5" @@ -5972,12 +5904,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - [[package]] name = "strum" version = "0.26.2" @@ -6189,16 +6115,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "terminal_size" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" -dependencies = [ - "rustix", - "windows-sys 0.48.0", -] - [[package]] name = "termtree" version = "0.4.1" @@ -6794,12 +6710,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - [[package]] name = "uuid" version = "0.8.2" diff --git a/crates/foundry/common/Cargo.toml b/crates/foundry/common/Cargo.toml index 70a244619..d36ed018d 100644 --- a/crates/foundry/common/Cargo.toml +++ b/crates/foundry/common/Cargo.toml @@ -7,9 +7,11 @@ edition.workspace = true [dependencies] foundry-block-explorers = { workspace = true, features = ["foundry-compilers"] } foundry-compilers.workspace = true -foundry-config.workspace = true foundry-linking.workspace = true +alloy-chains.workspace = true +alloy-contract.workspace = true +alloy-consensus.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } @@ -24,13 +26,10 @@ alloy-transport-ipc.workspace = true alloy-json-rpc.workspace = true alloy-pubsub.workspace = true alloy-sol-types.workspace = true -alloy-contract.workspace = true -alloy-consensus.workspace = true tower.workspace = true async-trait = "0.1" -clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } comfy-table = "7" dunce = "1" eyre.workspace = true diff --git a/crates/foundry/common/src/abi.rs b/crates/foundry/common/src/abi.rs index 0ada91930..449a812a1 100644 --- a/crates/foundry/common/src/abi.rs +++ b/crates/foundry/common/src/abi.rs @@ -2,12 +2,12 @@ use std::{future::Future, pin::Pin}; +use alloy_chains::Chain; use alloy_dyn_abi::{DynSolType, DynSolValue, FunctionExt, JsonAbiExt}; use alloy_json_abi::{Event, Function}; use alloy_primitives::{hex, Address, LogData}; use eyre::{Context, ContextCompat, Result}; use foundry_block_explorers::{contract::ContractMetadata, errors::EtherscanError, Client}; -use foundry_config::Chain; /// Given a function and a vector of string arguments, it proceeds to convert /// the args to alloy [`DynSolValue`]s and then ABI encode them. diff --git a/crates/foundry/common/src/evm.rs b/crates/foundry/common/src/evm.rs index 09b520f3c..23b37277a 100644 --- a/crates/foundry/common/src/evm.rs +++ b/crates/foundry/common/src/evm.rs @@ -1,353 +1,6 @@ //! cli arguments for configuring the evm settings -use alloy_primitives::{Address, B256, U256}; -use clap::Parser; -use eyre::ContextCompat; -use foundry_config::{ - figment::{ - self, - error::Kind::InvalidType, - value::{Dict, Map, Value}, - Metadata, Profile, Provider, - }, - Chain, Config, -}; +use alloy_primitives::Address; use rustc_hash::FxHashMap; -use serde::Serialize; /// Map keyed by breakpoints char to their location (contract address, pc) pub type Breakpoints = FxHashMap; - -/// `EvmArgs` and `EnvArgs` take the highest precedence in the Config/Figment -/// hierarchy. All vars are opt-in, their default values are expected to be set -/// by the [`foundry_config::Config`], and are always present -/// ([`foundry_config::Config::default`]) -/// -/// Both have corresponding types in the `evm_adapters` crate which have -/// mandatory fields. The expected workflow is -/// 1. load the [`foundry_config::Config`] -/// 2. merge with `EvmArgs` into a `figment::Figment` -/// 3. extract `evm_adapters::Opts` from the merged `Figment` -/// -/// # Example -/// -/// ```ignore -/// use foundry_config::Config; -/// use forge::executor::opts::EvmOpts; -/// use foundry_common::evm::EvmArgs; -/// # fn t(args: EvmArgs) { -/// let figment = Config::figment_with_root(".").merge(args); -/// let opts = figment.extract::().unwrap(); -/// # } -/// ``` -#[derive(Clone, Debug, Default, Serialize, Parser)] -#[command(next_help_heading = "EVM options", about = None, long_about = None)] // override doc -pub struct EvmArgs { - /// Fetch state over a remote endpoint instead of starting from an empty - /// state. - /// - /// If you want to fetch state from a specific block number, see - /// --fork-block-number. - #[arg(long, short, visible_alias = "rpc-url", value_name = "URL")] - #[serde(rename = "eth_rpc_url", skip_serializing_if = "Option::is_none")] - pub fork_url: Option, - - /// Fetch state from a specific block number over a remote endpoint. - /// - /// See --fork-url. - #[arg(long, requires = "fork_url", value_name = "BLOCK")] - #[serde(skip_serializing_if = "Option::is_none")] - pub fork_block_number: Option, - - /// Number of retries. - /// - /// See --fork-url. - #[arg(long, requires = "fork_url", value_name = "RETRIES")] - #[serde(skip_serializing_if = "Option::is_none")] - pub fork_retries: Option, - - /// Initial retry backoff on encountering errors. - /// - /// See --fork-url. - #[arg(long, requires = "fork_url", value_name = "BACKOFF")] - #[serde(skip_serializing_if = "Option::is_none")] - pub fork_retry_backoff: Option, - - /// Explicitly disables the use of RPC caching. - /// - /// All storage slots are read entirely from the endpoint. - /// - /// This flag overrides the project's configuration file. - /// - /// See --fork-url. - #[arg(long)] - #[serde(skip)] - pub no_storage_caching: bool, - - /// The initial balance of deployed test contracts. - #[arg(long, value_name = "BALANCE")] - #[serde(skip_serializing_if = "Option::is_none")] - pub initial_balance: Option, - - /// The address which will be executing tests. - #[arg(long, value_name = "ADDRESS")] - #[serde(skip_serializing_if = "Option::is_none")] - pub sender: Option
, - - /// Enable the FFI cheatcode. - #[arg(long)] - #[serde(skip)] - pub ffi: bool, - - /// Use the create 2 factory in all cases including tests and - /// non-broadcasting scripts. - #[arg(long)] - #[serde(skip)] - pub always_use_create_2_factory: bool, - - /// Sets the number of assumed available compute units per second for this - /// provider - /// - /// default value: 330 - /// - /// See also --fork-url and - #[arg( - long, - alias = "cups", - value_name = "CUPS", - help_heading = "Fork config" - )] - pub compute_units_per_second: Option, - - /// Disables rate limiting for this node's provider. - /// - /// See also --fork-url and - #[arg( - long, - value_name = "NO_RATE_LIMITS", - help_heading = "Fork config", - visible_alias = "no-rate-limit" - )] - #[serde(skip)] - pub no_rpc_rate_limit: bool, - - /// All ethereum environment related arguments - #[command(flatten)] - #[serde(flatten)] - pub env: EnvArgs, - - /// Whether to enable isolation of calls. - /// In isolation mode all top-level calls are executed as a separate - /// transaction in a separate EVM context, enabling more precise gas - /// accounting and transaction state changes. - #[arg(long)] - #[serde(skip)] - pub isolate: bool, -} - -// Make this set of options a `figment::Provider` so that it can be merged into -// the `Config` -impl Provider for EvmArgs { - fn metadata(&self) -> Metadata { - Metadata::named("Evm Opts Provider") - } - - fn data(&self) -> Result, figment::Error> { - let value = Value::serialize(self)?; - let error = InvalidType(value.to_actual(), "map".into()); - let mut dict = value.into_dict().ok_or(error)?; - - if self.ffi { - dict.insert("ffi".to_string(), self.ffi.into()); - } - - if self.isolate { - dict.insert("isolate".to_string(), self.isolate.into()); - } - - if self.always_use_create_2_factory { - dict.insert( - "always_use_create_2_factory".to_string(), - self.always_use_create_2_factory.into(), - ); - } - - if self.no_storage_caching { - dict.insert( - "no_storage_caching".to_string(), - self.no_storage_caching.into(), - ); - } - - if self.no_rpc_rate_limit { - dict.insert( - "no_rpc_rate_limit".to_string(), - self.no_rpc_rate_limit.into(), - ); - } - - if let Some(fork_url) = &self.fork_url { - dict.insert("eth_rpc_url".to_string(), fork_url.clone().into()); - } - - Ok(Map::from([(Config::selected_profile(), dict)])) - } -} - -/// Configures the executor environment during tests. -#[derive(Clone, Debug, Default, Serialize, Parser)] -#[command(next_help_heading = "Executor environment config")] -pub struct EnvArgs { - /// The block gas limit. - #[arg(long, value_name = "GAS_LIMIT")] - #[serde(skip_serializing_if = "Option::is_none")] - pub gas_limit: Option, - - /// EIP-170: Contract code size limit in bytes. Useful to increase this - /// because of tests. By default, it is 0x6000 (~25kb). - #[arg(long, value_name = "CODE_SIZE")] - #[serde(skip_serializing_if = "Option::is_none")] - pub code_size_limit: Option, - - /// The chain name or EIP-155 chain ID. - #[arg(long, visible_alias = "chain-id", value_name = "CHAIN")] - #[serde( - rename = "chain_id", - skip_serializing_if = "Option::is_none", - serialize_with = "id" - )] - pub chain: Option, - - /// The gas price. - #[arg(long, value_name = "GAS_PRICE")] - #[serde(skip_serializing_if = "Option::is_none")] - pub gas_price: Option, - - /// The base fee in a block. - #[arg(long, visible_alias = "base-fee", value_name = "FEE")] - #[serde(skip_serializing_if = "Option::is_none")] - pub block_base_fee_per_gas: Option, - - /// The transaction origin. - #[arg(long, value_name = "ADDRESS")] - #[serde(skip_serializing_if = "Option::is_none")] - pub tx_origin: Option
, - - /// The coinbase of the block. - #[arg(long, value_name = "ADDRESS")] - #[serde(skip_serializing_if = "Option::is_none")] - pub block_coinbase: Option
, - - /// The timestamp of the block. - #[arg(long, value_name = "TIMESTAMP")] - #[serde(skip_serializing_if = "Option::is_none")] - pub block_timestamp: Option, - - /// The block number. - #[arg(long, value_name = "BLOCK")] - #[serde(skip_serializing_if = "Option::is_none")] - pub block_number: Option, - - /// The block difficulty. - #[arg(long, value_name = "DIFFICULTY")] - #[serde(skip_serializing_if = "Option::is_none")] - pub block_difficulty: Option, - - /// The block prevrandao value. NOTE: Before merge this field was - /// `mix_hash`. - #[arg(long, value_name = "PREVRANDAO")] - #[serde(skip_serializing_if = "Option::is_none")] - pub block_prevrandao: Option, - - /// The block gas limit. - #[arg(long, value_name = "GAS_LIMIT")] - #[serde(skip_serializing_if = "Option::is_none")] - pub block_gas_limit: Option, - - /// The memory limit per EVM execution in bytes. - /// If this limit is exceeded, a `MemoryLimitOOG` result is thrown. - /// - /// The default is 128MiB. - #[arg(long, value_name = "MEMORY_LIMIT")] - #[serde(skip_serializing_if = "Option::is_none")] - pub memory_limit: Option, - - /// Whether to disable the block gas limit checks. - #[arg(long, visible_alias = "no-gas-limit")] - pub disable_block_gas_limit: bool, -} - -impl EvmArgs { - /// Ensures that fork url exists and returns its reference. - pub fn ensure_fork_url(&self) -> eyre::Result<&String> { - self.fork_url - .as_ref() - .wrap_err("Missing `--fork-url` field.") - } -} - -/// We have to serialize chain IDs and not names because when extracting an EVM -/// `Env`, it expects `chain_id` to be `u64`. -#[allow(clippy::trivially_copy_pass_by_ref)] -fn id(chain: &Option, s: S) -> Result { - if let Some(chain) = chain { - s.serialize_u64(chain.id()) - } else { - // skip_serializing_if = "Option::is_none" should prevent this branch from being - // taken - unreachable!() - } -} - -#[cfg(test)] -mod tests { - use foundry_config::NamedChain; - - use super::*; - - #[test] - fn can_parse_chain_id() { - let args = EvmArgs { - env: EnvArgs { - chain: Some(NamedChain::Mainnet.into()), - ..Default::default() - }, - ..Default::default() - }; - let config = Config::from_provider(Config::figment().merge(args)); - assert_eq!(config.chain, Some(NamedChain::Mainnet.into())); - - let env = EnvArgs::parse_from(["foundry-common", "--chain-id", "goerli"]); - assert_eq!(env.chain, Some(NamedChain::Goerli.into())); - } - - #[test] - fn test_memory_limit() { - let args = EvmArgs { - env: EnvArgs { - chain: Some(NamedChain::Mainnet.into()), - ..Default::default() - }, - ..Default::default() - }; - let config = Config::from_provider(Config::figment().merge(args)); - assert_eq!(config.memory_limit, Config::default().memory_limit); - - let env = EnvArgs::parse_from(["foundry-common", "--memory-limit", "100"]); - assert_eq!(env.memory_limit, Some(100)); - } - - #[test] - fn test_chain_id() { - let env = EnvArgs::parse_from(["foundry-common", "--chain-id", "1"]); - assert_eq!(env.chain, Some(Chain::mainnet())); - - let env = EnvArgs::parse_from(["foundry-common", "--chain-id", "mainnet"]); - assert_eq!(env.chain, Some(Chain::mainnet())); - let args = EvmArgs { - env, - ..Default::default() - }; - let config = Config::from_provider(Config::figment().merge(args)); - assert_eq!(config.chain, Some(Chain::mainnet())); - } -} diff --git a/crates/foundry/common/src/provider/mod.rs b/crates/foundry/common/src/provider/mod.rs index 76a5d8607..c6c33093e 100644 --- a/crates/foundry/common/src/provider/mod.rs +++ b/crates/foundry/common/src/provider/mod.rs @@ -11,6 +11,7 @@ use std::{ time::Duration, }; +use alloy_chains::NamedChain; use alloy_provider::{ fillers::{ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, SignerFiller}, network::{AnyNetwork, EthereumSigner}, @@ -19,7 +20,6 @@ use alloy_provider::{ use alloy_rpc_client::ClientBuilder; use alloy_transport::utils::guess_local_url; use eyre::{Result, WrapErr}; -use foundry_config::NamedChain; use reqwest::Url; use runtime_transport::RuntimeTransport; use tower::{RetryBackoffLayer, RetryBackoffService};