From 5af9d16ecb620192d4fe5ae61d33e429b7f5aff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ca=C3=ADque=20Porfirio?= <56317416+caiquejjx@users.noreply.github.com> Date: Tue, 23 Jul 2024 13:11:18 -0300 Subject: [PATCH 001/184] feat: add mixed mining mode (#8280) * feat: add mixed mining mode * feat: add mixed mining usage * fix: ensure unique txs * chore: undo comment deletion * touchups --------- Co-authored-by: Matthias Seitz --- crates/anvil/src/cmd.rs | 4 ++++ crates/anvil/src/config.rs | 14 ++++++++++++++ crates/anvil/src/eth/miner.rs | 33 +++++++++++++++++++++++++++++++++ crates/anvil/src/lib.rs | 8 +++++++- 4 files changed, 58 insertions(+), 1 deletion(-) diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index f86703023..e12c6b13a 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -98,6 +98,9 @@ pub struct NodeArgs { #[arg(long, visible_alias = "no-mine", conflicts_with = "block_time")] pub no_mining: bool, + #[arg(long, visible_alias = "mixed-mining", requires = "block_time")] + pub mixed_mining: bool, + /// The hosts the server will listen on. #[arg( long, @@ -200,6 +203,7 @@ impl NodeArgs { .with_hardfork(self.hardfork) .with_blocktime(self.block_time) .with_no_mining(self.no_mining) + .with_mixed_mining(self.mixed_mining, self.block_time) .with_account_generator(self.account_generator()) .with_genesis_balance(genesis_balance) .with_genesis_timestamp(self.timestamp) diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index ef6b2503d..4ae1be0e9 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -113,6 +113,8 @@ pub struct NodeConfig { pub block_time: Option, /// Disable auto, interval mining mode uns use `MiningMode::None` instead pub no_mining: bool, + /// Enables auto and interval mining mode + pub mixed_mining: bool, /// port to use for the server pub port: u16, /// maximum number of transactions in a block @@ -395,6 +397,7 @@ impl Default for NodeConfig { genesis_balance: Unit::ETHER.wei().saturating_mul(U256::from(100u64)), block_time: None, no_mining: false, + mixed_mining: false, port: NODE_PORT, // TODO make this something dependent on block capacity max_transactions: 1_000, @@ -633,6 +636,17 @@ impl NodeConfig { self } + #[must_use] + pub fn with_mixed_mining>( + mut self, + mixed_mining: bool, + block_time: Option, + ) -> Self { + self.block_time = block_time.map(Into::into); + self.mixed_mining = mixed_mining; + self + } + /// If set to `true` auto mining will be disabled #[must_use] pub fn with_no_mining(mut self, no_mining: bool) -> Self { diff --git a/crates/anvil/src/eth/miner.rs b/crates/anvil/src/eth/miner.rs index ddd271850..defb6624a 100644 --- a/crates/anvil/src/eth/miner.rs +++ b/crates/anvil/src/eth/miner.rs @@ -132,6 +132,9 @@ pub enum MiningMode { Auto(ReadyTransactionMiner), /// A miner that constructs a new block every `interval` tick FixedBlockTime(FixedBlockTimeMiner), + + /// A minner that uses both Auto and FixedBlockTime + Mixed(ReadyTransactionMiner, FixedBlockTimeMiner), } impl MiningMode { @@ -147,6 +150,13 @@ impl MiningMode { Self::FixedBlockTime(FixedBlockTimeMiner::new(duration)) } + pub fn mixed(max_transactions: usize, listener: Receiver, duration: Duration) -> Self { + Self::Mixed( + ReadyTransactionMiner { max_transactions, has_pending_txs: None, rx: listener.fuse() }, + FixedBlockTimeMiner::new(duration), + ) + } + /// polls the [Pool] and returns those transactions that should be put in a block, if any. pub fn poll( &mut self, @@ -157,6 +167,29 @@ impl MiningMode { Self::None => Poll::Pending, Self::Auto(miner) => miner.poll(pool, cx), Self::FixedBlockTime(miner) => miner.poll(pool, cx), + Self::Mixed(auto, fixed) => { + let auto_txs = auto.poll(pool, cx); + let fixed_txs = fixed.poll(pool, cx); + + match (auto_txs, fixed_txs) { + // Both auto and fixed transactions are ready, combine them + (Poll::Ready(mut auto_txs), Poll::Ready(fixed_txs)) => { + for tx in fixed_txs { + // filter unique transactions + if auto_txs.iter().any(|auto_tx| auto_tx.hash() == tx.hash()) { + continue; + } + auto_txs.push(tx); + } + Poll::Ready(auto_txs) + } + // Only auto transactions are ready, return them + (Poll::Ready(auto_txs), Poll::Pending) => Poll::Ready(auto_txs), + // Only fixed transactions are ready or both are pending, + // return fixed transactions or pending status + (Poll::Pending, fixed_txs) => fixed_txs, + } + } } } } diff --git a/crates/anvil/src/lib.rs b/crates/anvil/src/lib.rs index 56005dd60..dc7e0969f 100644 --- a/crates/anvil/src/lib.rs +++ b/crates/anvil/src/lib.rs @@ -142,13 +142,19 @@ pub async fn try_spawn(mut config: NodeConfig) -> io::Result<(EthApi, NodeHandle no_mining, transaction_order, genesis, + mixed_mining, .. } = config.clone(); let pool = Arc::new(Pool::default()); let mode = if let Some(block_time) = block_time { - MiningMode::interval(block_time) + if mixed_mining { + let listener = pool.add_ready_listener(); + MiningMode::mixed(max_transactions, listener, block_time) + } else { + MiningMode::interval(block_time) + } } else if no_mining { MiningMode::None } else { From 781fe52e592ef578a277ed76ce8f6ff99c034436 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 25 Jul 2024 03:09:59 +0800 Subject: [PATCH 002/184] chore: make clippy happy (#8515) --- crates/debugger/src/tui/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/debugger/src/tui/mod.rs b/crates/debugger/src/tui/mod.rs index 6b522250b..155973e15 100644 --- a/crates/debugger/src/tui/mod.rs +++ b/crates/debugger/src/tui/mod.rs @@ -136,6 +136,8 @@ impl Debugger { } } +// TODO: Update once on 1.82 +#[allow(deprecated)] type PanicHandler = Box) + 'static + Sync + Send>; /// Handles terminal state. From 2b301a7c25cfe3d58063f81e2e022b8b51083785 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 25 Jul 2024 03:35:14 +0800 Subject: [PATCH 003/184] fix: respect profiles in inline configs (#8514) * exclude internal fns tests for test-isolate * fix: respect profiles when merging inline configs --- crates/config/src/inline/conf_parser.rs | 8 +++++--- crates/forge/tests/cli/test_cmd.rs | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/config/src/inline/conf_parser.rs b/crates/config/src/inline/conf_parser.rs index c2b7b39f7..e69449654 100644 --- a/crates/config/src/inline/conf_parser.rs +++ b/crates/config/src/inline/conf_parser.rs @@ -37,12 +37,14 @@ where /// - `Err(InlineConfigParserError)` in case of wrong configuration. fn try_merge(&self, configs: &[String]) -> Result, InlineConfigParserError>; - /// Validates and merges the natspec configs into the current config. + /// Validates and merges the natspec configs for current profile into the current config. fn merge(&self, natspec: &NatSpec) -> Result, InlineConfigError> { let config_key = Self::config_key(); - let configs = - natspec.config_lines().filter(|l| l.contains(&config_key)).collect::>(); + let configs = natspec + .current_profile_configs() + .filter(|l| l.contains(&config_key)) + .collect::>(); self.try_merge(&configs).map_err(|e| { let line = natspec.debug_context(); diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index a3239e3f5..23f77f56b 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -904,6 +904,7 @@ forgetest_init!(should_not_show_logs_when_fuzz_test_inline_config, |prj, cmd| { }); // tests internal functions trace +#[cfg(not(feature = "isolate-by-default"))] forgetest_init!(internal_functions_trace, |prj, cmd| { prj.wipe_contracts(); prj.clear(); @@ -971,6 +972,7 @@ Traces: }); // tests internal functions trace with memory decoding +#[cfg(not(feature = "isolate-by-default"))] forgetest_init!(internal_functions_trace_memory, |prj, cmd| { prj.wipe_contracts(); prj.clear(); From 6c5816773e5ea2ef7c652b735c9df46a53d134bb Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Wed, 24 Jul 2024 14:03:36 -0700 Subject: [PATCH 004/184] feat(cast,common): calldata-decode, abi-decode, and 4byte-decode json flag (#8494) * add calldata-decode json flag * use print_tokens for calldata-decate, abi-decode, and 4byte-decode * fix clippy --- crates/cast/bin/main.rs | 18 +++++++----------- crates/cast/bin/opts.rs | 12 ++++++++++++ crates/common/fmt/src/dynamic.rs | 17 +++++++++++++++++ crates/common/fmt/src/lib.rs | 4 +++- 4 files changed, 39 insertions(+), 12 deletions(-) diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index ff4f799cd..044cc25ff 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -12,7 +12,7 @@ use foundry_cli::{handler, prompt, stdin, utils}; use foundry_common::{ abi::get_event, ens::{namehash, ProviderEnsExt}, - fmt::{format_tokens, format_uint_exp}, + fmt::{format_uint_exp, print_tokens}, fs, selectors::{ decode_calldata, decode_event_topic, decode_function_selector, decode_selectors, @@ -163,10 +163,9 @@ async fn main() -> Result<()> { } // ABI encoding & decoding - CastSubcommand::AbiDecode { sig, calldata, input } => { + CastSubcommand::AbiDecode { sig, calldata, input, json } => { let tokens = SimpleCast::abi_decode(&sig, &calldata, input)?; - let tokens = format_tokens(&tokens); - tokens.for_each(|t| println!("{t}")); + print_tokens(&tokens, json) } CastSubcommand::AbiEncode { sig, packed, args } => { if !packed { @@ -175,10 +174,9 @@ async fn main() -> Result<()> { println!("{}", SimpleCast::abi_encode_packed(&sig, &args)?); } } - CastSubcommand::CalldataDecode { sig, calldata } => { + CastSubcommand::CalldataDecode { sig, calldata, json } => { let tokens = SimpleCast::calldata_decode(&sig, &calldata, true)?; - let tokens = format_tokens(&tokens); - tokens.for_each(|t| println!("{t}")); + print_tokens(&tokens, json) } CastSubcommand::CalldataEncode { sig, args } => { println!("{}", SimpleCast::calldata_encode(sig, &args)?); @@ -425,7 +423,7 @@ async fn main() -> Result<()> { println!("{sig}"); } } - CastSubcommand::FourByteDecode { calldata } => { + CastSubcommand::FourByteDecode { calldata, json } => { let calldata = stdin::unwrap_line(calldata)?; let sigs = decode_calldata(&calldata).await?; sigs.iter().enumerate().for_each(|(i, sig)| println!("{}) \"{sig}\"", i + 1)); @@ -440,9 +438,7 @@ async fn main() -> Result<()> { }; let tokens = SimpleCast::calldata_decode(sig, &calldata, true)?; - for token in format_tokens(&tokens) { - println!("{token}"); - } + print_tokens(&tokens, json) } CastSubcommand::FourByteEvent { topic } => { let topic = stdin::unwrap_line(topic)?; diff --git a/crates/cast/bin/opts.rs b/crates/cast/bin/opts.rs index 5b3c09c8a..395b4fe27 100644 --- a/crates/cast/bin/opts.rs +++ b/crates/cast/bin/opts.rs @@ -490,6 +490,10 @@ pub enum CastSubcommand { /// The ABI-encoded calldata. calldata: String, + + /// Print the decoded calldata as JSON. + #[arg(long, short, help_heading = "Display options")] + json: bool, }, /// Decode ABI-encoded input or output data. @@ -508,6 +512,10 @@ pub enum CastSubcommand { /// Whether to decode the input or output data. #[arg(long, short, help_heading = "Decode input data instead of output data")] input: bool, + + /// Print the decoded calldata as JSON. + #[arg(long, short, help_heading = "Display options")] + json: bool, }, /// ABI encode the given function argument, excluding the selector. @@ -594,6 +602,10 @@ pub enum CastSubcommand { FourByteDecode { /// The ABI-encoded calldata. calldata: Option, + + /// Print the decoded calldata as JSON. + #[arg(long, short, help_heading = "Display options")] + json: bool, }, /// Get the event signature for a given topic 0 from https://openchain.xyz. diff --git a/crates/common/fmt/src/dynamic.rs b/crates/common/fmt/src/dynamic.rs index 5055e6561..4c59989e9 100644 --- a/crates/common/fmt/src/dynamic.rs +++ b/crates/common/fmt/src/dynamic.rs @@ -119,6 +119,23 @@ pub fn format_tokens(tokens: &[DynSolValue]) -> impl Iterator + ' tokens.iter().map(format_token) } +/// Pretty-prints a slice of tokens using [`format_token_raw`]. +pub fn format_tokens_raw(tokens: &[DynSolValue]) -> impl Iterator + '_ { + tokens.iter().map(format_token_raw) +} + +/// Prints slice of tokens using [`format_tokens`] or [`format_tokens_raw`] depending on `json` +/// parameter. +pub fn print_tokens(tokens: &[DynSolValue], json: bool) { + if json { + let tokens: Vec = format_tokens_raw(tokens).collect(); + println!("{}", serde_json::to_string_pretty(&tokens).unwrap()); + } else { + let tokens = format_tokens(tokens); + tokens.for_each(|t| println!("{t}")); + } +} + /// Pretty-prints the given value into a string suitable for user output. pub fn format_token(value: &DynSolValue) -> String { DynValueDisplay::new(value, false).to_string() diff --git a/crates/common/fmt/src/lib.rs b/crates/common/fmt/src/lib.rs index c02090809..f6f792e99 100644 --- a/crates/common/fmt/src/lib.rs +++ b/crates/common/fmt/src/lib.rs @@ -4,7 +4,9 @@ mod console; pub use console::{console_format, ConsoleFmt, FormatSpec}; mod dynamic; -pub use dynamic::{format_token, format_token_raw, format_tokens, parse_tokens}; +pub use dynamic::{ + format_token, format_token_raw, format_tokens, format_tokens_raw, parse_tokens, print_tokens, +}; mod exp; pub use exp::{format_int_exp, format_uint_exp, to_exp_notation}; From 0ad127a36ad716b09d180bb60b47c8eff1349202 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 25 Jul 2024 05:06:14 +0800 Subject: [PATCH 005/184] fix: some verify-bytecode fixes (#8513) * fix: some verify-bytecode fixes * update constructor args handling * --encoded-constructor-args * warn on length mismatch --------- Co-authored-by: Matthias Seitz --- Cargo.lock | 1 + crates/common/src/abi.rs | 17 ++- crates/verify/Cargo.toml | 1 + crates/verify/src/bytecode.rs | 241 +++++++++++++++++----------------- 4 files changed, 132 insertions(+), 128 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 42579ff89..a0797e8ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3502,6 +3502,7 @@ dependencies = [ name = "forge-verify" version = "0.2.0" dependencies = [ + "alloy-dyn-abi", "alloy-json-abi", "alloy-primitives", "alloy-provider", diff --git a/crates/common/src/abi.rs b/crates/common/src/abi.rs index a7c545bc8..bcf82b1cd 100644 --- a/crates/common/src/abi.rs +++ b/crates/common/src/abi.rs @@ -1,13 +1,23 @@ //! ABI related helper functions. use alloy_dyn_abi::{DynSolType, DynSolValue, FunctionExt, JsonAbiExt}; -use alloy_json_abi::{Event, Function}; +use alloy_json_abi::{Event, Function, Param}; use alloy_primitives::{hex, Address, LogData}; use eyre::{Context, ContextCompat, Result}; use foundry_block_explorers::{contract::ContractMetadata, errors::EtherscanError, Client}; use foundry_config::Chain; use std::{future::Future, pin::Pin}; +pub fn encode_args(inputs: &[Param], args: I) -> Result> +where + I: IntoIterator, + S: AsRef, +{ + std::iter::zip(inputs, args) + .map(|(input, arg)| coerce_value(&input.selector_type(), arg.as_ref())) + .collect() +} + /// Given a function and a vector of string arguments, it proceeds to convert the args to alloy /// [DynSolValue]s and then ABI encode them. pub fn encode_function_args(func: &Function, args: I) -> Result> @@ -15,10 +25,7 @@ where I: IntoIterator, S: AsRef, { - let params = std::iter::zip(&func.inputs, args) - .map(|(input, arg)| coerce_value(&input.selector_type(), arg.as_ref())) - .collect::>>()?; - func.abi_encode_input(params.as_slice()).map_err(Into::into) + Ok(func.abi_encode_input(&encode_args(&func.inputs, args)?)?) } /// Given a function and a vector of string arguments, it proceeds to convert the args to alloy diff --git a/crates/verify/Cargo.toml b/crates/verify/Cargo.toml index 02da21157..97ff5e425 100644 --- a/crates/verify/Cargo.toml +++ b/crates/verify/Cargo.toml @@ -22,6 +22,7 @@ serde_json.workspace = true alloy-json-abi.workspace = true alloy-primitives.workspace = true alloy-rpc-types.workspace = true +alloy-dyn-abi.workspace = true revm-primitives.workspace = true serde.workspace = true eyre.workspace = true diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index dfa8c31a9..a0b3a7c84 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -1,4 +1,5 @@ -use alloy_primitives::{Address, Bytes, U256}; +use alloy_dyn_abi::DynSolValue; +use alloy_primitives::{hex, Address, Bytes, U256}; use alloy_provider::Provider; use alloy_rpc_types::{BlockId, BlockNumberOrTag}; use clap::{Parser, ValueHint}; @@ -8,11 +9,10 @@ use foundry_cli::{ opts::EtherscanOpts, utils::{self, read_constructor_args_file, LoadConfig}, }; -use foundry_common::{compile::ProjectCompiler, provider::ProviderBuilder}; +use foundry_common::{abi::encode_args, compile::ProjectCompiler, provider::ProviderBuilder}; use foundry_compilers::{ - artifacts::{BytecodeHash, BytecodeObject, CompactContractBytecode, EvmVersion}, + artifacts::{BytecodeHash, CompactContractBytecode, EvmVersion}, info::ContractInfo, - Artifact, }; use foundry_config::{figment, filter::SkipBuildFilter, impl_figment_convert, Chain, Config}; use foundry_evm::{ @@ -42,25 +42,33 @@ pub struct VerifyBytecodeArgs { /// The constructor args to generate the creation code. #[clap( long, - conflicts_with = "constructor_args_path", + num_args(1..), + conflicts_with_all = &["constructor_args_path", "encoded_constructor_args"], value_name = "ARGS", - visible_alias = "encoded-constructor-args" )] - pub constructor_args: Option, + pub constructor_args: Option>, + + /// The ABI-encoded constructor arguments. + #[arg( + long, + conflicts_with_all = &["constructor_args_path", "constructor_args"], + value_name = "HEX", + )] + pub encoded_constructor_args: Option, /// The path to a file containing the constructor arguments. - #[clap(long, value_hint = ValueHint::FilePath, value_name = "PATH")] + #[arg( + long, + value_hint = ValueHint::FilePath, + value_name = "PATH", + conflicts_with_all = &["constructor_args", "encoded_constructor_args"] + )] pub constructor_args_path: Option, /// The rpc url to use for verification. #[clap(short = 'r', long, value_name = "RPC_URL", env = "ETH_RPC_URL")] pub rpc_url: Option, - /// Verfication Type: `full` or `partial`. - /// Ref: - #[clap(long, default_value = "full", value_name = "TYPE")] - pub verification_type: VerificationType, - #[clap(flatten)] pub etherscan_opts: EtherscanOpts, @@ -86,14 +94,13 @@ impl figment::Provider for VerifyBytecodeArgs { fn data( &self, ) -> Result, figment::Error> { - let mut dict = figment::value::Dict::new(); + let mut dict = self.etherscan_opts.dict(); if let Some(block) = &self.block { dict.insert("block".into(), figment::value::Value::serialize(block)?); } if let Some(rpc_url) = &self.rpc_url { dict.insert("eth_rpc_url".into(), rpc_url.to_string().into()); } - dict.insert("verification_type".into(), self.verification_type.to_string().into()); Ok(figment::value::Map::from([(Config::selected_profile(), dict)])) } @@ -149,31 +156,60 @@ impl VerifyBytecodeArgs { eyre::bail!("Contract name mismatch"); } + // Obtain Etherscan compilation metadata + let etherscan_metadata = source_code.items.first().unwrap(); + + // Obtain local artifact + let artifact = + if let Ok(local_bytecode) = self.build_using_cache(etherscan_metadata, &config) { + trace!("using cache"); + local_bytecode + } else { + self.build_project(&config)? + }; + // Get the constructor args from etherscan - let constructor_args = if let Some(args) = source_code.items.first() { + let mut constructor_args = if let Some(args) = source_code.items.first() { args.constructor_arguments.clone() } else { eyre::bail!("No constructor arguments found for contract at address {}", self.address); }; - // Get user provided constructor args - let provided_constructor_args = if let Some(args) = self.constructor_args.to_owned() { - args - } else if let Some(path) = self.constructor_args_path.to_owned() { + // Get and encode user provided constructor args + let provided_constructor_args = if let Some(path) = self.constructor_args_path.to_owned() { // Read from file - let res = read_constructor_args_file(path)?; - // Convert res to Bytes - res.join("") + Some(read_constructor_args_file(path)?) } else { - constructor_args.to_string() - }; + self.constructor_args.to_owned() + } + .map(|args| { + if let Some(constructor) = artifact.abi.as_ref().and_then(|abi| abi.constructor()) { + if constructor.inputs.len() != args.len() { + eyre::bail!( + "Mismatch of constructor arguments length. Expected {}, got {}", + constructor.inputs.len(), + args.len() + ); + } + encode_args(&constructor.inputs, &args) + .map(|args| DynSolValue::Tuple(args).abi_encode()) + } else { + Ok(Vec::new()) + } + }) + .transpose()? + .or(self.encoded_constructor_args.to_owned().map(hex::decode).transpose()?); - // Constructor args mismatch - if provided_constructor_args != constructor_args.to_string() && !self.json { - println!( - "{}", - "The provided constructor args do not match the constructor args from etherscan. This will result in a mismatch - Using the args from etherscan".red().bold(), - ); + if let Some(provided) = provided_constructor_args { + if provided != constructor_args && !self.json { + println!( + "{}", + format!("The provided constructor args do not match the constructor args from etherscan ({constructor_args}).") + .yellow() + .bold(), + ); + } + constructor_args = provided.into(); } // Get creation tx hash @@ -215,27 +251,12 @@ impl VerifyBytecodeArgs { }; // If bytecode_hash is disabled then its always partial verification - let (verification_type, has_metadata) = - match (&self.verification_type, config.bytecode_hash) { - (VerificationType::Full, BytecodeHash::None) => (VerificationType::Partial, false), - (VerificationType::Partial, BytecodeHash::None) => { - (VerificationType::Partial, false) - } - (VerificationType::Full, _) => (VerificationType::Full, true), - (VerificationType::Partial, _) => (VerificationType::Partial, true), - }; + let has_metadata = config.bytecode_hash == BytecodeHash::None; - trace!(?verification_type, has_metadata); - // Etherscan compilation metadata - let etherscan_metadata = source_code.items.first().unwrap(); - - let local_bytecode = - if let Some(local_bytecode) = self.build_using_cache(etherscan_metadata, &config) { - trace!("using cache"); - local_bytecode - } else { - self.build_project(&config)? - }; + let local_bytecode = artifact + .bytecode + .and_then(|b| b.into_bytes()) + .ok_or_eyre("Unlinked bytecode is not supported for verification")?; // Append constructor args to the local_bytecode trace!(%constructor_args); @@ -243,18 +264,17 @@ impl VerifyBytecodeArgs { local_bytecode_vec.extend_from_slice(&constructor_args); // Cmp creation code with locally built bytecode and maybe_creation_code - let (did_match, with_status) = try_match( + let match_type = match_bytecodes( local_bytecode_vec.as_slice(), maybe_creation_code, &constructor_args, - &verification_type, false, has_metadata, - )?; + ); let mut json_results: Vec = vec![]; self.print_result( - (did_match, with_status), + match_type, BytecodeType::Creation, &mut json_results, etherscan_metadata, @@ -262,9 +282,9 @@ impl VerifyBytecodeArgs { ); // If the creation code does not match, the runtime also won't match. Hence return. - if !did_match { + if match_type.is_none() { self.print_result( - (did_match, with_status), + None, BytecodeType::Runtime, &mut json_results, etherscan_metadata, @@ -382,17 +402,16 @@ impl VerifyBytecodeArgs { provider.get_code_at(self.address).block_id(BlockId::number(simulation_block)).await?; // Compare the onchain runtime bytecode with the runtime code from the fork. - let (did_match, with_status) = try_match( + let match_type = match_bytecodes( &fork_runtime_code.original_bytes(), &onchain_runtime_code, &constructor_args, - &verification_type, true, has_metadata, - )?; + ); self.print_result( - (did_match, with_status), + match_type, BytecodeType::Runtime, &mut json_results, etherscan_metadata, @@ -405,41 +424,34 @@ impl VerifyBytecodeArgs { Ok(()) } - fn build_project(&self, config: &Config) -> Result { + fn build_project(&self, config: &Config) -> Result { let project = config.project()?; let compiler = ProjectCompiler::new(); - let output = compiler.compile(&project)?; + let mut output = compiler.compile(&project)?; let artifact = output - .find_contract(&self.contract) + .remove_contract(&self.contract) .ok_or_eyre("Build Error: Contract artifact not found locally")?; - let local_bytecode = artifact - .get_bytecode_object() - .ok_or_eyre("Contract artifact does not have bytecode")?; - - let local_bytecode = match local_bytecode.as_ref() { - BytecodeObject::Bytecode(bytes) => bytes, - BytecodeObject::Unlinked(_) => { - eyre::bail!("Unlinked bytecode is not supported for verification") - } - }; - - Ok(local_bytecode.to_owned()) + Ok(artifact.into_contract_bytecode()) } - fn build_using_cache(&self, etherscan_settings: &Metadata, config: &Config) -> Option { - let project = config.project().ok()?; - let cache = project.read_cache_file().ok()?; - let cached_artifacts = cache.read_artifacts::().ok()?; + fn build_using_cache( + &self, + etherscan_settings: &Metadata, + config: &Config, + ) -> Result { + let project = config.project()?; + let cache = project.read_cache_file()?; + let cached_artifacts = cache.read_artifacts::()?; for (key, value) in cached_artifacts { let name = self.contract.name.to_owned() + ".sol"; let version = etherscan_settings.compiler_version.to_owned(); // Ignores vyper if version.starts_with("vyper:") { - return None; + eyre::bail!("Vyper contracts are not supported") } // Parse etherscan version string let version = @@ -447,10 +459,8 @@ impl VerifyBytecodeArgs { // Check if `out/directory` name matches the contract name if key.ends_with(name.as_str()) { - let artifacts = - value.iter().flat_map(|(_, artifacts)| artifacts.iter()).collect::>(); let name = name.replace(".sol", ".json"); - for artifact in artifacts { + for artifact in value.into_values().flatten() { // Check if ABI file matches the name if !artifact.file.ends_with(&name) { continue; @@ -466,46 +476,34 @@ impl VerifyBytecodeArgs { } } - return artifact - .artifact - .bytecode - .as_ref() - .and_then(|bytes| bytes.bytes().to_owned()) - .cloned(); + return Ok(artifact.artifact) } - - return None } } - None + eyre::bail!("couldn't find cached artifact for contract {}", self.contract.name) } fn print_result( &self, - res: (bool, Option), + res: Option, bytecode_type: BytecodeType, json_results: &mut Vec, etherscan_config: &Metadata, config: &Config, ) { - if res.0 { + if let Some(res) = res { if !self.json { println!( "{} with status {}", format!("{bytecode_type:?} code matched").green().bold(), - res.1.unwrap().green().bold() + res.green().bold() ); } else { - let json_res = JsonResult { - bytecode_type, - matched: true, - verification_type: res.1.unwrap(), - message: None, - }; + let json_res = JsonResult { bytecode_type, match_type: Some(res), message: None }; json_results.push(json_res); } - } else if !res.0 && !self.json { + } else if !self.json { println!( "{}", format!( @@ -518,11 +516,10 @@ impl VerifyBytecodeArgs { for mismatch in mismatches { println!("{}", mismatch.red().bold()); } - } else if !res.0 && self.json { + } else { let json_res = JsonResult { bytecode_type, - matched: false, - verification_type: res.1.unwrap(), + match_type: res, message: Some(format!( "{bytecode_type:?} code did not match - this may be due to varying compiler settings" )), @@ -585,36 +582,34 @@ pub enum BytecodeType { #[derive(Debug, Serialize, Deserialize)] pub struct JsonResult { pub bytecode_type: BytecodeType, - pub matched: bool, - pub verification_type: VerificationType, + pub match_type: Option, #[serde(skip_serializing_if = "Option::is_none")] pub message: Option, } -fn try_match( +fn match_bytecodes( local_bytecode: &[u8], bytecode: &[u8], constructor_args: &[u8], - match_type: &VerificationType, is_runtime: bool, has_metadata: bool, -) -> Result<(bool, Option)> { +) -> Option { // 1. Try full match - if *match_type == VerificationType::Full && local_bytecode == bytecode { - Ok((true, Some(VerificationType::Full))) + if local_bytecode == bytecode { + Some(VerificationType::Full) } else { - try_partial_match(local_bytecode, bytecode, constructor_args, is_runtime, has_metadata) - .map(|matched| (matched, Some(VerificationType::Partial))) + is_partial_match(local_bytecode, bytecode, constructor_args, is_runtime, has_metadata) + .then_some(VerificationType::Partial) } } -fn try_partial_match( +fn is_partial_match( mut local_bytecode: &[u8], mut bytecode: &[u8], constructor_args: &[u8], is_runtime: bool, has_metadata: bool, -) -> Result { +) -> bool { // 1. Check length of constructor args if constructor_args.is_empty() || is_runtime { // Assume metadata is at the end of the bytecode @@ -632,24 +627,24 @@ fn try_extract_and_compare_bytecode( mut local_bytecode: &[u8], mut bytecode: &[u8], has_metadata: bool, -) -> Result { +) -> bool { if has_metadata { - local_bytecode = extract_metadata_hash(local_bytecode)?; - bytecode = extract_metadata_hash(bytecode)?; + local_bytecode = extract_metadata_hash(local_bytecode); + bytecode = extract_metadata_hash(bytecode); } // Now compare the local code and bytecode - Ok(local_bytecode == bytecode) + local_bytecode == bytecode } /// @dev This assumes that the metadata is at the end of the bytecode -fn extract_metadata_hash(bytecode: &[u8]) -> Result<&[u8]> { +fn extract_metadata_hash(bytecode: &[u8]) -> &[u8] { // Get the last two bytes of the bytecode to find the length of CBOR metadata let metadata_len = &bytecode[bytecode.len() - 2..]; let metadata_len = u16::from_be_bytes([metadata_len[0], metadata_len[1]]); // Now discard the metadata from the bytecode - Ok(&bytecode[..bytecode.len() - 2 - metadata_len as usize]) + &bytecode[..bytecode.len() - 2 - metadata_len as usize] } fn find_mismatch_in_settings( From 644bb31ec0972d67256441842201590598801bcf Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 25 Jul 2024 11:08:48 +0300 Subject: [PATCH 006/184] fix(coverage): proper instruction for 1st branch anchor (#8512) --- crates/evm/coverage/src/analysis.rs | 2 +- crates/evm/coverage/src/anchors.rs | 2 +- crates/forge/tests/cli/coverage.rs | 106 ++++++++++++++++++++++------ 3 files changed, 87 insertions(+), 23 deletions(-) diff --git a/crates/evm/coverage/src/analysis.rs b/crates/evm/coverage/src/analysis.rs index b110f6e69..3eb16332d 100644 --- a/crates/evm/coverage/src/analysis.rs +++ b/crates/evm/coverage/src/analysis.rs @@ -417,7 +417,7 @@ impl<'a> ContractVisitor<'a> { if let Some(NodeType::Identifier) = expr.as_ref().map(|expr| &expr.node_type) { // Might be a require call, add branch coverage. let name: Option = expr.and_then(|expr| expr.attribute("name")); - if let Some("require") = name.as_deref() { + if let Some("require" | "assert") = name.as_deref() { let branch_id = self.branch_id; self.branch_id += 1; self.push_item(CoverageItem { diff --git a/crates/evm/coverage/src/anchors.rs b/crates/evm/coverage/src/anchors.rs index 983cc77d4..ad4ad744d 100644 --- a/crates/evm/coverage/src/anchors.rs +++ b/crates/evm/coverage/src/anchors.rs @@ -149,7 +149,7 @@ pub fn find_anchor_branch( ItemAnchor { item_id, // The first branch is the opcode directly after JUMPI - instruction: pc + 1, + instruction: pc + 2, }, ItemAnchor { item_id, instruction: pc_jump }, )); diff --git a/crates/forge/tests/cli/coverage.rs b/crates/forge/tests/cli/coverage.rs index d33cbb47f..f9d591fe2 100644 --- a/crates/forge/tests/cli/coverage.rs +++ b/crates/forge/tests/cli/coverage.rs @@ -173,8 +173,8 @@ forgetest!(test_assert_coverage, |prj, cmd| { "AContract.sol", r#" contract AContract { - function checkA() external pure returns (bool) { - assert(10 > 2); + function checkA(uint256 a) external pure returns (bool) { + assert(a > 2); return true; } } @@ -188,26 +188,66 @@ contract AContract { import "./test.sol"; import {AContract} from "./AContract.sol"; +interface Vm { + function expectRevert() external; +} + contract AContractTest is DSTest { - function testA() external { + Vm constant vm = Vm(HEVM_ADDRESS); + function testAssertBranch() external { AContract a = new AContract(); - bool result = a.checkA(); + bool result = a.checkA(10); assertTrue(result); } + + function testAssertRevertBranch() external { + AContract a = new AContract(); + vm.expectRevert(); + a.checkA(1); + } } "#, ) .unwrap(); + // Assert 50% branch coverage for assert failure. + cmd.arg("coverage") + .args(["--mt".to_string(), "testAssertRevertBranch".to_string()]) + .assert_success() + .stdout_eq(str![[r#" +... +| File | % Lines | % Statements | % Branches | % Funcs | +|-------------------|--------------|--------------|--------------|---------------| +| src/AContract.sol | 50.00% (1/2) | 50.00% (1/2) | 50.00% (1/2) | 100.00% (1/1) | +| Total | 50.00% (1/2) | 50.00% (1/2) | 50.00% (1/2) | 100.00% (1/1) | + +"#]]); + + // Assert 50% branch coverage for proper assert. + cmd.forge_fuse() + .arg("coverage") + .args(["--mt".to_string(), "testAssertBranch".to_string()]) + .assert_success() + .stdout_eq(str![[r#" +... +| File | % Lines | % Statements | % Branches | % Funcs | +|-------------------|---------------|---------------|--------------|---------------| +| src/AContract.sol | 100.00% (2/2) | 100.00% (2/2) | 50.00% (1/2) | 100.00% (1/1) | +| Total | 100.00% (2/2) | 100.00% (2/2) | 50.00% (1/2) | 100.00% (1/1) | + +"#]]); + // Assert 100% coverage (assert properly covered). - cmd.arg("coverage").args(["--summary".to_string()]).assert_success().stdout_eq(str![[r#" + cmd.forge_fuse().arg("coverage").args(["--summary".to_string()]).assert_success().stdout_eq( + str![[r#" ... | File | % Lines | % Statements | % Branches | % Funcs | |-------------------|---------------|---------------|---------------|---------------| -| src/AContract.sol | 100.00% (2/2) | 100.00% (2/2) | 100.00% (0/0) | 100.00% (1/1) | -| Total | 100.00% (2/2) | 100.00% (2/2) | 100.00% (0/0) | 100.00% (1/1) | +| src/AContract.sol | 100.00% (2/2) | 100.00% (2/2) | 100.00% (2/2) | 100.00% (1/1) | +| Total | 100.00% (2/2) | 100.00% (2/2) | 100.00% (2/2) | 100.00% (1/1) | -"#]]); +"#]], + ); }); forgetest!(test_require_coverage, |prj, cmd| { @@ -262,6 +302,20 @@ contract AContractTest is DSTest { | src/AContract.sol | 100.00% (1/1) | 100.00% (1/1) | 50.00% (1/2) | 100.00% (1/1) | | Total | 100.00% (1/1) | 100.00% (1/1) | 50.00% (1/2) | 100.00% (1/1) | +"#]]); + + // Assert 50% branch coverage if only happy path tested. + cmd.forge_fuse() + .arg("coverage") + .args(["--mt".to_string(), "testRequireNoRevert".to_string()]) + .assert_success() + .stdout_eq(str![[r#" +... +| File | % Lines | % Statements | % Branches | % Funcs | +|-------------------|---------------|---------------|--------------|---------------| +| src/AContract.sol | 100.00% (1/1) | 100.00% (1/1) | 50.00% (1/2) | 100.00% (1/1) | +| Total | 100.00% (1/1) | 100.00% (1/1) | 50.00% (1/2) | 100.00% (1/1) | + "#]]); // Assert 100% branch coverage. @@ -384,7 +438,7 @@ contract Foo { function foo(uint256 a) external pure { if (a < 10) { - if (a == 1) { + if (a < 3) { assert(a == 1); } else { assert(a == 5); @@ -423,16 +477,23 @@ import {Foo} from "./Foo.sol"; interface Vm { function expectRevert(bytes calldata revertData) external; + function expectRevert() external; } contract FooTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); Foo internal foo = new Foo(); - function test_issue_7784() external view { + function test_issue_7784() external { foo.foo(1); + vm.expectRevert(); + foo.foo(2); + vm.expectRevert(); + foo.foo(4); foo.foo(5); foo.foo(60); + vm.expectRevert(); + foo.foo(70); } function test_issue_4310() external { @@ -506,13 +567,16 @@ contract FooTest is DSTest { ) .unwrap(); - // Assert 100% coverage. + // TODO: fix following issues for 100% coverage + // https://github.com/foundry-rs/foundry/issues/4309 + // https://github.com/foundry-rs/foundry/issues/4310 + // https://github.com/foundry-rs/foundry/issues/4315 cmd.arg("coverage").args(["--summary".to_string()]).assert_success().stdout_eq(str![[r#" ... -| File | % Lines | % Statements | % Branches | % Funcs | -|-------------|-----------------|-----------------|-----------------|---------------| -| src/Foo.sol | 100.00% (20/20) | 100.00% (23/23) | 100.00% (12/12) | 100.00% (7/7) | -| Total | 100.00% (20/20) | 100.00% (23/23) | 100.00% (12/12) | 100.00% (7/7) | +| File | % Lines | % Statements | % Branches | % Funcs | +|-------------|-----------------|-----------------|----------------|---------------| +| src/Foo.sol | 100.00% (20/20) | 100.00% (23/23) | 83.33% (15/18) | 100.00% (7/7) | +| Total | 100.00% (20/20) | 100.00% (23/23) | 83.33% (15/18) | 100.00% (7/7) | "#]]); }); @@ -681,10 +745,10 @@ contract FooTest is DSTest { cmd.arg("coverage").args(["--mt".to_string(), "happy".to_string()]).assert_success().stdout_eq( str![[r#" ... -| File | % Lines | % Statements | % Branches | % Funcs | -|-------------|----------------|----------------|---------------|---------------| -| src/Foo.sol | 66.67% (10/15) | 66.67% (14/21) | 100.00% (4/4) | 100.00% (5/5) | -| Total | 66.67% (10/15) | 66.67% (14/21) | 100.00% (4/4) | 100.00% (5/5) | +| File | % Lines | % Statements | % Branches | % Funcs | +|-------------|----------------|----------------|--------------|---------------| +| src/Foo.sol | 66.67% (10/15) | 66.67% (14/21) | 83.33% (5/6) | 100.00% (5/5) | +| Total | 66.67% (10/15) | 66.67% (14/21) | 83.33% (5/6) | 100.00% (5/5) | "#]], ); @@ -695,8 +759,8 @@ contract FooTest is DSTest { ... | File | % Lines | % Statements | % Branches | % Funcs | |-------------|-----------------|-----------------|---------------|---------------| -| src/Foo.sol | 100.00% (15/15) | 100.00% (21/21) | 100.00% (4/4) | 100.00% (5/5) | -| Total | 100.00% (15/15) | 100.00% (21/21) | 100.00% (4/4) | 100.00% (5/5) | +| src/Foo.sol | 100.00% (15/15) | 100.00% (21/21) | 100.00% (6/6) | 100.00% (5/5) | +| Total | 100.00% (15/15) | 100.00% (21/21) | 100.00% (6/6) | 100.00% (5/5) | "#]], ); From 4e4f35c1d536b4a371ddc5a666258c260fd31b53 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 25 Jul 2024 12:53:40 +0300 Subject: [PATCH 007/184] chore: fix flaky can_test_repeatedly test (#8519) --- crates/forge/tests/cli/test_cmd.rs | 53 ++++++++++++++----- .../tests/fixtures/can_test_repeatedly.stdout | 8 --- .../suggest_when_no_tests_match.stdout | 9 ---- .../forge/tests/fixtures/warn_no_tests.stdout | 1 - .../tests/fixtures/warn_no_tests_match.stdout | 7 --- crates/test-utils/src/util.rs | 6 +++ 6 files changed, 45 insertions(+), 39 deletions(-) delete mode 100644 crates/forge/tests/fixtures/can_test_repeatedly.stdout delete mode 100644 crates/forge/tests/fixtures/suggest_when_no_tests_match.stdout delete mode 100644 crates/forge/tests/fixtures/warn_no_tests.stdout delete mode 100644 crates/forge/tests/fixtures/warn_no_tests_match.stdout diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 23f77f56b..1f2e22a15 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -50,9 +50,11 @@ contract Dummy {} cmd.args(["test"]); // run command and assert - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/warn_no_tests.stdout"), - ); + cmd.assert_failure().stdout_eq(str![[r#" +... +No tests found in project! Forge looks for functions that starts with `test`. + +"#]]); }); // tests that warning is displayed with pattern when no tests match @@ -71,9 +73,17 @@ contract Dummy {} cmd.args(["--match-path", "*TestE*", "--no-match-path", "*TestF*"]); // run command and assert - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/warn_no_tests_match.stdout"), - ); + cmd.assert_failure().stdout_eq(str![[r#" +... +No tests match the provided pattern: + match-test: `testA.*` + no-match-test: `testB.*` + match-contract: `TestC.*` + no-match-contract: `TestD.*` + match-path: `*TestE*` + no-match-path: `*TestF*` + +"#]]); }); // tests that suggestion is provided with pattern when no tests match @@ -96,10 +106,19 @@ contract TestC { cmd.args(["--match-path", "*TestE*", "--no-match-path", "*TestF*"]); // run command and assert - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/suggest_when_no_tests_match.stdout"), - ); + cmd.assert_failure().stdout_eq(str![[r#" +... +No tests match the provided pattern: + match-test: `testA.*` + no-match-test: `testB.*` + match-contract: `TestC.*` + no-match-contract: `TestD.*` + match-path: `*TestE*` + no-match-path: `*TestF*` + +Did you mean `test1`? + +"#]]); }); // tests that direct import paths are handled correctly @@ -266,10 +285,16 @@ forgetest_init!(can_test_repeatedly, |_prj, cmd| { cmd.assert_non_empty_stdout(); for _ in 0..5 { - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/can_test_repeatedly.stdout"), - ); + cmd.assert_success().stdout_eq(str![[r#" +... +Ran 2 tests for test/Counter.t.sol:CounterTest +[PASS] testFuzz_SetNumber(uint256) (runs: 256, μ: [..], ~: [..]) +[PASS] test_Increment() (gas: 31303) +Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in [..] ([..] CPU time) + +Ran 1 test suite in [..] ([..] CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests) + +"#]]); } }); diff --git a/crates/forge/tests/fixtures/can_test_repeatedly.stdout b/crates/forge/tests/fixtures/can_test_repeatedly.stdout deleted file mode 100644 index 5a29d4dd7..000000000 --- a/crates/forge/tests/fixtures/can_test_repeatedly.stdout +++ /dev/null @@ -1,8 +0,0 @@ -No files changed, compilation skipped - -Ran 2 tests for test/Counter.t.sol:CounterTest -[PASS] testFuzz_SetNumber(uint256) (runs: 256, μ: 26521, ~: 28387) -[PASS] test_Increment() (gas: 31303) -Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 9.42ms - -Ran 1 test suite: 2 tests passed, 0 failed, 0 skipped (2 total tests) diff --git a/crates/forge/tests/fixtures/suggest_when_no_tests_match.stdout b/crates/forge/tests/fixtures/suggest_when_no_tests_match.stdout deleted file mode 100644 index 1cf6ad73f..000000000 --- a/crates/forge/tests/fixtures/suggest_when_no_tests_match.stdout +++ /dev/null @@ -1,9 +0,0 @@ -No tests match the provided pattern: - match-test: `testA.*` - no-match-test: `testB.*` - match-contract: `TestC.*` - no-match-contract: `TestD.*` - match-path: `*TestE*` - no-match-path: `*TestF*` - -Did you mean `test1`? diff --git a/crates/forge/tests/fixtures/warn_no_tests.stdout b/crates/forge/tests/fixtures/warn_no_tests.stdout deleted file mode 100644 index a9a7e7fc6..000000000 --- a/crates/forge/tests/fixtures/warn_no_tests.stdout +++ /dev/null @@ -1 +0,0 @@ -No tests found in project! Forge looks for functions that starts with `test`. diff --git a/crates/forge/tests/fixtures/warn_no_tests_match.stdout b/crates/forge/tests/fixtures/warn_no_tests_match.stdout deleted file mode 100644 index 4b4080f15..000000000 --- a/crates/forge/tests/fixtures/warn_no_tests_match.stdout +++ /dev/null @@ -1,7 +0,0 @@ -No tests match the provided pattern: - match-test: `testA.*` - no-match-test: `testB.*` - match-contract: `TestC.*` - no-match-contract: `TestD.*` - match-path: `*TestE*` - no-match-path: `*TestF*` diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index c1c2df0b0..9b6380e07 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -926,6 +926,12 @@ impl TestCommand { self.assert().success() } + /// Runs the command and asserts that it failed. + #[track_caller] + pub fn assert_failure(&mut self) -> OutputAssert { + self.assert().failure() + } + /// Executes command, applies stdin function and returns output #[track_caller] pub fn execute(&mut self) -> Output { From 4845380050c55929c0f9309e20552e1412c4a0d2 Mon Sep 17 00:00:00 2001 From: James Kim Date: Thu, 25 Jul 2024 11:41:16 -0400 Subject: [PATCH 008/184] fix(anvil): remove override for block.basefee when building transaction env (#8517) * remove override for block.basefee * disable basefee check for env * add tests * fix --- crates/anvil/src/eth/backend/mem/mod.rs | 9 ++-- crates/anvil/tests/it/api.rs | 65 +++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 429cefcae..804ef74f4 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -1152,9 +1152,12 @@ impl Backend { // impls and providers env.cfg.disable_block_gas_limit = true; - if let Some(base) = max_fee_per_gas { - env.block.basefee = U256::from(base); - } + // The basefee should be ignored for calls against state for + // - eth_call + // - eth_estimateGas + // - eth_createAccessList + // - tracing + env.cfg.disable_base_fee = true; let gas_price = gas_price.or(max_fee_per_gas).unwrap_or_else(|| { self.fees().raw_gas_price().saturating_add(MIN_SUGGESTED_PRIORITY_FEE) diff --git a/crates/anvil/tests/it/api.rs b/crates/anvil/tests/it/api.rs index 13f8200ef..9e640beb9 100644 --- a/crates/anvil/tests/it/api.rs +++ b/crates/anvil/tests/it/api.rs @@ -161,6 +161,39 @@ async fn can_get_pending_block() { assert_eq!(block.transactions.len(), 1); } +#[tokio::test(flavor = "multi_thread")] +async fn can_estimate_gas_with_undersized_max_fee_per_gas() { + let (api, handle) = spawn(NodeConfig::test()).await; + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumWallet = wallet.clone().into(); + + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); + + api.anvil_set_auto_mine(true).await.unwrap(); + + let init_value = "toto".to_string(); + + let simple_storage_contract = + SimpleStorage::deploy(&provider, init_value.clone()).await.unwrap(); + + let undersized_max_fee_per_gas = 1_u128; + + let latest_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); + let latest_block_base_fee_per_gas = latest_block.header.base_fee_per_gas.unwrap(); + + assert!(undersized_max_fee_per_gas < latest_block_base_fee_per_gas); + + let estimated_gas = simple_storage_contract + .setValue("new_value".to_string()) + .max_fee_per_gas(undersized_max_fee_per_gas) + .from(wallet.address()) + .estimate_gas() + .await + .unwrap(); + + assert!(estimated_gas > 0); +} + #[tokio::test(flavor = "multi_thread")] async fn can_call_on_pending_block() { let (api, handle) = spawn(NodeConfig::test()).await; @@ -239,6 +272,38 @@ async fn can_call_on_pending_block() { } } +#[tokio::test(flavor = "multi_thread")] +async fn can_call_with_undersized_max_fee_per_gas() { + let (api, handle) = spawn(NodeConfig::test()).await; + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumWallet = wallet.clone().into(); + + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); + + api.anvil_set_auto_mine(true).await.unwrap(); + + let init_value = "toto".to_string(); + + let simple_storage_contract = + SimpleStorage::deploy(&provider, init_value.clone()).await.unwrap(); + + let latest_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); + let latest_block_base_fee_per_gas = latest_block.header.base_fee_per_gas.unwrap(); + let undersized_max_fee_per_gas = 1_u128; + + assert!(undersized_max_fee_per_gas < latest_block_base_fee_per_gas); + + let last_sender = simple_storage_contract + .lastSender() + .max_fee_per_gas(undersized_max_fee_per_gas) + .from(wallet.address()) + .call() + .await + .unwrap() + ._0; + assert_eq!(last_sender, Address::ZERO); +} + #[tokio::test(flavor = "multi_thread")] async fn can_call_with_state_override() { let (api, handle) = spawn(NodeConfig::test()).await; From 9444c6217a258cb4fd6ac38597ea282d6642ec7c Mon Sep 17 00:00:00 2001 From: 0xKitsune <77890308+0xKitsune@users.noreply.github.com> Date: Thu, 25 Jul 2024 13:27:30 -0400 Subject: [PATCH 009/184] Update `ScriptArgs::preprocess()` visibility to `pub` (#8524) update preprocess visibility --- crates/script/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index af7000672..b6370b432 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -195,7 +195,7 @@ pub struct ScriptArgs { } impl ScriptArgs { - async fn preprocess(self) -> Result { + pub async fn preprocess(self) -> Result { let script_wallets = ScriptWallets::new(self.wallets.get_multi_wallet().await?, self.evm_opts.sender); From e606f5328ded4486029eff51362dc7a140f6e561 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Fri, 26 Jul 2024 17:01:06 +0800 Subject: [PATCH 010/184] fix: etherscan identifier for scripts (#8528) --- crates/script/src/execute.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index bfa9de0d2..8f92f01bd 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -332,14 +332,6 @@ impl ExecutedState { self.script_config.evm_opts.get_remote_chain_id().await, )?; - // Decoding traces using etherscan is costly as we run into rate limits, - // causing scripts to run for a very long time unnecessarily. - // Therefore, we only try and use etherscan if the user has provided an API key. - let should_use_etherscan_traces = self.script_config.config.etherscan_api_key.is_some(); - if !should_use_etherscan_traces { - identifier.etherscan = None; - } - for (_, trace) in &self.execution_result.traces { decoder.identify(trace, &mut identifier); } From 1f9b52c7ccc79728d6551ecbe67a3436e8b2ad2b Mon Sep 17 00:00:00 2001 From: teddav Date: Fri, 26 Jul 2024 11:29:13 +0200 Subject: [PATCH 011/184] feat: sendRawTransaction cheatcode (#4931) * feat: sendRawTransaction cheatcode * added unit tests * clippy + forge fmt * rebase * rename cheatcode to broadcastrawtransaction * revert anvil to sendrawtransaction + rename enum to Unsigned * better TransactionMaybeSigned * fix: ci * fixes * review fixes * add newline * Update crates/common/src/transactions.rs * Update crates/script/src/broadcast.rs * revm now uses Alloys AccessList: https://github.com/bluealloy/revm/pull/1552/files * only broadcast if you can transact, reorder cheatcode to be in broadcast section + document its behavior * update spec --------- Co-authored-by: Arsenii Kulikov Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Matthias Seitz --- Cargo.lock | 4 + crates/cheatcodes/Cargo.toml | 4 +- crates/cheatcodes/assets/cheatcodes.json | 20 + crates/cheatcodes/spec/src/vm.rs | 4 + crates/cheatcodes/src/evm.rs | 35 +- crates/cheatcodes/src/inspector.rs | 12 +- crates/common/Cargo.toml | 1 + crates/common/src/transactions.rs | 89 ++++- crates/evm/core/src/backend/cow.rs | 11 + crates/evm/core/src/backend/mod.rs | 61 ++- crates/script/Cargo.toml | 1 + crates/script/src/broadcast.rs | 128 ++++--- crates/script/src/build.rs | 2 +- crates/script/src/execute.rs | 4 +- crates/script/src/lib.rs | 6 +- crates/script/src/runner.rs | 6 +- crates/script/src/sequence.rs | 13 +- crates/script/src/simulate.rs | 97 ++--- crates/script/src/transaction.rs | 35 +- docs/dev/cheatcodes.md | 2 +- testdata/cheats/Vm.sol | 1 + .../cheats/BroadcastRawTransaction.t.sol | 346 ++++++++++++++++++ 22 files changed, 737 insertions(+), 145 deletions(-) create mode 100644 testdata/default/cheats/BroadcastRawTransaction.t.sol diff --git a/Cargo.lock b/Cargo.lock index a0797e8ad..dccf91eef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3444,6 +3444,7 @@ name = "forge-script" version = "0.2.0" dependencies = [ "alloy-chains", + "alloy-consensus", "alloy-dyn-abi", "alloy-eips", "alloy-json-abi", @@ -3563,11 +3564,13 @@ dependencies = [ name = "foundry-cheatcodes" version = "0.2.0" dependencies = [ + "alloy-consensus", "alloy-dyn-abi", "alloy-genesis", "alloy-json-abi", "alloy-primitives", "alloy-provider", + "alloy-rlp", "alloy-rpc-types", "alloy-signer", "alloy-signer-local", @@ -3649,6 +3652,7 @@ dependencies = [ name = "foundry-common" version = "0.2.0" dependencies = [ + "alloy-consensus", "alloy-contract", "alloy-dyn-abi", "alloy-json-abi", diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index 93636c3f2..60304a47d 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -28,13 +28,15 @@ alloy-primitives.workspace = true alloy-genesis.workspace = true alloy-sol-types.workspace = true alloy-provider.workspace = true -alloy-rpc-types.workspace = true +alloy-rpc-types = { workspace = true, features = ["k256"] } alloy-signer.workspace = true alloy-signer-local = { workspace = true, features = [ "mnemonic-all-languages", "keystore", ] } parking_lot.workspace = true +alloy-consensus = { workspace = true, features = ["k256"] } +alloy-rlp.workspace = true eyre.workspace = true itertools.workspace = true diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index a6a9c9479..ec12113eb 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -3051,6 +3051,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "broadcastRawTransaction", + "description": "Takes a signed transaction and broadcasts it to the network.", + "declaration": "function broadcastRawTransaction(bytes calldata data) external;", + "visibility": "external", + "mutability": "", + "signature": "broadcastRawTransaction(bytes)", + "selector": "0x8c0c72e0", + "selectorBytes": [ + 140, + 12, + 114, + 224 + ] + }, + "group": "scripting", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "broadcast_0", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index a009c9f59..ff9a92e58 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1759,6 +1759,10 @@ interface Vm { #[cheatcode(group = Scripting)] function stopBroadcast() external; + /// Takes a signed transaction and broadcasts it to the network. + #[cheatcode(group = Scripting)] + function broadcastRawTransaction(bytes calldata data) external; + // ======== Utilities ======== // -------- Strings -------- diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 33b064201..97923a948 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -1,8 +1,12 @@ //! Implementations of [`Evm`](spec::Group::Evm) cheatcodes. -use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Result, Vm::*}; +use crate::{ + BroadcastableTransaction, Cheatcode, Cheatcodes, CheatcodesExecutor, CheatsCtxt, Result, Vm::*, +}; +use alloy_consensus::TxEnvelope; use alloy_genesis::{Genesis, GenesisAccount}; use alloy_primitives::{Address, Bytes, B256, U256}; +use alloy_rlp::Decodable; use alloy_sol_types::SolValue; use foundry_common::fs::{read_json_file, write_json_file}; use foundry_evm_core::{ @@ -567,6 +571,35 @@ impl Cheatcode for stopAndReturnStateDiffCall { } } +impl Cheatcode for broadcastRawTransactionCall { + fn apply_full( + &self, + ccx: &mut CheatsCtxt, + executor: &mut E, + ) -> Result { + let mut data = self.data.as_ref(); + let tx = TxEnvelope::decode(&mut data).map_err(|err| { + fmt_err!("broadcastRawTransaction: error decoding transaction ({err})") + })?; + + ccx.ecx.db.transact_from_tx( + tx.clone().into(), + &ccx.ecx.env, + &mut ccx.ecx.journaled_state, + &mut executor.get_inspector(ccx.state), + )?; + + if ccx.state.broadcast.is_some() { + ccx.state.broadcastable_transactions.push_back(BroadcastableTransaction { + rpc: ccx.db.active_fork_url(), + transaction: tx.try_into()?, + }); + } + + Ok(Default::default()) + } +} + impl Cheatcode for setBlockhashCall { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { blockNumber, blockHash } = *self; diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index c989659e1..13cc02df3 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -19,7 +19,7 @@ use crate::{ use alloy_primitives::{hex, Address, Bytes, Log, TxKind, B256, U256}; use alloy_rpc_types::request::{TransactionInput, TransactionRequest}; use alloy_sol_types::{SolCall, SolInterface, SolValue}; -use foundry_common::{evm::Breakpoints, SELECTOR_LEN}; +use foundry_common::{evm::Breakpoints, TransactionMaybeSigned, SELECTOR_LEN}; use foundry_config::Config; use foundry_evm_core::{ abi::Vm::stopExpectSafeMemoryCall, @@ -188,12 +188,12 @@ impl Context { } /// Helps collecting transactions from different forks. -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] pub struct BroadcastableTransaction { /// The optional RPC URL. pub rpc: Option, /// The transaction to broadcast. - pub transaction: TransactionRequest, + pub transaction: TransactionMaybeSigned, } /// List of transactions that can be broadcasted. @@ -513,7 +513,8 @@ impl Cheatcodes { None }, ..Default::default() - }, + } + .into(), }); input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create)); @@ -849,7 +850,8 @@ impl Cheatcodes { None }, ..Default::default() - }, + } + .into(), }); debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call"); diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 094f32bed..5c843979a 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -41,6 +41,7 @@ alloy-transport-http = { workspace = true, features = [ alloy-transport-ipc.workspace = true alloy-transport-ws.workspace = true alloy-transport.workspace = true +alloy-consensus = { workspace = true, features = ["k256"] } tower.workspace = true diff --git a/crates/common/src/transactions.rs b/crates/common/src/transactions.rs index 2693c8ac2..c927d575d 100644 --- a/crates/common/src/transactions.rs +++ b/crates/common/src/transactions.rs @@ -1,7 +1,9 @@ //! Wrappers for transactions. +use alloy_consensus::{Transaction, TxEnvelope}; +use alloy_primitives::{Address, TxKind, U256}; use alloy_provider::{network::AnyNetwork, Provider}; -use alloy_rpc_types::{AnyTransactionReceipt, BlockId}; +use alloy_rpc_types::{AnyTransactionReceipt, BlockId, TransactionRequest}; use alloy_serde::WithOtherFields; use alloy_transport::Transport; use eyre::Result; @@ -144,3 +146,88 @@ mod tests { assert_eq!(extract_revert_reason(error_string_2), None); } } + +/// Used for broadcasting transactions +/// A transaction can either be a [`TransactionRequest`] waiting to be signed +/// or a [`TxEnvelope`], already signed +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum TransactionMaybeSigned { + Signed { + #[serde(flatten)] + tx: TxEnvelope, + from: Address, + }, + Unsigned(WithOtherFields), +} + +impl TransactionMaybeSigned { + /// Creates a new (unsigned) transaction for broadcast + pub fn new(tx: WithOtherFields) -> Self { + Self::Unsigned(tx) + } + + /// Creates a new signed transaction for broadcast. + pub fn new_signed( + tx: TxEnvelope, + ) -> core::result::Result { + let from = tx.recover_signer()?; + Ok(Self::Signed { tx, from }) + } + + pub fn as_unsigned_mut(&mut self) -> Option<&mut WithOtherFields> { + match self { + Self::Unsigned(tx) => Some(tx), + _ => None, + } + } + + pub fn from(&self) -> Option
{ + match self { + Self::Signed { from, .. } => Some(*from), + Self::Unsigned(tx) => tx.from, + } + } + + pub fn input(&self) -> Option<&[u8]> { + match self { + Self::Signed { tx, .. } => Some(tx.input()), + Self::Unsigned(tx) => tx.input.input().map(|i| i.as_ref()), + } + } + + pub fn to(&self) -> Option { + match self { + Self::Signed { tx, .. } => Some(tx.to()), + Self::Unsigned(tx) => tx.to, + } + } + + pub fn value(&self) -> Option { + match self { + Self::Signed { tx, .. } => Some(tx.value()), + Self::Unsigned(tx) => tx.value, + } + } + + pub fn gas(&self) -> Option { + match self { + Self::Signed { tx, .. } => Some(tx.gas_limit()), + Self::Unsigned(tx) => tx.gas, + } + } +} + +impl From for TransactionMaybeSigned { + fn from(tx: TransactionRequest) -> Self { + Self::new(WithOtherFields::new(tx)) + } +} + +impl TryFrom for TransactionMaybeSigned { + type Error = alloy_primitives::SignatureError; + + fn try_from(tx: TxEnvelope) -> core::result::Result { + Self::new_signed(tx) + } +} diff --git a/crates/evm/core/src/backend/cow.rs b/crates/evm/core/src/backend/cow.rs index 9ca63c513..2dcd985ae 100644 --- a/crates/evm/core/src/backend/cow.rs +++ b/crates/evm/core/src/backend/cow.rs @@ -10,6 +10,7 @@ use crate::{ }; use alloy_genesis::GenesisAccount; use alloy_primitives::{Address, B256, U256}; +use alloy_rpc_types::TransactionRequest; use eyre::WrapErr; use foundry_fork_db::DatabaseError; use revm::{ @@ -190,6 +191,16 @@ impl<'a> DatabaseExt for CowBackend<'a> { self.backend_mut(env).transact(id, transaction, env, journaled_state, inspector) } + fn transact_from_tx( + &mut self, + transaction: TransactionRequest, + env: &Env, + journaled_state: &mut JournaledState, + inspector: &mut dyn InspectorExt, + ) -> eyre::Result<()> { + self.backend_mut(env).transact_from_tx(transaction, env, journaled_state, inspector) + } + fn active_fork_id(&self) -> Option { self.backend.active_fork_id() } diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 23c54e6e2..7c2ed11c4 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -4,12 +4,14 @@ use crate::{ constants::{CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, TEST_CONTRACT_ADDRESS}, fork::{CreateFork, ForkId, MultiFork}, snapshot::Snapshots, - utils::configure_tx_env, + utils::{configure_tx_env, new_evm_with_inspector}, InspectorExt, }; use alloy_genesis::GenesisAccount; -use alloy_primitives::{keccak256, uint, Address, B256, U256}; -use alloy_rpc_types::{Block, BlockNumberOrTag, BlockTransactions, Transaction}; +use alloy_primitives::{keccak256, uint, Address, TxKind, B256, U256}; +use alloy_rpc_types::{ + Block, BlockNumberOrTag, BlockTransactions, Transaction, TransactionRequest, +}; use alloy_serde::WithOtherFields; use eyre::Context; use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE}; @@ -20,7 +22,7 @@ use revm::{ precompile::{PrecompileSpecId, Precompiles}, primitives::{ Account, AccountInfo, Bytecode, Env, EnvWithHandlerCfg, EvmState, EvmStorageSlot, - HashMap as Map, Log, ResultAndState, SpecId, TxKind, KECCAK_EMPTY, + HashMap as Map, Log, ResultAndState, SpecId, KECCAK_EMPTY, }, Database, DatabaseCommit, JournaledState, }; @@ -202,6 +204,15 @@ pub trait DatabaseExt: Database + DatabaseCommit { inspector: &mut dyn InspectorExt, ) -> eyre::Result<()>; + /// Executes a given TransactionRequest, commits the new state to the DB + fn transact_from_tx( + &mut self, + transaction: TransactionRequest, + env: &Env, + journaled_state: &mut JournaledState, + inspector: &mut dyn InspectorExt, + ) -> eyre::Result<()>; + /// Returns the `ForkId` that's currently used in the database, if fork mode is on fn active_fork_id(&self) -> Option; @@ -1246,6 +1257,48 @@ impl DatabaseExt for Backend { ) } + fn transact_from_tx( + &mut self, + tx: TransactionRequest, + env: &Env, + journaled_state: &mut JournaledState, + inspector: &mut dyn InspectorExt, + ) -> eyre::Result<()> { + trace!(?tx, "execute signed transaction"); + + let mut env = env.clone(); + + env.tx.caller = + tx.from.ok_or_else(|| eyre::eyre!("transact_from_tx: No `from` field found"))?; + env.tx.gas_limit = + tx.gas.ok_or_else(|| eyre::eyre!("transact_from_tx: No `gas` field found"))? as u64; + env.tx.gas_price = U256::from(tx.gas_price.or(tx.max_fee_per_gas).unwrap_or_default()); + env.tx.gas_priority_fee = tx.max_priority_fee_per_gas.map(U256::from); + env.tx.nonce = tx.nonce; + env.tx.access_list = tx.access_list.clone().unwrap_or_default().0.into_iter().collect(); + env.tx.value = + tx.value.ok_or_else(|| eyre::eyre!("transact_from_tx: No `value` field found"))?; + env.tx.data = tx.input.into_input().unwrap_or_default(); + env.tx.transact_to = + tx.to.ok_or_else(|| eyre::eyre!("transact_from_tx: No `to` field found"))?; + env.tx.chain_id = tx.chain_id; + + self.commit(journaled_state.state.clone()); + + let res = { + let db = self.clone(); + let env = self.env_with_handler_cfg(env); + let mut evm = new_evm_with_inspector(db, env, inspector); + evm.context.evm.journaled_state.depth = journaled_state.depth + 1; + evm.transact()? + }; + + self.commit(res.state); + update_state(&mut journaled_state.state, self, None)?; + + Ok(()) + } + fn active_fork_id(&self) -> Option { self.active_fork_ids.map(|(id, _)| id) } diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml index 3ef1f6b68..9c0426617 100644 --- a/crates/script/Cargo.toml +++ b/crates/script/Cargo.toml @@ -53,6 +53,7 @@ alloy-dyn-abi.workspace = true alloy-primitives.workspace = true alloy-eips.workspace = true alloy-transport.workspace = true +alloy-consensus.workspace = true [dev-dependencies] tempfile.workspace = true diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index b7eb9f5b0..a1113bb2d 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -3,6 +3,7 @@ use crate::{ verify::BroadcastedState, ScriptArgs, ScriptConfig, }; use alloy_chains::Chain; +use alloy_consensus::TxEnvelope; use alloy_eips::eip2718::Encodable2718; use alloy_network::{AnyNetwork, EthereumWallet, TransactionBuilder}; use alloy_primitives::{utils::format_units, Address, TxHash}; @@ -16,7 +17,7 @@ use foundry_cheatcodes::ScriptWallets; use foundry_cli::utils::{has_batch_support, has_different_gas_calc}; use foundry_common::{ provider::{get_http_provider, try_get_http_provider, RetryProvider}, - shell, + shell, TransactionMaybeSigned, }; use foundry_config::Config; use futures::{future::join_all, StreamExt}; @@ -55,45 +56,49 @@ pub async fn next_nonce(caller: Address, provider_url: &str) -> eyre::Result, - mut tx: WithOtherFields, - kind: SendTransactionKind<'_>, + mut kind: SendTransactionKind<'_>, sequential_broadcast: bool, is_fixed_gas_limit: bool, estimate_via_rpc: bool, estimate_multiplier: u64, ) -> Result { - let from = tx.from.expect("no sender"); + if let SendTransactionKind::Raw(tx, _) | SendTransactionKind::Unlocked(tx) = &mut kind { + if sequential_broadcast { + let from = tx.from.expect("no sender"); - if sequential_broadcast { - let nonce = provider.get_transaction_count(from).await?; + let nonce = provider.get_transaction_count(from).await?; - let tx_nonce = tx.nonce.expect("no nonce"); - if nonce != tx_nonce { - bail!("EOA nonce changed unexpectedly while sending transactions. Expected {tx_nonce} got {nonce} from provider.") + let tx_nonce = tx.nonce.expect("no nonce"); + if nonce != tx_nonce { + bail!("EOA nonce changed unexpectedly while sending transactions. Expected {tx_nonce} got {nonce} from provider.") + } } - } - // Chains which use `eth_estimateGas` are being sent sequentially and require their - // gas to be re-estimated right before broadcasting. - if !is_fixed_gas_limit && estimate_via_rpc { - estimate_gas(&mut tx, &provider, estimate_multiplier).await?; + // Chains which use `eth_estimateGas` are being sent sequentially and require their + // gas to be re-estimated right before broadcasting. + if !is_fixed_gas_limit && estimate_via_rpc { + estimate_gas(tx, &provider, estimate_multiplier).await?; + } } let pending = match kind { - SendTransactionKind::Unlocked(addr) => { - debug!("sending transaction from unlocked account {:?}: {:?}", addr, tx); + SendTransactionKind::Unlocked(tx) => { + debug!("sending transaction from unlocked account {:?}", tx); // Submit the transaction provider.send_transaction(tx).await? } - SendTransactionKind::Raw(signer) => { + SendTransactionKind::Raw(tx, signer) => { debug!("sending transaction: {:?}", tx); - let signed = tx.build(signer).await?; // Submit the raw transaction provider.send_raw_transaction(signed.encoded_2718().as_ref()).await? } + SendTransactionKind::Signed(tx) => { + debug!("sending transaction: {:?}", tx); + provider.send_raw_transaction(tx.encoded_2718().as_ref()).await? + } }; Ok(*pending.tx_hash()) @@ -102,8 +107,9 @@ pub async fn send_transaction( /// How to send a single transaction #[derive(Clone)] pub enum SendTransactionKind<'a> { - Unlocked(Address), - Raw(&'a EthereumWallet), + Unlocked(WithOtherFields), + Raw(WithOtherFields, &'a EthereumWallet), + Signed(TxEnvelope), } /// Represents how to send _all_ transactions @@ -118,31 +124,27 @@ impl SendTransactionsKind { /// Returns the [`SendTransactionKind`] for the given address /// /// Returns an error if no matching signer is found or the address is not unlocked - pub fn for_sender(&self, addr: &Address) -> Result> { + pub fn for_sender( + &self, + addr: &Address, + tx: WithOtherFields, + ) -> Result> { match self { Self::Unlocked(unlocked) => { if !unlocked.contains(addr) { bail!("Sender address {:?} is not unlocked", addr) } - Ok(SendTransactionKind::Unlocked(*addr)) + Ok(SendTransactionKind::Unlocked(tx)) } Self::Raw(wallets) => { if let Some(wallet) = wallets.get(addr) { - Ok(SendTransactionKind::Raw(wallet)) + Ok(SendTransactionKind::Raw(tx, wallet)) } else { bail!("No matching signer for {:?} found", addr) } } } } - - /// How many signers are set - pub fn signers_count(&self) -> usize { - match self { - Self::Unlocked(addr) => addr.len(), - Self::Raw(signers) => signers.len(), - } - } } /// State after we have bundled all @@ -189,11 +191,7 @@ impl BundledState { .sequence .sequences() .iter() - .flat_map(|sequence| { - sequence - .transactions() - .map(|tx| (tx.from().expect("No sender for onchain transaction!"))) - }) + .flat_map(|sequence| sequence.transactions().map(|tx| tx.from().expect("missing from"))) .collect::>(); if required_addresses.contains(&Config::DEFAULT_SENDER) { @@ -203,7 +201,7 @@ impl BundledState { } let send_kind = if self.args.unlocked { - SendTransactionsKind::Unlocked(required_addresses) + SendTransactionsKind::Unlocked(required_addresses.clone()) } else { let signers = self.script_wallets.into_multi_wallet().into_signers()?; let mut missing_addresses = Vec::new(); @@ -279,29 +277,38 @@ impl BundledState { .iter() .skip(already_broadcasted) .map(|tx_with_metadata| { - let tx = tx_with_metadata.tx(); - let from = tx.from().expect("No sender for onchain transaction!"); - - let kind = send_kind.for_sender(&from)?; let is_fixed_gas_limit = tx_with_metadata.is_fixed_gas_limit; - let mut tx = tx.clone(); - tx.set_chain_id(sequence.chain); - - // Set TxKind::Create explicityly to satify `check_reqd_fields` in alloy - if tx.to().is_none() { - tx.set_create(); - } - - if let Some(gas_price) = gas_price { - tx.set_gas_price(gas_price); - } else { - let eip1559_fees = eip1559_fees.expect("was set above"); - tx.set_max_priority_fee_per_gas(eip1559_fees.max_priority_fee_per_gas); - tx.set_max_fee_per_gas(eip1559_fees.max_fee_per_gas); - } - - Ok((tx, kind, is_fixed_gas_limit)) + let kind = match tx_with_metadata.tx().clone() { + TransactionMaybeSigned::Signed { tx, .. } => { + SendTransactionKind::Signed(tx) + } + TransactionMaybeSigned::Unsigned(mut tx) => { + let from = tx.from.expect("No sender for onchain transaction!"); + + tx.set_chain_id(sequence.chain); + + // Set TxKind::Create explicitly to satify `check_reqd_fields` in + // alloy + if tx.to.is_none() { + tx.set_create(); + } + + if let Some(gas_price) = gas_price { + tx.set_gas_price(gas_price); + } else { + let eip1559_fees = eip1559_fees.expect("was set above"); + tx.set_max_priority_fee_per_gas( + eip1559_fees.max_priority_fee_per_gas, + ); + tx.set_max_fee_per_gas(eip1559_fees.max_fee_per_gas); + } + + send_kind.for_sender(&from, tx)? + } + }; + + Ok((kind, is_fixed_gas_limit)) }) .collect::>>()?; @@ -315,7 +322,7 @@ impl BundledState { // Or if we need to invoke eth_estimateGas before sending transactions. let sequential_broadcast = estimate_via_rpc || self.args.slow || - send_kind.signers_count() != 1 || + required_addresses.len() != 1 || !has_batch_support(sequence.chain); // We send transactions and wait for receipts in batches. @@ -330,10 +337,9 @@ impl BundledState { batch_number * batch_size, batch_number * batch_size + std::cmp::min(batch_size, batch.len()) - 1 )); - for (tx, kind, is_fixed_gas_limit) in batch { + for (kind, is_fixed_gas_limit) in batch { let fut = send_transaction( provider.clone(), - tx.clone(), kind.clone(), sequential_broadcast, *is_fixed_gas_limit, diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index 14feb42ff..b0c5a2947 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -291,7 +291,7 @@ impl CompiledState { s.transactions .iter() .skip(s.receipts.len()) - .map(|t| t.transaction.from.expect("from is missing in script artifact")) + .map(|t| t.transaction.from().expect("from is missing in script artifact")) }); let available_signers = self diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index 8f92f01bd..1eff7e0de 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -189,8 +189,8 @@ impl PreExecutionState { self.args.evm_opts.sender.is_none() { for tx in txs.iter() { - if tx.transaction.to.is_none() { - let sender = tx.transaction.from.expect("no sender"); + if tx.transaction.to().is_none() { + let sender = tx.transaction.from().expect("no sender"); if let Some(ns) = new_sender { if sender != ns { shell::println("You have more than one deployer who could predeploy libraries. Using `--sender` instead.")?; diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index b6370b432..97a33541f 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -386,11 +386,9 @@ impl ScriptArgs { for (data, to) in result.transactions.iter().flat_map(|txes| { txes.iter().filter_map(|tx| { tx.transaction - .input - .clone() - .into_input() + .input() .filter(|data| data.len() > max_size) - .map(|data| (data, tx.transaction.to)) + .map(|data| (data, tx.transaction.to())) }) }) { let mut offset = 0; diff --git a/crates/script/src/runner.rs b/crates/script/src/runner.rs index a4f437644..7b7a2375b 100644 --- a/crates/script/src/runner.rs +++ b/crates/script/src/runner.rs @@ -78,7 +78,8 @@ impl ScriptRunner { input: Some(code.clone()).into(), nonce: Some(sender_nonce + library_transactions.len() as u64), ..Default::default() - }, + } + .into(), }) }), ScriptPredeployLibraries::Create2(libraries, salt) => { @@ -112,7 +113,8 @@ impl ScriptRunner { nonce: Some(sender_nonce + library_transactions.len() as u64), to: Some(TxKind::Call(DEFAULT_CREATE2_DEPLOYER)), ..Default::default() - }, + } + .into(), }); } diff --git a/crates/script/src/sequence.rs b/crates/script/src/sequence.rs index 5a8e789a6..53212f4eb 100644 --- a/crates/script/src/sequence.rs +++ b/crates/script/src/sequence.rs @@ -4,12 +4,11 @@ use crate::{ verify::VerifyBundle, }; use alloy_primitives::{hex, Address, TxHash}; -use alloy_rpc_types::{AnyTransactionReceipt, TransactionRequest}; -use alloy_serde::WithOtherFields; +use alloy_rpc_types::AnyTransactionReceipt; use eyre::{eyre, ContextCompat, Result, WrapErr}; use forge_verify::provider::VerificationProviderType; use foundry_cli::utils::{now, Git}; -use foundry_common::{fs, shell, SELECTOR_LEN}; +use foundry_common::{fs, shell, TransactionMaybeSigned, SELECTOR_LEN}; use foundry_compilers::ArtifactId; use foundry_config::Config; use serde::{Deserialize, Serialize}; @@ -284,10 +283,8 @@ impl ScriptSequence { } // Verify contract created directly from the transaction - if let (Some(address), Some(data)) = - (receipt.contract_address, tx.tx().input.input()) - { - match verify.get_verify_args(address, offset, &data.0, &self.libraries) { + if let (Some(address), Some(data)) = (receipt.contract_address, tx.tx().input()) { + match verify.get_verify_args(address, offset, data, &self.libraries) { Some(verify) => future_verifications.push(verify.run()), None => unverifiable_contracts.push(address), }; @@ -363,7 +360,7 @@ impl ScriptSequence { } /// Returns the list of the transactions without the metadata. - pub fn transactions(&self) -> impl Iterator> { + pub fn transactions(&self) -> impl Iterator { self.transactions.iter().map(|tx| tx.tx()) } diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 4a5f44117..077c507e2 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -13,7 +13,7 @@ use crate::{ ScriptArgs, ScriptConfig, ScriptResult, }; use alloy_network::TransactionBuilder; -use alloy_primitives::{utils::format_units, Address, TxKind, U256}; +use alloy_primitives::{utils::format_units, Address, Bytes, TxKind, U256}; use eyre::{Context, Result}; use foundry_cheatcodes::{BroadcastableTransactions, ScriptWallets}; use foundry_cli::utils::{has_different_gas_calc, now}; @@ -99,14 +99,14 @@ impl PreSimulationState { let mut runner = runners.get(&rpc).expect("invalid rpc url").write(); let mut tx = transaction.transaction; - let to = if let Some(TxKind::Call(to)) = tx.to { Some(to) } else { None }; + let to = if let Some(TxKind::Call(to)) = tx.to() { Some(to) } else { None }; let result = runner .simulate( - tx.from + tx.from() .expect("transaction doesn't have a `from` address at execution time"), to, - tx.input.clone().into_input(), - tx.value, + tx.input().map(Bytes::copy_from_slice), + tx.value(), ) .wrap_err("Internal EVM error during simulation")?; @@ -121,18 +121,25 @@ impl PreSimulationState { runner.executor.env_mut().block.number += U256::from(1); } - let is_fixed_gas_limit = tx.gas.is_some(); - match tx.gas { - // If tx.gas is already set that means it was specified in script - Some(gas) => { - println!("Gas limit was set in script to {gas}"); + let is_fixed_gas_limit = if let Some(tx) = tx.as_unsigned_mut() { + match tx.gas { + // If tx.gas is already set that means it was specified in script + Some(gas) => { + println!("Gas limit was set in script to {gas}"); + true + } + // We inflate the gas used by the user specified percentage + None => { + let gas = result.gas_used * self.args.gas_estimate_multiplier / 100; + tx.gas = Some(gas as u128); + false + } } - // We inflate the gas used by the user specified percentage - None => { - let gas = result.gas_used * self.args.gas_estimate_multiplier / 100; - tx.gas = Some(gas as u128); - } - } + } else { + // for pre-signed transactions we can't alter gas limit + true + }; + let tx = TransactionWithMetadata::new( tx, rpc, @@ -271,40 +278,48 @@ impl FilledTransactionsState { let tx_rpc = tx.rpc.clone(); let provider_info = manager.get_or_init_provider(&tx.rpc, self.args.legacy).await?; - // Handles chain specific requirements. - tx.transaction.set_chain_id(provider_info.chain); + if let Some(tx) = tx.transaction.as_unsigned_mut() { + // Handles chain specific requirements for unsigned transactions. + tx.set_chain_id(provider_info.chain); + } if !self.args.skip_simulation { let tx = tx.tx_mut(); if has_different_gas_calc(provider_info.chain) { - trace!("estimating with different gas calculation"); - let gas = tx.gas.expect("gas is set by simulation."); - - // We are trying to show the user an estimation of the total gas usage. - // - // However, some transactions might depend on previous ones. For - // example, tx1 might deploy a contract that tx2 uses. That - // will result in the following `estimate_gas` call to fail, - // since tx1 hasn't been broadcasted yet. - // - // Not exiting here will not be a problem when actually broadcasting, because - // for chains where `has_different_gas_calc` returns true, - // we await each transaction before broadcasting the next - // one. - if let Err(err) = - estimate_gas(tx, &provider_info.provider, self.args.gas_estimate_multiplier) - .await - { - trace!("gas estimation failed: {err}"); - - // Restore gas value, since `estimate_gas` will remove it. - tx.set_gas_limit(gas); + // only estimate gas for unsigned transactions + if let Some(tx) = tx.as_unsigned_mut() { + trace!("estimating with different gas calculation"); + let gas = tx.gas.expect("gas is set by simulation."); + + // We are trying to show the user an estimation of the total gas usage. + // + // However, some transactions might depend on previous ones. For + // example, tx1 might deploy a contract that tx2 uses. That + // will result in the following `estimate_gas` call to fail, + // since tx1 hasn't been broadcasted yet. + // + // Not exiting here will not be a problem when actually broadcasting, + // because for chains where `has_different_gas_calc` + // returns true, we await each transaction before + // broadcasting the next one. + if let Err(err) = estimate_gas( + tx, + &provider_info.provider, + self.args.gas_estimate_multiplier, + ) + .await + { + trace!("gas estimation failed: {err}"); + + // Restore gas value, since `estimate_gas` will remove it. + tx.set_gas_limit(gas); + } } } let total_gas = total_gas_per_rpc.entry(tx_rpc.clone()).or_insert(0); - *total_gas += tx.gas.expect("gas is set"); + *total_gas += tx.gas().expect("gas is set"); } new_sequence.push_back(tx); diff --git a/crates/script/src/transaction.rs b/crates/script/src/transaction.rs index de91ab3e3..0ef2559d9 100644 --- a/crates/script/src/transaction.rs +++ b/crates/script/src/transaction.rs @@ -1,10 +1,8 @@ use super::ScriptResult; use alloy_dyn_abi::JsonAbiExt; use alloy_primitives::{hex, Address, Bytes, TxKind, B256}; -use alloy_rpc_types::request::TransactionRequest; -use alloy_serde::WithOtherFields; use eyre::{ContextCompat, Result, WrapErr}; -use foundry_common::{fmt::format_token_raw, ContractData, SELECTOR_LEN}; +use foundry_common::{fmt::format_token_raw, ContractData, TransactionMaybeSigned, SELECTOR_LEN}; use foundry_evm::{constants::DEFAULT_CREATE2_DEPLOYER, traces::CallTraceDecoder}; use itertools::Itertools; use revm_inspectors::tracing::types::CallKind; @@ -20,7 +18,7 @@ pub struct AdditionalContract { pub init_code: Bytes, } -#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TransactionWithMetadata { pub hash: Option, @@ -36,7 +34,7 @@ pub struct TransactionWithMetadata { pub arguments: Option>, #[serde(skip)] pub rpc: String, - pub transaction: WithOtherFields, + pub transaction: TransactionMaybeSigned, pub additional_contracts: Vec, pub is_fixed_gas_limit: bool, } @@ -54,12 +52,23 @@ fn default_vec_of_strings() -> Option> { } impl TransactionWithMetadata { - pub fn from_tx_request(transaction: TransactionRequest) -> Self { - Self { transaction: WithOtherFields::new(transaction), ..Default::default() } + pub fn from_tx_request(transaction: TransactionMaybeSigned) -> Self { + Self { + transaction, + hash: Default::default(), + opcode: Default::default(), + contract_name: Default::default(), + contract_address: Default::default(), + function: Default::default(), + arguments: Default::default(), + is_fixed_gas_limit: Default::default(), + additional_contracts: Default::default(), + rpc: Default::default(), + } } pub fn new( - transaction: TransactionRequest, + transaction: TransactionMaybeSigned, rpc: String, result: &ScriptResult, local_contracts: &BTreeMap, @@ -72,7 +81,7 @@ impl TransactionWithMetadata { metadata.is_fixed_gas_limit = is_fixed_gas_limit; // Specify if any contract was directly created with this transaction - if let Some(TxKind::Call(to)) = metadata.transaction.to { + if let Some(TxKind::Call(to)) = metadata.transaction.to() { if to == DEFAULT_CREATE2_DEPLOYER { metadata.set_create( true, @@ -130,7 +139,7 @@ impl TransactionWithMetadata { self.contract_name = info.map(|info| info.name.clone()); self.contract_address = Some(address); - let Some(data) = self.transaction.input.input() else { return Ok(()) }; + let Some(data) = self.transaction.input() else { return Ok(()) }; let Some(info) = info else { return Ok(()) }; let Some(bytecode) = info.bytecode() else { return Ok(()) }; @@ -177,7 +186,7 @@ impl TransactionWithMetadata { self.opcode = CallKind::Call; self.contract_address = Some(target); - let Some(data) = self.transaction.input.input() else { return Ok(()) }; + let Some(data) = self.transaction.input() else { return Ok(()) }; if data.len() < SELECTOR_LEN { return Ok(()); } @@ -208,11 +217,11 @@ impl TransactionWithMetadata { Ok(()) } - pub fn tx(&self) -> &WithOtherFields { + pub fn tx(&self) -> &TransactionMaybeSigned { &self.transaction } - pub fn tx_mut(&mut self) -> &mut WithOtherFields { + pub fn tx_mut(&mut self) -> &mut TransactionMaybeSigned { &mut self.transaction } diff --git a/docs/dev/cheatcodes.md b/docs/dev/cheatcodes.md index ca4226be3..9ca0368d3 100644 --- a/docs/dev/cheatcodes.md +++ b/docs/dev/cheatcodes.md @@ -170,4 +170,4 @@ update of the files. [`cheatcodes/spec/src/vm.rs`]: ../../crates/cheatcodes/spec/src/vm.rs [`cheatcodes`]: ../../crates/cheatcodes/ [`spec::Cheatcodes::new`]: ../../crates/cheatcodes/spec/src/lib.rs#L74 -[`testdata/cheats/`]: ../../testdata/cheats/ +[`testdata/cheats/`]: ../../testdata/default/cheats/ diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 9c008c425..dfe466d3a 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -148,6 +148,7 @@ interface Vm { function blobhashes(bytes32[] calldata hashes) external; function breakpoint(string calldata char) external; function breakpoint(string calldata char, bool value) external; + function broadcastRawTransaction(bytes calldata data) external; function broadcast() external; function broadcast(address signer) external; function broadcast(uint256 privateKey) external; diff --git a/testdata/default/cheats/BroadcastRawTransaction.t.sol b/testdata/default/cheats/BroadcastRawTransaction.t.sol new file mode 100644 index 000000000..7425e9d37 --- /dev/null +++ b/testdata/default/cheats/BroadcastRawTransaction.t.sol @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract BroadcastRawTransactionTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function test_revert_not_a_tx() public { + vm.expectRevert("broadcastRawTransaction: error decoding transaction (unexpected string)"); + vm.broadcastRawTransaction(hex"0102"); + } + + function test_revert_missing_signature() public { + vm.expectRevert("broadcastRawTransaction: error decoding transaction (input too short)"); + vm.broadcastRawTransaction(hex"dd806483030d40940993863c19b0defb183ca2b502db7d1b331ded757b80"); + } + + function test_revert_wrong_chainid() public { + vm.expectRevert("transaction validation error: invalid chain ID"); + vm.broadcastRawTransaction( + hex"f860806483030d40946fd0a0cff9a87adf51695b40b4fa267855a8f4c6118025a03ebeabbcfe43c2c982e99b376b5fb6e765059d7f215533c8751218cac99bbd80a00a56cf5c382442466770a756e81272d06005c9e90fb8dbc5b53af499d5aca856" + ); + } + + function test_execute_signed_tx() public { + vm.fee(1); + vm.chainId(1); + + address from = 0x5316812db67073C4d4af8BB3000C5B86c2877e94; + address to = 0x6Fd0A0CFF9A87aDF51695b40b4fA267855a8F4c6; + + uint256 balance = 1 ether; + uint256 amountSent = 17; + + vm.deal(address(from), balance); + assertEq(address(from).balance, balance); + assertEq(address(to).balance, 0); + + /* + Signed transaction: + TransactionRequest { from: Some(0x5316812db67073c4d4af8bb3000c5b86c2877e94), to: Some(Address(0x6fd0a0cff9a87adf51695b40b4fa267855a8f4c6)), gas: Some(200000), gas_price: Some(100), value: Some(17), data: None, nonce: Some(0), chain_id: Some(1) } + */ + vm.broadcastRawTransaction( + hex"f860806483030d40946fd0a0cff9a87adf51695b40b4fa267855a8f4c6118025a03ebeabbcfe43c2c982e99b376b5fb6e765059d7f215533c8751218cac99bbd80a00a56cf5c382442466770a756e81272d06005c9e90fb8dbc5b53af499d5aca856" + ); + + uint256 gasPrice = 100; + assertEq(address(from).balance, balance - (gasPrice * 21_000) - amountSent); + assertEq(address(to).balance, amountSent); + } + + function test_execute_signed_tx2() public { + vm.fee(1); + vm.chainId(1); + + address from = 0x5316812db67073C4d4af8BB3000C5B86c2877e94; + address to = 0x6Fd0A0CFF9A87aDF51695b40b4fA267855a8F4c6; + address random = address(uint160(uint256(keccak256(abi.encodePacked("random"))))); + + uint256 balance = 1 ether; + uint256 amountSent = 17; + + vm.deal(address(from), balance); + assertEq(address(from).balance, balance); + assertEq(address(to).balance, 0); + + /* + Signed transaction: + TransactionRequest { from: Some(0x5316812db67073c4d4af8bb3000c5b86c2877e94), to: Some(Address(0x6fd0a0cff9a87adf51695b40b4fa267855a8f4c6)), gas: Some(200000), gas_price: Some(100), value: Some(17), data: None, nonce: Some(0), chain_id: Some(1) } + */ + vm.broadcastRawTransaction( + hex"f860806483030d40946fd0a0cff9a87adf51695b40b4fa267855a8f4c6118025a03ebeabbcfe43c2c982e99b376b5fb6e765059d7f215533c8751218cac99bbd80a00a56cf5c382442466770a756e81272d06005c9e90fb8dbc5b53af499d5aca856" + ); + + uint256 gasPrice = 100; + assertEq(address(from).balance, balance - (gasPrice * 21_000) - amountSent); + assertEq(address(to).balance, amountSent); + assertEq(address(random).balance, 0); + + uint256 value = 5; + + vm.prank(to); + (bool success,) = random.call{value: value}(""); + require(success); + assertEq(address(to).balance, amountSent - value); + assertEq(address(random).balance, value); + } + + // this test is to make sure that the journaledstate is correctly handled + // i ran into an issue where the test would fail after running `broadcastRawTransaction` + // because there was an issue in the journaledstate + function test_execute_signed_tx_with_revert() public { + vm.fee(1); + vm.chainId(1); + + address from = 0x5316812db67073C4d4af8BB3000C5B86c2877e94; + address to = 0x6Fd0A0CFF9A87aDF51695b40b4fA267855a8F4c6; + + uint256 balance = 1 ether; + uint256 amountSent = 17; + + vm.deal(address(from), balance); + assertEq(address(from).balance, balance); + assertEq(address(to).balance, 0); + + /* + Signed transaction: + TransactionRequest { from: Some(0x5316812db67073c4d4af8bb3000c5b86c2877e94), to: Some(Address(0x6fd0a0cff9a87adf51695b40b4fa267855a8f4c6)), gas: Some(200000), gas_price: Some(100), value: Some(17), data: None, nonce: Some(0), chain_id: Some(1) } + */ + vm.broadcastRawTransaction( + hex"f860806483030d40946fd0a0cff9a87adf51695b40b4fa267855a8f4c6118025a03ebeabbcfe43c2c982e99b376b5fb6e765059d7f215533c8751218cac99bbd80a00a56cf5c382442466770a756e81272d06005c9e90fb8dbc5b53af499d5aca856" + ); + + uint256 gasPrice = 100; + assertEq(address(from).balance, balance - (gasPrice * 21_000) - amountSent); + assertEq(address(to).balance, amountSent); + + vm.expectRevert(); + assert(3 == 4); + } + + function test_execute_multiple_signed_tx() public { + vm.fee(1); + vm.chainId(1); + + address alice = 0x7ED31830602f9F7419307235c0610Fb262AA0375; + address bob = 0x70CF146aB98ffD5dE24e75dd7423F16181Da8E13; + address charlie = 0xae0900Cf97f8C233c64F7089cEC7d5457215BB8d; + + // this is the runtime code of "MyERC20" (see below) + // this is equivalent to: + // type(MyERC20).runtimeCode + bytes memory code = + hex"608060405234801561001057600080fd5b50600436106100625760003560e01c8063095ea7b31461006757806323b872dd1461008f57806370a08231146100a257806394bf804d146100d9578063a9059cbb146100ee578063dd62ed3e14610101575b600080fd5b61007a61007536600461051d565b61013a565b60405190151581526020015b60405180910390f35b61007a61009d366004610547565b610152565b6100cb6100b0366004610583565b6001600160a01b031660009081526020819052604090205490565b604051908152602001610086565b6100ec6100e73660046105a5565b610176565b005b61007a6100fc36600461051d565b610184565b6100cb61010f3660046105d1565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b600033610148818585610192565b5060019392505050565b600033610160858285610286565b61016b858585610318565b506001949350505050565b6101808183610489565b5050565b600033610148818585610318565b6001600160a01b0383166101f95760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b60648201526084015b60405180910390fd5b6001600160a01b03821661025a5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b60648201526084016101f0565b6001600160a01b0392831660009081526001602090815260408083209490951682529290925291902055565b6001600160a01b03838116600090815260016020908152604080832093861683529290522054600019811461031257818110156103055760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e636500000060448201526064016101f0565b6103128484848403610192565b50505050565b6001600160a01b03831661037c5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b60648201526084016101f0565b6001600160a01b0382166103de5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b60648201526084016101f0565b6001600160a01b038316600090815260208190526040902054818110156104565760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b60648201526084016101f0565b6001600160a01b039384166000908152602081905260408082209284900390925592909316825291902080549091019055565b6001600160a01b0382166104df5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016101f0565b6001600160a01b03909116600090815260208190526040902080549091019055565b80356001600160a01b038116811461051857600080fd5b919050565b6000806040838503121561053057600080fd5b61053983610501565b946020939093013593505050565b60008060006060848603121561055c57600080fd5b61056584610501565b925061057360208501610501565b9150604084013590509250925092565b60006020828403121561059557600080fd5b61059e82610501565b9392505050565b600080604083850312156105b857600080fd5b823591506105c860208401610501565b90509250929050565b600080604083850312156105e457600080fd5b6105ed83610501565b91506105c86020840161050156fea2646970667358221220e1fee5cd1c5bbf066a9ce9228e1baf7e7fcb77b5050506c7d614aaf8608b42e364736f6c63430008110033"; + + // this is equivalent to: + // MyERC20 token = new MyERC20{ salt: bytes32(uint256(1)) }(); + // address: 0x5bf11839f61ef5cceeaf1f4153e44df5d02825f7 + MyERC20 token = MyERC20(address(uint160(uint256(keccak256(abi.encodePacked("mytoken")))))); + vm.etch(address(token), code); + + token.mint(100, alice); + + assertEq(token.balanceOf(alice), 100); + assertEq(token.balanceOf(bob), 0); + assertEq(token.balanceOf(charlie), 0); + + vm.deal(alice, 10 ether); + + /* + Signed transaction: + { + from: '0x7ED31830602f9F7419307235c0610Fb262AA0375', + to: '0x5bF11839F61EF5ccEEaf1F4153e44df5D02825f7', + value: 0, + data: '0x095ea7b300000000000000000000000070cf146ab98ffd5de24e75dd7423f16181da8e130000000000000000000000000000000000000000000000000000000000000032', + nonce: 0, + gasPrice: 100, + gasLimit: 200000, + chainId: 1 + } + */ + // this would be equivalent to using those cheatcodes: + // vm.prank(alice); + // token.approve(bob, 50); + vm.broadcastRawTransaction( + hex"f8a5806483030d40945bf11839f61ef5cceeaf1f4153e44df5d02825f780b844095ea7b300000000000000000000000070cf146ab98ffd5de24e75dd7423f16181da8e13000000000000000000000000000000000000000000000000000000000000003225a0e25b9ef561d9a413b21755cc0e4bb6e80f2a88a8a52305690956130d612074dfa07bfd418bc2ad3c3f435fa531cdcdc64887f64ed3fb0d347d6b0086e320ad4eb1" + ); + + assertEq(token.allowance(alice, bob), 50); + + vm.deal(bob, 1 ether); + vm.prank(bob); + token.transferFrom(alice, charlie, 20); + + assertEq(token.balanceOf(bob), 0); + assertEq(token.balanceOf(charlie), 20); + + vm.deal(charlie, 1 ether); + + /* + Signed transaction: + { + from: '0xae0900Cf97f8C233c64F7089cEC7d5457215BB8d', + to: '0x5bF11839F61EF5ccEEaf1F4153e44df5D02825f7', + value: 0, + data: '0xa9059cbb00000000000000000000000070cf146ab98ffd5de24e75dd7423f16181da8e130000000000000000000000000000000000000000000000000000000000000005', + nonce: 0, + gasPrice: 100, + gasLimit: 200000, + chainId: 1 + } + */ + // this would be equivalent to using those cheatcodes: + // vm.prank(charlie); + // token.transfer(bob, 5); + vm.broadcastRawTransaction( + hex"f8a5806483030d40945bf11839f61ef5cceeaf1f4153e44df5d02825f780b844a9059cbb00000000000000000000000070cf146ab98ffd5de24e75dd7423f16181da8e13000000000000000000000000000000000000000000000000000000000000000525a0941562f519e33dfe5b44ebc2b799686cebeaeacd617dd89e393620b380797da2a0447dfd38d9444ccd571b000482c81674733761753430c81ee6669e9542c266a1" + ); + + assertEq(token.balanceOf(alice), 80); + assertEq(token.balanceOf(bob), 5); + assertEq(token.balanceOf(charlie), 15); + } +} + +contract MyERC20 { + mapping(address => uint256) private _balances; + mapping(address => mapping(address => uint256)) private _allowances; + + function mint(uint256 amount, address to) public { + _mint(to, amount); + } + + function balanceOf(address account) public view returns (uint256) { + return _balances[account]; + } + + function transfer(address to, uint256 amount) public returns (bool) { + address owner = msg.sender; + _transfer(owner, to, amount); + return true; + } + + function allowance(address owner, address spender) public view returns (uint256) { + return _allowances[owner][spender]; + } + + function approve(address spender, uint256 amount) public returns (bool) { + address owner = msg.sender; + _approve(owner, spender, amount); + return true; + } + + function transferFrom(address from, address to, uint256 amount) public returns (bool) { + address spender = msg.sender; + _spendAllowance(from, spender, amount); + _transfer(from, to, amount); + return true; + } + + function _transfer(address from, address to, uint256 amount) internal { + require(from != address(0), "ERC20: transfer from the zero address"); + require(to != address(0), "ERC20: transfer to the zero address"); + + uint256 fromBalance = _balances[from]; + require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); + unchecked { + _balances[from] = fromBalance - amount; + _balances[to] += amount; + } + } + + function _mint(address account, uint256 amount) internal { + require(account != address(0), "ERC20: mint to the zero address"); + unchecked { + _balances[account] += amount; + } + } + + function _approve(address owner, address spender, uint256 amount) internal { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + _allowances[owner][spender] = amount; + } + + function _spendAllowance(address owner, address spender, uint256 amount) internal { + uint256 currentAllowance = allowance(owner, spender); + if (currentAllowance != type(uint256).max) { + require(currentAllowance >= amount, "ERC20: insufficient allowance"); + unchecked { + _approve(owner, spender, currentAllowance - amount); + } + } + } +} + +contract ScriptBroadcastRawTransactionBroadcast is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function runSignedTxBroadcast() public { + uint256 pk_to = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80; + vm.startBroadcast(pk_to); + + address from = 0x73E1A965542AFA4B412467761b1CED8A764E1D3B; + address to = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266; + address random = address(uint160(uint256(keccak256(abi.encodePacked("random"))))); + + assert(address(from).balance == 1 ether); + assert(address(to).balance == 1 ether); + assert(address(random).balance == 0); + + /* + TransactionRequest { + from: Some( + 0x73e1a965542afa4b412467761b1ced8a764e1d3b, + ), + to: Some( + Address( + 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266, + ), + ), + gas: Some( + 200000, + ), + gas_price: Some( + 10000000000, + ), + value: Some( + 1234, + ), + data: None, + nonce: Some( + 0, + ), + chain_id: Some( + 31337, + ), + } + */ + vm.broadcastRawTransaction( + hex"f869808502540be40083030d4094f39fd6e51aad88f6f4ce6ab8827279cfffb922668204d28082f4f6a061ce3c0f4280cb79c1eb0060a9a491cca1ba48ed32f141e3421ccb60c9dbe444a07fcd35cbec5f81427ac20f60484f4da9d00f59652f5053cd13ee90b992e94ab3" + ); + + uint256 value = 34; + (bool success,) = random.call{value: value}(""); + require(success); + + vm.stopBroadcast(); + + uint256 gasPrice = 10 * 1e9; + assert(address(from).balance == 1 ether - (gasPrice * 21_000) - 1234); + assert(address(to).balance == 1 ether + 1234 - value); + assert(address(random).balance == value); + } + + function runDeployCreate2Deployer() public { + vm.startBroadcast(); + vm.broadcastRawTransaction( + hex"f8a58085174876e800830186a08080b853604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf31ba02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222" + ); + vm.stopBroadcast(); + } +} From cc88da946ddd2aa04ce6eb902b5cec1fb0401edd Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 26 Jul 2024 11:53:11 +0200 Subject: [PATCH 012/184] ci: fix conditional release features (#8529) --- .github/workflows/release.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c993c0d8d..35ca11d93 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -133,18 +133,16 @@ jobs: run: | set -eo pipefail target="${{ matrix.target }}" - flags=() + flags=(--release --bins --no-default-features --features rustls,aws-kms) # `jemalloc` and `keccak-asm` are not supported on MSVC or aarch64 Linux. if [[ "$target" != *msvc* && "$target" != "aarch64-unknown-linux-gnu" ]]; then - flags+=(--features asm-keccak,jemalloc,cast/aws-kms,forge/aws-kms) - else - flags+=(--features cast/aws-kms,forge/aws-kms) + flags+=(--features asm-keccak,jemalloc) fi [[ "$target" == *windows* ]] && exe=".exe" - cargo build --release --bins --target "$target" "${flags[@]}" + cargo build --target "$target" "${flags[@]}" bins=(anvil cast chisel forge) for name in "${bins[@]}"; do From 4a41367398ddc35faf705f5d93e7e1a4eae3884d Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 26 Jul 2024 13:26:21 +0300 Subject: [PATCH 013/184] feat(test): allow custom txes before unit and fuzz test (#8497) * feat(test): allow performing txes before unit test * Changes after review: - do not unwrap func - check if `beforeTestSelectors` exists - move logic in prepare_unit_test fn - apply same logic to fuzz tests * Review: Before test is not a test kind * Changes after review: beforeTestSetup new fn signature * Remove obsolete struct from test * Update crates/forge/src/runner.rs Co-authored-by: Matthias Seitz * Changes after review: avoid executor clone * Fix Cow::Borrowed usage --------- Co-authored-by: Matthias Seitz --- crates/common/src/traits.rs | 5 + crates/evm/evm/src/executors/invariant/mod.rs | 53 +++++----- crates/evm/evm/src/executors/mod.rs | 13 +++ crates/forge/src/result.rs | 12 ++- crates/forge/src/runner.rs | 98 ++++++++++++++++--- crates/forge/tests/it/repros.rs | 3 + testdata/default/repros/Issue1543.t.sol | 91 +++++++++++++++++ 7 files changed, 232 insertions(+), 43 deletions(-) create mode 100644 testdata/default/repros/Issue1543.t.sol diff --git a/crates/common/src/traits.rs b/crates/common/src/traits.rs index 606b4861a..f5f3ea14c 100644 --- a/crates/common/src/traits.rs +++ b/crates/common/src/traits.rs @@ -44,6 +44,11 @@ pub trait TestFunctionExt { matches!(self.test_function_kind(), TestFunctionKind::UnitTest { .. }) } + /// Returns `true` if this function is a `beforeTestSetup` function. + fn is_before_test_setup(&self) -> bool { + self.tfe_as_str().eq_ignore_ascii_case("beforetestsetup") + } + /// Returns `true` if this function is a fuzz test. fn is_fuzz_test(&self) -> bool { self.test_function_kind().is_fuzz_test() diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 7dcdc1568..91143f4b2 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -532,6 +532,7 @@ impl<'a> InvariantExecutor<'a> { /// targetArtifactSelectors > excludeArtifacts > targetArtifacts pub fn select_contract_artifacts(&mut self, invariant_address: Address) -> Result<()> { let result = self + .executor .call_sol_default(invariant_address, &IInvariantTest::targetArtifactSelectorsCall {}); // Insert them into the executor `targeted_abi`. @@ -542,10 +543,12 @@ impl<'a> InvariantExecutor<'a> { self.artifact_filters.targeted.entry(identifier).or_default().extend(selectors); } - let selected = - self.call_sol_default(invariant_address, &IInvariantTest::targetArtifactsCall {}); - let excluded = - self.call_sol_default(invariant_address, &IInvariantTest::excludeArtifactsCall {}); + let selected = self + .executor + .call_sol_default(invariant_address, &IInvariantTest::targetArtifactsCall {}); + let excluded = self + .executor + .call_sol_default(invariant_address, &IInvariantTest::excludeArtifactsCall {}); // Insert `excludeArtifacts` into the executor `excluded_abi`. for contract in excluded.excludedArtifacts { @@ -620,10 +623,14 @@ impl<'a> InvariantExecutor<'a> { &self, to: Address, ) -> Result<(SenderFilters, FuzzRunIdentifiedContracts)> { - let targeted_senders = - self.call_sol_default(to, &IInvariantTest::targetSendersCall {}).targetedSenders; - let mut excluded_senders = - self.call_sol_default(to, &IInvariantTest::excludeSendersCall {}).excludedSenders; + let targeted_senders = self + .executor + .call_sol_default(to, &IInvariantTest::targetSendersCall {}) + .targetedSenders; + let mut excluded_senders = self + .executor + .call_sol_default(to, &IInvariantTest::excludeSendersCall {}) + .excludedSenders; // Extend with default excluded addresses - https://github.com/foundry-rs/foundry/issues/4163 excluded_senders.extend([ CHEATCODE_ADDRESS, @@ -634,10 +641,14 @@ impl<'a> InvariantExecutor<'a> { excluded_senders.extend(PRECOMPILES); let sender_filters = SenderFilters::new(targeted_senders, excluded_senders); - let selected = - self.call_sol_default(to, &IInvariantTest::targetContractsCall {}).targetedContracts; - let excluded = - self.call_sol_default(to, &IInvariantTest::excludeContractsCall {}).excludedContracts; + let selected = self + .executor + .call_sol_default(to, &IInvariantTest::targetContractsCall {}) + .targetedContracts; + let excluded = self + .executor + .call_sol_default(to, &IInvariantTest::excludeContractsCall {}) + .excludedContracts; let contracts = self .setup_contracts @@ -678,6 +689,7 @@ impl<'a> InvariantExecutor<'a> { targeted_contracts: &mut TargetedContracts, ) -> Result<()> { let interfaces = self + .executor .call_sol_default(invariant_address, &IInvariantTest::targetInterfacesCall {}) .targetedInterfaces; @@ -735,13 +747,15 @@ impl<'a> InvariantExecutor<'a> { } // Collect contract functions marked as target for fuzzing campaign. - let selectors = self.call_sol_default(address, &IInvariantTest::targetSelectorsCall {}); + let selectors = + self.executor.call_sol_default(address, &IInvariantTest::targetSelectorsCall {}); for IInvariantTest::FuzzSelector { addr, selectors } in selectors.targetedSelectors { self.add_address_with_functions(addr, &selectors, false, targeted_contracts)?; } // Collect contract functions excluded from fuzzing campaign. - let selectors = self.call_sol_default(address, &IInvariantTest::excludeSelectorsCall {}); + let selectors = + self.executor.call_sol_default(address, &IInvariantTest::excludeSelectorsCall {}); for IInvariantTest::FuzzSelector { addr, selectors } in selectors.excludedSelectors { self.add_address_with_functions(addr, &selectors, true, targeted_contracts)?; } @@ -773,17 +787,6 @@ impl<'a> InvariantExecutor<'a> { contract.add_selectors(selectors.iter().copied(), should_exclude)?; Ok(()) } - - fn call_sol_default(&self, to: Address, args: &C) -> C::Return - where - C::Return: Default, - { - self.executor - .call_sol(CALLER, to, args, U256::ZERO, None) - .map(|c| c.decoded_result) - .inspect_err(|e| warn!(target: "forge::test", "failed calling {:?}: {e}", C::SIGNATURE)) - .unwrap_or_default() - } } /// Collects data from call for fuzzing. However, it first verifies that the sender is not an EOA diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 66010a33c..2f08b2f42 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -50,6 +50,9 @@ sol! { interface ITest { function setUp() external; function failed() external view returns (bool failed); + + #[derive(Default)] + function beforeTestSetup(bytes4 testSelector) public view returns (bytes[] memory beforeTestCalldata); } } @@ -602,6 +605,16 @@ impl Executor { EnvWithHandlerCfg::new_with_spec_id(Box::new(env), self.spec_id()) } + + pub fn call_sol_default(&self, to: Address, args: &C) -> C::Return + where + C::Return: Default, + { + self.call_sol(CALLER, to, args, U256::ZERO, None) + .map(|c| c.decoded_result) + .inspect_err(|e| warn!(target: "forge::test", "failed calling {:?}: {e}", C::SIGNATURE)) + .unwrap_or_default() + } } /// Represents the context after an execution error occurred. diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index 8352432e4..e7953575f 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -449,9 +449,9 @@ impl TestResult { } /// Returns the failed result with reason for single test. - pub fn single_fail(mut self, err: EvmError) -> Self { + pub fn single_fail(mut self, reason: Option) -> Self { self.status = TestStatus::Failure; - self.reason = Some(err.to_string()); + self.reason = reason; self } @@ -579,6 +579,14 @@ impl TestResult { format!("{self} {name} {}", self.kind.report()) } + /// Function to merge logs, addresses, traces and coverage from a call result into test result. + pub fn merge_call_result(&mut self, call_result: &RawCallResult) { + self.logs.extend(call_result.logs.clone()); + self.labeled_addresses.extend(call_result.labels.clone()); + self.traces.extend(call_result.traces.clone().map(|traces| (TraceKind::Execution, traces))); + self.merge_coverages(call_result.coverage.clone()); + } + /// Function to merge given coverage in current test result coverage. pub fn merge_coverages(&mut self, other_coverage: Option) { let old_coverage = std::mem::take(&mut self.coverage); diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 2198921be..6ed06e525 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -24,7 +24,7 @@ use foundry_evm::{ invariant::{ check_sequence, replay_error, replay_run, InvariantExecutor, InvariantFuzzError, }, - CallResult, EvmError, ExecutionErr, Executor, RawCallResult, + CallResult, EvmError, ExecutionErr, Executor, ITest, RawCallResult, }, fuzz::{ fixture_name, @@ -36,6 +36,7 @@ use foundry_evm::{ use proptest::test_runner::TestRunner; use rayon::prelude::*; use std::{ + borrow::Cow, cmp::min, collections::{BTreeMap, HashMap}, time::Instant, @@ -270,6 +271,7 @@ impl<'a> ContractRunner<'a> { )); } } + // There are multiple setUp function, so we return a single test result for `setUp` if setup_fns.len() > 1 { return SuiteResult::new( @@ -412,21 +414,26 @@ impl<'a> ContractRunner<'a> { /// Runs a single unit test. /// - /// Calls the given functions and returns the `TestResult`. + /// Applies before test txes (if any), runs current test and returns the `TestResult`. /// - /// State modifications are not committed to the evm database but discarded after the call, - /// similar to `eth_call`. + /// Before test txes are applied in order and state modifications committed to the EVM database + /// (therefore the unit test call will be made on modified state). + /// State modifications of before test txes and unit test function call are discarded after + /// test ends, similar to `eth_call`. pub fn run_unit_test( &self, func: &Function, should_fail: bool, setup: TestSetup, ) -> TestResult { - let address = setup.address; - let test_result = TestResult::new(setup); + // Prepare unit test execution. + let (executor, test_result, address) = match self.prepare_test(func, setup) { + Ok(res) => res, + Err(res) => return res, + }; - // Run unit test - let (mut raw_call_result, reason) = match self.executor.call( + // Run current unit test. + let (mut raw_call_result, reason) = match executor.call( self.sender, address, func, @@ -437,11 +444,10 @@ impl<'a> ContractRunner<'a> { Ok(res) => (res.raw, None), Err(EvmError::Execution(err)) => (err.raw, Some(err.reason)), Err(EvmError::SkipError) => return test_result.single_skip(), - Err(err) => return test_result.single_fail(err), + Err(err) => return test_result.single_fail(Some(err.to_string())), }; - let success = - self.executor.is_raw_call_mut_success(address, &mut raw_call_result, should_fail); + let success = executor.is_raw_call_mut_success(address, &mut raw_call_result, should_fail); test_result.single_result(success, reason, raw_call_result) } @@ -618,6 +624,15 @@ impl<'a> ContractRunner<'a> { ) } + /// Runs a fuzzed test. + /// + /// Applies the before test txes (if any), fuzzes the current function and returns the + /// `TestResult`. + /// + /// Before test txes are applied in order and state modifications committed to the EVM database + /// (therefore the fuzz test will use the modified state). + /// State modifications of before test txes and fuzz test are discarded after test ends, + /// similar to `eth_call`. pub fn run_fuzz_test( &self, func: &Function, @@ -626,14 +641,18 @@ impl<'a> ContractRunner<'a> { setup: TestSetup, fuzz_config: FuzzConfig, ) -> TestResult { - let address = setup.address; + let progress = start_fuzz_progress(self.progress, self.name, &func.name, fuzz_config.runs); + + // Prepare fuzz test execution. let fuzz_fixtures = setup.fuzz_fixtures.clone(); - let test_result = TestResult::new(setup); + let (executor, test_result, address) = match self.prepare_test(func, setup) { + Ok(res) => res, + Err(res) => return res, + }; - // Run fuzz test - let progress = start_fuzz_progress(self.progress, self.name, &func.name, fuzz_config.runs); + // Run fuzz test. let fuzzed_executor = - FuzzedExecutor::new(self.executor.clone(), runner, self.sender, fuzz_config); + FuzzedExecutor::new(executor.into_owned(), runner, self.sender, fuzz_config); let result = fuzzed_executor.fuzz( func, &fuzz_fixtures, @@ -650,4 +669,51 @@ impl<'a> ContractRunner<'a> { } test_result.fuzz_result(result) } + + /// Prepares single unit test and fuzz test execution: + /// - set up the test result and executor + /// - check if before test txes are configured and apply them in order + /// + /// Before test txes are arrays of arbitrary calldata obtained by calling the `beforeTest` + /// function with test selector as a parameter. + /// + /// Unit tests within same contract (or even current test) are valid options for before test tx + /// configuration. Test execution stops if any of before test txes fails. + fn prepare_test( + &self, + func: &Function, + setup: TestSetup, + ) -> Result<(Cow<'_, Executor>, TestResult, Address), TestResult> { + let address = setup.address; + let mut executor = Cow::Borrowed(&self.executor); + let mut test_result = TestResult::new(setup); + + // Apply before test configured functions (if any). + if self.contract.abi.functions().filter(|func| func.name.is_before_test_setup()).count() == + 1 + { + for calldata in executor + .call_sol_default( + address, + &ITest::beforeTestSetupCall { testSelector: func.selector() }, + ) + .beforeTestCalldata + { + // Apply before test configured calldata. + match executor.to_mut().transact_raw(self.sender, address, calldata, U256::ZERO) { + Ok(call_result) => { + // Merge tx result traces in unit test result. + test_result.merge_call_result(&call_result); + + // To continue unit test execution the call should not revert. + if call_result.reverted { + return Err(test_result.single_fail(None)) + } + } + Err(_) => return Err(test_result.single_fail(None)), + } + } + } + Ok((executor, test_result, address)) + } } diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index 1268300e4..a74933f6f 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -364,3 +364,6 @@ test_repro!(8168); // https://github.com/foundry-rs/foundry/issues/8383 test_repro!(8383); + +// https://github.com/foundry-rs/foundry/issues/1543 +test_repro!(1543); diff --git a/testdata/default/repros/Issue1543.t.sol b/testdata/default/repros/Issue1543.t.sol new file mode 100644 index 000000000..e8b4806ed --- /dev/null +++ b/testdata/default/repros/Issue1543.t.sol @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; + +contract SelfDestructor { + function kill() external { + selfdestruct(payable(msg.sender)); + } +} + +// https://github.com/foundry-rs/foundry/issues/1543 +contract Issue1543Test is DSTest { + SelfDestructor killer; + uint256 a; + uint256 b; + + function setUp() public { + killer = new SelfDestructor(); + } + + function beforeTestSetup(bytes4 testSelector) public pure returns (bytes[] memory beforeTestCalldata) { + if (testSelector == this.testKill.selector) { + beforeTestCalldata = new bytes[](1); + beforeTestCalldata[0] = abi.encodePacked(this.kill_contract.selector); + } + + if (testSelector == this.testA.selector) { + beforeTestCalldata = new bytes[](3); + beforeTestCalldata[0] = abi.encodePacked(this.testA.selector); + beforeTestCalldata[1] = abi.encodePacked(this.testA.selector); + beforeTestCalldata[2] = abi.encodePacked(this.testA.selector); + } + + if (testSelector == this.testB.selector) { + beforeTestCalldata = new bytes[](1); + beforeTestCalldata[0] = abi.encodePacked(this.setB.selector); + } + + if (testSelector == this.testC.selector) { + beforeTestCalldata = new bytes[](2); + beforeTestCalldata[0] = abi.encodePacked(this.testA.selector); + beforeTestCalldata[1] = abi.encodeWithSignature("setBWithValue(uint256)", 111); + } + } + + function kill_contract() external { + uint256 killer_size = getSize(address(killer)); + require(killer_size == 106); + killer.kill(); + } + + function testKill() public view { + uint256 killer_size = getSize(address(killer)); + require(killer_size == 0); + } + + function getSize(address c) public view returns (uint32) { + uint32 size; + assembly { + size := extcodesize(c) + } + return size; + } + + function testA() public { + require(a <= 3); + a += 1; + } + + function testSimpleA() public view { + require(a == 0); + } + + function setB() public { + b = 100; + } + + function testB() public { + require(b == 100); + } + + function setBWithValue(uint256 value) public { + b = value; + } + + function testC(uint256 h) public { + assertEq(a, 1); + assertEq(b, 111); + } +} From f7f1240b971887ff5958bcde2961b99311896d28 Mon Sep 17 00:00:00 2001 From: Paul Razvan Berg Date: Fri, 26 Jul 2024 17:24:23 +0300 Subject: [PATCH 014/184] docs: document unit for --with-gas-price (#8370) * docs: document unit for --with-gas-price * chore: apply same doc style to clarify string with denoms can be used * Apply suggestions from code review --------- Co-authored-by: Enrique Ortiz Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/cli/src/opts/transaction.rs | 5 ++++- crates/script/src/lib.rs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/cli/src/opts/transaction.rs b/crates/cli/src/opts/transaction.rs index d424626c4..5cf126685 100644 --- a/crates/cli/src/opts/transaction.rs +++ b/crates/cli/src/opts/transaction.rs @@ -10,7 +10,10 @@ pub struct TransactionOpts { #[arg(long, env = "ETH_GAS_LIMIT")] pub gas_limit: Option, - /// Gas price for legacy transactions, or max fee per gas for EIP1559 transactions. + /// Gas price for legacy transactions, or max fee per gas for EIP1559 transactions, either + /// specified in wei, or as a string with a unit type. + /// + /// Examples: 1ether, 10gwei, 0.01ether #[arg( long, env = "ETH_GAS_PRICE", diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 97a33541f..6ca1d4610 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -169,7 +169,10 @@ pub struct ScriptArgs { #[arg(long)] pub json: bool, - /// Gas price for legacy transactions, or max fee per gas for EIP1559 transactions. + /// Gas price for legacy transactions, or max fee per gas for EIP1559 transactions, either + /// specified in wei, or as a string with a unit type. + /// + /// Examples: 1ether, 10gwei, 0.01ether #[arg( long, env = "ETH_GAS_PRICE", From f5e4ec8ad6c7282f09deb75b94a728336d1dbefe Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 26 Jul 2024 17:02:52 +0200 Subject: [PATCH 015/184] chore: decode only ASCII reverts (#8531) --- crates/cheatcodes/src/test/expect.rs | 11 +++++++---- crates/evm/core/src/decode.rs | 6 +++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/crates/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs index f2b7cbc06..4dc535508 100644 --- a/crates/cheatcodes/src/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -582,10 +582,13 @@ pub(crate) fn handle_expect_revert( Ok(success_return()) } else { let stringify = |data: &[u8]| { - String::abi_decode(data, false) - .ok() - .or_else(|| std::str::from_utf8(data).ok().map(ToOwned::to_owned)) - .unwrap_or_else(|| hex::encode_prefixed(data)) + if let Ok(s) = String::abi_decode(data, false) { + return s; + } + if data.is_ascii() { + return std::str::from_utf8(data).unwrap().to_owned(); + } + hex::encode_prefixed(data) }; Err(fmt_err!( "Error != expected error: {} != {}", diff --git a/crates/evm/core/src/decode.rs b/crates/evm/core/src/decode.rs index 4a2fadb81..0c52ea3c7 100644 --- a/crates/evm/core/src/decode.rs +++ b/crates/evm/core/src/decode.rs @@ -171,9 +171,9 @@ impl RevertDecoder { return Some(s); } - // UTF-8-encoded string. - if let Ok(s) = std::str::from_utf8(err) { - return Some(s.to_string()); + // ASCII string. + if err.is_ascii() { + return Some(std::str::from_utf8(err).unwrap().to_string()); } // Generic custom error. From 0ade1fd5db835d60d94b9b47a1052a1d6160d6ed Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 26 Jul 2024 17:05:03 +0200 Subject: [PATCH 016/184] test: consolidate RPC URLs, remove flaky ones (#8534) * test: consolidate RPC URLs, remove flaky ones * chore: clippy --- .github/scripts/format.sh | 7 ++ .github/workflows/test.yml | 6 +- crates/forge/tests/it/fork.rs | 4 +- crates/forge/tests/it/test_helpers.rs | 24 +++--- crates/test-utils/src/rpc.rs | 85 +++++++++++++------ testdata/default/cheats/Fork.t.sol | 12 +-- testdata/default/cheats/Fork2.t.sol | 14 +-- testdata/default/cheats/RpcUrls.t.sol | 17 +--- testdata/default/fork/ForkSame_1.t.sol | 2 +- testdata/default/fork/ForkSame_2.t.sol | 2 +- testdata/default/fork/Transact.t.sol | 4 +- .../invariant/common/InvariantRollFork.t.sol | 4 +- testdata/default/repros/Issue2623.t.sol | 2 +- testdata/default/repros/Issue2629.t.sol | 4 +- testdata/default/repros/Issue2723.t.sol | 2 +- testdata/default/repros/Issue2956.t.sol | 4 +- testdata/default/repros/Issue2984.t.sol | 4 +- testdata/default/repros/Issue3077.t.sol | 2 +- testdata/default/repros/Issue3110.t.sol | 2 +- testdata/default/repros/Issue3119.t.sol | 2 +- testdata/default/repros/Issue3192.t.sol | 4 +- testdata/default/repros/Issue3220.t.sol | 4 +- testdata/default/repros/Issue3221.t.sol | 4 +- testdata/default/repros/Issue3223.t.sol | 4 +- testdata/default/repros/Issue3653.t.sol | 2 +- testdata/default/repros/Issue3674.t.sol | 4 +- testdata/default/repros/Issue3703.t.sol | 6 +- testdata/default/repros/Issue4586.t.sol | 2 +- testdata/default/repros/Issue4640.t.sol | 2 +- testdata/default/repros/Issue5739.t.sol | 2 +- testdata/default/repros/Issue5929.t.sol | 2 +- testdata/default/repros/Issue5935.t.sol | 7 +- testdata/default/repros/Issue6032.t.sol | 2 +- testdata/default/repros/Issue6538.t.sol | 2 +- testdata/default/repros/Issue6616.t.sol | 4 +- testdata/default/repros/Issue6759.t.sol | 6 +- testdata/default/repros/Issue7481.t.sol | 2 +- testdata/default/repros/Issue8004.t.sol | 10 +-- testdata/default/repros/Issue8006.t.sol | 2 +- testdata/default/repros/Issue8168.t.sol | 4 +- testdata/default/repros/Issue8287.t.sol | 4 +- 41 files changed, 152 insertions(+), 130 deletions(-) create mode 100755 .github/scripts/format.sh diff --git a/.github/scripts/format.sh b/.github/scripts/format.sh new file mode 100755 index 000000000..aefb4c0ea --- /dev/null +++ b/.github/scripts/format.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -eo pipefail + +# We have to ignore at shell level because testdata/ is not a valid Foundry project, +# so running `forge fmt` with `--root testdata` won't actually check anything +shopt -s extglob +cargo run --bin forge -- fmt "$@" $(find testdata -name '*.sol' ! -name Vm.sol) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c8758b236..9e6db23ae 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -86,11 +86,7 @@ jobs: cache-on-failure: true - name: forge fmt shell: bash - # We have to ignore at shell level because testdata/ is not a valid Foundry project, - # so running `forge fmt` with `--root testdata` won't actually check anything - run: | - shopt -s extglob - cargo run --bin forge -- fmt --check $(find testdata -name '*.sol' ! -name Vm.sol) + run: ./.github/scripts/format.sh --check crate-checks: runs-on: ubuntu-latest diff --git a/crates/forge/tests/it/fork.rs b/crates/forge/tests/it/fork.rs index a6b215624..26c45aa18 100644 --- a/crates/forge/tests/it/fork.rs +++ b/crates/forge/tests/it/fork.rs @@ -110,8 +110,8 @@ async fn test_storage_caching_config() { Filter::new("testStorageCaching", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork")) .exclude_tests(".*Revert"); TestConfig::with_filter(runner, filter).run().await; - let cache_dir = Config::foundry_block_cache_dir(Chain::mainnet(), 19800000); - assert!(!cache_dir.unwrap().exists()); + let cache_dir = Config::foundry_block_cache_dir(Chain::mainnet(), 19800000).unwrap(); + let _ = fs::remove_file(cache_dir); // no_storage_caching set to false: storage should be cached let mut config = TEST_DATA_DEFAULT.config.clone(); diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 75da92141..480a924a0 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -1,5 +1,6 @@ //! Test helpers for Forge integration tests. +use alloy_chains::NamedChain; use alloy_primitives::U256; use forge::{ revm::primitives::SpecId, MultiContractRunner, MultiContractRunnerBuilder, TestOptions, @@ -18,7 +19,7 @@ use foundry_evm::{ constants::CALLER, opts::{Env, EvmOpts}, }; -use foundry_test_utils::{fd_lock, init_tracing}; +use foundry_test_utils::{fd_lock, init_tracing, rpc::next_rpc_endpoint}; use once_cell::sync::Lazy; use std::{ env, fmt, @@ -357,18 +358,13 @@ pub fn manifest_root() -> &'static Path { /// the RPC endpoints used during tests pub fn rpc_endpoints() -> RpcEndpoints { RpcEndpoints::new([ - ( - "rpcAlias", - RpcEndpoint::Url( - "https://eth-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf".to_string(), - ), - ), - ( - "rpcAliasSepolia", - RpcEndpoint::Url( - "https://eth-sepolia.g.alchemy.com/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf".to_string(), - ), - ), - ("rpcEnvAlias", RpcEndpoint::Env("${RPC_ENV_ALIAS}".to_string())), + ("mainnet", RpcEndpoint::Url(next_rpc_endpoint(NamedChain::Mainnet))), + ("mainnet2", RpcEndpoint::Url(next_rpc_endpoint(NamedChain::Mainnet))), + ("sepolia", RpcEndpoint::Url(next_rpc_endpoint(NamedChain::Sepolia))), + ("optimism", RpcEndpoint::Url(next_rpc_endpoint(NamedChain::Optimism))), + ("arbitrum", RpcEndpoint::Url(next_rpc_endpoint(NamedChain::Arbitrum))), + ("polygon", RpcEndpoint::Url(next_rpc_endpoint(NamedChain::Polygon))), + ("avaxTestnet", RpcEndpoint::Url("https://api.avax-test.network/ext/bc/C/rpc".into())), + ("rpcEnvAlias", RpcEndpoint::Env("${RPC_ENV_ALIAS}".into())), ]) } diff --git a/crates/test-utils/src/rpc.rs b/crates/test-utils/src/rpc.rs index 8ed6fb8d5..8d4229da7 100644 --- a/crates/test-utils/src/rpc.rs +++ b/crates/test-utils/src/rpc.rs @@ -1,5 +1,6 @@ //! RPC API keys utilities. +use foundry_config::NamedChain; use once_cell::sync::Lazy; use rand::seq::SliceRandom; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -18,7 +19,7 @@ static INFURA_KEYS: Lazy> = Lazy::new(|| { }); // List of alchemy keys for mainnet -static ALCHEMY_MAINNET_KEYS: Lazy> = Lazy::new(|| { +static ALCHEMY_KEYS: Lazy> = Lazy::new(|| { let mut keys = vec![ "ib1f4u1ojm-9lJJypwkeZeG-75TJRB7O", "7mTtk6IW4DwroGnKmG_bOWri2hyaGYhX", @@ -44,6 +45,9 @@ static ALCHEMY_MAINNET_KEYS: Lazy> = Lazy::new(|| { "sDNCLu_e99YZRkbWlVHiuM3BQ5uxYCZU", "M6lfpxTBrywHOvKXOS4yb7cTTpa25ZQ9", "UK8U_ogrbYB4lQFTGJHHDrbiS4UPnac6", + "Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf", + "UVatYU2Ax0rX6bDiqddeTRDdcCxzdpoE", + "bVjX9v-FpmUhf5R_oHIgwJx2kXvYPRbx", ]; keys.shuffle(&mut rand::thread_rng()); @@ -79,55 +83,43 @@ fn next() -> usize { } fn num_keys() -> usize { - INFURA_KEYS.len() + ALCHEMY_MAINNET_KEYS.len() + INFURA_KEYS.len() + ALCHEMY_KEYS.len() } /// Returns the next _mainnet_ rpc endpoint in inline /// /// This will rotate all available rpc endpoints pub fn next_http_rpc_endpoint() -> String { - next_rpc_endpoint("mainnet") + next_rpc_endpoint(NamedChain::Mainnet) } /// Returns the next _mainnet_ rpc endpoint in inline /// /// This will rotate all available rpc endpoints pub fn next_ws_rpc_endpoint() -> String { - next_ws_endpoint("mainnet") + next_ws_endpoint(NamedChain::Mainnet) } /// Returns the next HTTP RPC endpoint. -pub fn next_rpc_endpoint(network: &str) -> String { - let idx = next() % num_keys(); - if idx < INFURA_KEYS.len() { - format!("https://{network}.infura.io/v3/{}", INFURA_KEYS[idx]) - } else { - let idx = idx - INFURA_KEYS.len(); - format!("https://eth-{network}.alchemyapi.io/v2/{}", ALCHEMY_MAINNET_KEYS[idx]) - } +pub fn next_rpc_endpoint(chain: NamedChain) -> String { + next_url(false, chain) } /// Returns the next WS RPC endpoint. -pub fn next_ws_endpoint(network: &str) -> String { - let idx = next() % num_keys(); - if idx < INFURA_KEYS.len() { - format!("wss://{network}.infura.io/v3/{}", INFURA_KEYS[idx]) - } else { - let idx = idx - INFURA_KEYS.len(); - format!("wss://eth-{network}.alchemyapi.io/v2/{}", ALCHEMY_MAINNET_KEYS[idx]) - } +pub fn next_ws_endpoint(chain: NamedChain) -> String { + next_url(true, chain) } /// Returns endpoint that has access to archive state pub fn next_http_archive_rpc_endpoint() -> String { - let idx = next() % ALCHEMY_MAINNET_KEYS.len(); - format!("https://eth-mainnet.alchemyapi.io/v2/{}", ALCHEMY_MAINNET_KEYS[idx]) + let idx = next() % ALCHEMY_KEYS.len(); + format!("https://eth-mainnet.g.alchemy.com/v2/{}", ALCHEMY_KEYS[idx]) } /// Returns endpoint that has access to archive state pub fn next_ws_archive_rpc_endpoint() -> String { - let idx = next() % ALCHEMY_MAINNET_KEYS.len(); - format!("wss://eth-mainnet.alchemyapi.io/v2/{}", ALCHEMY_MAINNET_KEYS[idx]) + let idx = next() % ALCHEMY_KEYS.len(); + format!("wss://eth-mainnet.g.alchemy.com/v2/{}", ALCHEMY_KEYS[idx]) } /// Returns the next etherscan api key @@ -136,6 +128,51 @@ pub fn next_etherscan_api_key() -> String { ETHERSCAN_MAINNET_KEYS[idx].to_string() } +fn next_url(is_ws: bool, chain: NamedChain) -> String { + use NamedChain::*; + + let idx = next() % num_keys(); + let is_infura = idx < INFURA_KEYS.len(); + + let key = if is_infura { INFURA_KEYS[idx] } else { ALCHEMY_KEYS[idx - INFURA_KEYS.len()] }; + + // Nowhere near complete. + let prefix = if is_infura { + match chain { + Optimism => "optimism", + Arbitrum => "arbitrum", + Polygon => "polygon", + _ => "", + } + } else { + match chain { + Optimism => "opt", + Arbitrum => "arb", + Polygon => "polygon", + _ => "eth", + } + }; + let network = if is_infura { + match chain { + Mainnet | Optimism | Arbitrum | Polygon => "mainnet", + _ => chain.as_str(), + } + } else { + match chain { + Mainnet | Optimism | Arbitrum | Polygon => "mainnet", + _ => chain.as_str(), + } + }; + let full = if prefix.is_empty() { network.to_string() } else { format!("{prefix}-{network}") }; + + match (is_ws, is_infura) { + (false, true) => format!("https://{full}.infura.io/v3/{key}"), + (true, true) => format!("wss://{full}.infura.io/v3/{key}"), + (false, false) => format!("https://{full}.g.alchemy.com/v2/{key}"), + (true, false) => format!("wss://{full}.g.alchemy.com/v2/{key}"), + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/testdata/default/cheats/Fork.t.sol b/testdata/default/cheats/Fork.t.sol index cb6a4e3ff..873fbec13 100644 --- a/testdata/default/cheats/Fork.t.sol +++ b/testdata/default/cheats/Fork.t.sol @@ -23,8 +23,8 @@ contract ForkTest is DSTest { // this will create two _different_ forks during setup function setUp() public { - forkA = vm.createFork("https://eth-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf", mainblock); - forkB = vm.createFork("https://eth-mainnet.alchemyapi.io/v2/9VWGraLx0tMiSWx05WH-ywgSVmMxs66W", mainblock - 1); + forkA = vm.createFork("mainnet", mainblock); + forkB = vm.createFork("mainnet2", mainblock - 1); testValue = 999; } @@ -35,7 +35,7 @@ contract ForkTest is DSTest { // ensures we can create and select in one step function testCreateSelect() public { - uint256 fork = vm.createSelectFork("https://eth-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf"); + uint256 fork = vm.createSelectFork("mainnet"); assertEq(fork, vm.activeFork()); } @@ -114,12 +114,12 @@ contract ForkTest is DSTest { // ensures forks change chain ids automatically function testCanAutoUpdateChainId() public { - vm.createSelectFork("https://polygon-pokt.nodies.app"); // Polygon mainnet RPC URL - assertEq(block.chainid, 137); + vm.createSelectFork("sepolia"); + assertEq(block.chainid, 11155111); } // ensures forks storage is cached at block function testStorageCaching() public { - vm.createSelectFork("https://eth-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf", 19800000); + vm.createSelectFork("mainnet", 19800000); } } diff --git a/testdata/default/cheats/Fork2.t.sol b/testdata/default/cheats/Fork2.t.sol index da382e90e..3e8f68a6c 100644 --- a/testdata/default/cheats/Fork2.t.sol +++ b/testdata/default/cheats/Fork2.t.sol @@ -35,8 +35,8 @@ contract ForkTest is DSTest { // this will create two _different_ forks during setup function setUp() public { - mainnetFork = vm.createFork("rpcAlias"); - optimismFork = vm.createFork("https://opt-mainnet.g.alchemy.com/v2/UVatYU2Ax0rX6bDiqddeTRDdcCxzdpoE"); + mainnetFork = vm.createFork("mainnet"); + optimismFork = vm.createFork("optimism"); } // ensures forks use different ids @@ -57,7 +57,7 @@ contract ForkTest is DSTest { } function testCanCreateSelect() public { - uint256 anotherFork = vm.createSelectFork("rpcAlias"); + uint256 anotherFork = vm.createSelectFork("mainnet"); assertEq(anotherFork, vm.activeFork()); } @@ -75,12 +75,12 @@ contract ForkTest is DSTest { // test that we can switch between forks, and "roll" blocks function testCanRollFork() public { vm.selectFork(mainnetFork); - uint256 otherMain = vm.createFork("rpcAlias", block.number - 1); + uint256 otherMain = vm.createFork("mainnet", block.number - 1); vm.selectFork(otherMain); uint256 mainBlock = block.number; uint256 forkedBlock = 14608400; - uint256 otherFork = vm.createFork("rpcAlias", forkedBlock); + uint256 otherFork = vm.createFork("mainnet", forkedBlock); vm.selectFork(otherFork); assertEq(block.number, forkedBlock); @@ -101,7 +101,7 @@ contract ForkTest is DSTest { uint256 block = 16261704; // fork until previous block - uint256 fork = vm.createSelectFork("rpcAlias", block - 1); + uint256 fork = vm.createSelectFork("mainnet", block - 1); // block transactions in order: https://beaconcha.in/block/16261704#transactions // run transactions from current block until tx @@ -230,7 +230,7 @@ contract ForkTest is DSTest { } function testRpcWithUrl() public { - bytes memory result = vm.rpc("rpcAlias", "eth_blockNumber", "[]"); + bytes memory result = vm.rpc("mainnet", "eth_blockNumber", "[]"); uint256 decodedResult = vm.parseUint(vm.toString(result)); assertGt(decodedResult, 20_000_000); } diff --git a/testdata/default/cheats/RpcUrls.t.sol b/testdata/default/cheats/RpcUrls.t.sol index 4e3ceba58..f7b980860 100644 --- a/testdata/default/cheats/RpcUrls.t.sol +++ b/testdata/default/cheats/RpcUrls.t.sol @@ -9,8 +9,8 @@ contract RpcUrlTest is DSTest { // returns the correct url function testCanGetRpcUrl() public { - string memory url = vm.rpcUrl("rpcAlias"); // note: this alias is pre-configured in the test runner - assertEq(url, "https://eth-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf"); + string memory url = vm.rpcUrl("mainnet"); + assertEq(bytes(url).length, 69); } // returns an error if env alias does not exist @@ -27,21 +27,12 @@ contract RpcUrlTest is DSTest { ); string[2][] memory _urls = vm.rpcUrls(); - string memory url = vm.rpcUrl("rpcAlias"); + string memory url = vm.rpcUrl("mainnet"); vm.setEnv("RPC_ENV_ALIAS", url); string memory envUrl = vm.rpcUrl("rpcEnvAlias"); assertEq(url, envUrl); string[2][] memory allUrls = vm.rpcUrls(); - assertEq(allUrls.length, 3); - - string[2] memory val = allUrls[0]; - assertEq(val[0], "rpcAlias"); - - string[2] memory env = allUrls[1]; - assertEq(env[0], "rpcAliasSepolia"); - - string[2] memory env2 = allUrls[2]; - assertEq(env2[0], "rpcEnvAlias"); + assertGe(allUrls.length, 2); } } diff --git a/testdata/default/fork/ForkSame_1.t.sol b/testdata/default/fork/ForkSame_1.t.sol index bff9678f6..bbb73fcaa 100644 --- a/testdata/default/fork/ForkSame_1.t.sol +++ b/testdata/default/fork/ForkSame_1.t.sol @@ -11,7 +11,7 @@ contract ForkTest is DSTest { // this will create two _different_ forks during setup function setUp() public { - forkA = vm.createFork("https://eth-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf", 15_977_624); + forkA = vm.createFork("mainnet", 15_977_624); } function testDummy() public { diff --git a/testdata/default/fork/ForkSame_2.t.sol b/testdata/default/fork/ForkSame_2.t.sol index bff9678f6..bbb73fcaa 100644 --- a/testdata/default/fork/ForkSame_2.t.sol +++ b/testdata/default/fork/ForkSame_2.t.sol @@ -11,7 +11,7 @@ contract ForkTest is DSTest { // this will create two _different_ forks during setup function setUp() public { - forkA = vm.createFork("https://eth-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf", 15_977_624); + forkA = vm.createFork("mainnet", 15_977_624); } function testDummy() public { diff --git a/testdata/default/fork/Transact.t.sol b/testdata/default/fork/Transact.t.sol index ec803906d..0e3d9c9cb 100644 --- a/testdata/default/fork/Transact.t.sol +++ b/testdata/default/fork/Transact.t.sol @@ -20,7 +20,7 @@ contract TransactOnForkTest is DSTest { function testTransact() public { // A random block https://etherscan.io/block/17134913 - uint256 fork = vm.createFork("rpcAlias", 17134913); + uint256 fork = vm.createFork("mainnet", 17134913); vm.selectFork(fork); // a random transfer transaction in the next block: https://etherscan.io/tx/0xaf6201d435b216a858c580e20512a16136916d894aa33260650e164e3238c771 bytes32 tx = 0xaf6201d435b216a858c580e20512a16136916d894aa33260650e164e3238c771; @@ -48,7 +48,7 @@ contract TransactOnForkTest is DSTest { function testTransactCooperatesWithCheatcodes() public { // A random block https://etherscan.io/block/16260609 - uint256 fork = vm.createFork("rpcAlias", 16260609); + uint256 fork = vm.createFork("mainnet", 16260609); vm.selectFork(fork); // a random ERC20 USDT transfer transaction in the next block: https://etherscan.io/tx/0x33350512fec589e635865cbdb38fa3a20a2aa160c52611f1783d0ba24ad13c8c diff --git a/testdata/default/fuzz/invariant/common/InvariantRollFork.t.sol b/testdata/default/fuzz/invariant/common/InvariantRollFork.t.sol index d74509cd4..d15619b63 100644 --- a/testdata/default/fuzz/invariant/common/InvariantRollFork.t.sol +++ b/testdata/default/fuzz/invariant/common/InvariantRollFork.t.sol @@ -23,7 +23,7 @@ contract InvariantRollForkBlockTest is DSTest { RollForkHandler forkHandler; function setUp() public { - vm.createSelectFork("rpcAlias", 19812632); + vm.createSelectFork("mainnet", 19812632); forkHandler = new RollForkHandler(); } @@ -39,7 +39,7 @@ contract InvariantRollForkStateTest is DSTest { RollForkHandler forkHandler; function setUp() public { - vm.createSelectFork("rpcAlias", 19812632); + vm.createSelectFork("mainnet", 19812632); forkHandler = new RollForkHandler(); } diff --git a/testdata/default/repros/Issue2623.t.sol b/testdata/default/repros/Issue2623.t.sol index 8534aeeaf..1d1c2b35b 100644 --- a/testdata/default/repros/Issue2623.t.sol +++ b/testdata/default/repros/Issue2623.t.sol @@ -9,7 +9,7 @@ contract Issue2623Test is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); function testRollFork() public { - uint256 fork = vm.createFork("rpcAlias", 10); + uint256 fork = vm.createFork("mainnet", 10); vm.selectFork(fork); assertEq(block.number, 10); diff --git a/testdata/default/repros/Issue2629.t.sol b/testdata/default/repros/Issue2629.t.sol index a1f430858..ffff50722 100644 --- a/testdata/default/repros/Issue2629.t.sol +++ b/testdata/default/repros/Issue2629.t.sol @@ -11,13 +11,13 @@ contract Issue2629Test is DSTest { function testSelectFork() public { address coinbase = 0x0193d941b50d91BE6567c7eE1C0Fe7AF498b4137; - uint256 f1 = vm.createSelectFork("rpcAlias", 9); + uint256 f1 = vm.createSelectFork("mainnet", 9); vm.selectFork(f1); assertEq(block.number, 9); assertEq(coinbase.balance, 11250000000000000000); - uint256 f2 = vm.createFork("rpcAlias", 10); + uint256 f2 = vm.createFork("mainnet", 10); vm.selectFork(f2); assertEq(block.number, 10); diff --git a/testdata/default/repros/Issue2723.t.sol b/testdata/default/repros/Issue2723.t.sol index c260f9467..70e522296 100644 --- a/testdata/default/repros/Issue2723.t.sol +++ b/testdata/default/repros/Issue2723.t.sol @@ -11,7 +11,7 @@ contract Issue2723Test is DSTest { function testRollFork() public { address coinbase = 0x0193d941b50d91BE6567c7eE1C0Fe7AF498b4137; - vm.createSelectFork("rpcAlias", 9); + vm.createSelectFork("mainnet", 9); assertEq(block.number, 9); assertEq(coinbase.balance, 11250000000000000000); diff --git a/testdata/default/repros/Issue2956.t.sol b/testdata/default/repros/Issue2956.t.sol index 9d9e5f9ac..b69d17fb3 100644 --- a/testdata/default/repros/Issue2956.t.sol +++ b/testdata/default/repros/Issue2956.t.sol @@ -11,8 +11,8 @@ contract Issue2956Test is DSTest { uint256 fork2; function setUp() public { - fork1 = vm.createFork("rpcAliasSepolia", 5565573); - fork2 = vm.createFork("https://api.avax-test.network/ext/bc/C/rpc", 12880747); + fork1 = vm.createFork("sepolia", 5565573); + fork2 = vm.createFork("avaxTestnet", 12880747); } function testForkNonce() public { diff --git a/testdata/default/repros/Issue2984.t.sol b/testdata/default/repros/Issue2984.t.sol index 8e55d5dae..1a181ad53 100644 --- a/testdata/default/repros/Issue2984.t.sol +++ b/testdata/default/repros/Issue2984.t.sol @@ -11,7 +11,7 @@ contract Issue2984Test is DSTest { uint256 snapshot; function setUp() public { - fork = vm.createSelectFork("https://api.avax-test.network/ext/bc/C/rpc", 12880747); + fork = vm.createSelectFork("avaxTestnet", 12880747); snapshot = vm.snapshot(); } @@ -20,6 +20,6 @@ contract Issue2984Test is DSTest { } function testForkSelectSnapshot() public { - uint256 fork2 = vm.createSelectFork("https://api.avax-test.network/ext/bc/C/rpc", 12880749); + uint256 fork2 = vm.createSelectFork("avaxTestnet", 12880749); } } diff --git a/testdata/default/repros/Issue3077.t.sol b/testdata/default/repros/Issue3077.t.sol index cc76b57b6..b67316294 100644 --- a/testdata/default/repros/Issue3077.t.sol +++ b/testdata/default/repros/Issue3077.t.sol @@ -15,7 +15,7 @@ abstract contract ZeroState is DSTest { function setUp() public virtual { vm.startPrank(deployer); - mainnetFork = vm.createFork("rpcAlias"); + mainnetFork = vm.createFork("mainnet"); vm.selectFork(mainnetFork); vm.rollFork(block.number - 20); // deploy tokens diff --git a/testdata/default/repros/Issue3110.t.sol b/testdata/default/repros/Issue3110.t.sol index 7a2622427..f9ca984bd 100644 --- a/testdata/default/repros/Issue3110.t.sol +++ b/testdata/default/repros/Issue3110.t.sol @@ -17,7 +17,7 @@ abstract contract ZeroState is DSTest { vm.label(deployer, "Deployer"); vm.startPrank(deployer); - mainnetFork = vm.createFork("rpcAlias"); + mainnetFork = vm.createFork("mainnet"); vm.selectFork(mainnetFork); vm.rollFork(block.number - 20); diff --git a/testdata/default/repros/Issue3119.t.sol b/testdata/default/repros/Issue3119.t.sol index 5c94b4c5f..3e82985c2 100644 --- a/testdata/default/repros/Issue3119.t.sol +++ b/testdata/default/repros/Issue3119.t.sol @@ -12,7 +12,7 @@ contract Issue3119Test is DSTest { address public alice = vm.addr(2); function testRollFork() public { - uint256 fork = vm.createFork("rpcAlias"); + uint256 fork = vm.createFork("mainnet"); vm.selectFork(fork); FortressSwap fortressSwap = new FortressSwap(address(owner)); diff --git a/testdata/default/repros/Issue3192.t.sol b/testdata/default/repros/Issue3192.t.sol index 0deb22f49..1f693b1aa 100644 --- a/testdata/default/repros/Issue3192.t.sol +++ b/testdata/default/repros/Issue3192.t.sol @@ -11,8 +11,8 @@ contract Issue3192Test is DSTest { uint256 fork2; function setUp() public { - fork1 = vm.createFork("rpcAlias", 7475589); - fork2 = vm.createFork("rpcAlias", 12880747); + fork1 = vm.createFork("mainnet", 7475589); + fork2 = vm.createFork("mainnet", 12880747); vm.selectFork(fork1); } diff --git a/testdata/default/repros/Issue3220.t.sol b/testdata/default/repros/Issue3220.t.sol index b88d997c1..30e75027a 100644 --- a/testdata/default/repros/Issue3220.t.sol +++ b/testdata/default/repros/Issue3220.t.sol @@ -14,9 +14,9 @@ contract Issue3220Test is DSTest { uint256 counter; function setUp() public { - fork1 = vm.createFork("rpcAlias", 7475589); + fork1 = vm.createFork("mainnet", 7475589); vm.selectFork(fork1); - fork2 = vm.createFork("rpcAlias", 12880747); + fork2 = vm.createFork("mainnet", 12880747); } function testForkRevert() public { diff --git a/testdata/default/repros/Issue3221.t.sol b/testdata/default/repros/Issue3221.t.sol index 4a9dd7be4..628f9d0e1 100644 --- a/testdata/default/repros/Issue3221.t.sol +++ b/testdata/default/repros/Issue3221.t.sol @@ -11,8 +11,8 @@ contract Issue3221Test is DSTest { uint256 fork2; function setUp() public { - fork1 = vm.createFork("rpcAliasSepolia", 5565573); - fork2 = vm.createFork("https://api.avax-test.network/ext/bc/C/rpc", 12880747); + fork1 = vm.createFork("sepolia", 5565573); + fork2 = vm.createFork("avaxTestnet", 12880747); } function testForkNonce() public { diff --git a/testdata/default/repros/Issue3223.t.sol b/testdata/default/repros/Issue3223.t.sol index d4c5da751..cd43cb8ef 100644 --- a/testdata/default/repros/Issue3223.t.sol +++ b/testdata/default/repros/Issue3223.t.sol @@ -11,8 +11,8 @@ contract Issue3223Test is DSTest { uint256 fork2; function setUp() public { - fork1 = vm.createFork("rpcAliasSepolia", 2362365); - fork2 = vm.createFork("https://api.avax-test.network/ext/bc/C/rpc", 12880747); + fork1 = vm.createFork("sepolia", 2362365); + fork2 = vm.createFork("avaxTestnet", 12880747); } function testForkNonce() public { diff --git a/testdata/default/repros/Issue3653.t.sol b/testdata/default/repros/Issue3653.t.sol index b86f84c3e..8cfcc2d0e 100644 --- a/testdata/default/repros/Issue3653.t.sol +++ b/testdata/default/repros/Issue3653.t.sol @@ -11,7 +11,7 @@ contract Issue3653Test is DSTest { Token token; constructor() { - fork = vm.createSelectFork("rpcAlias", 1000000); + fork = vm.createSelectFork("mainnet", 1000000); token = new Token(); vm.makePersistent(address(token)); } diff --git a/testdata/default/repros/Issue3674.t.sol b/testdata/default/repros/Issue3674.t.sol index 813f73b5d..0b680342a 100644 --- a/testdata/default/repros/Issue3674.t.sol +++ b/testdata/default/repros/Issue3674.t.sol @@ -9,9 +9,9 @@ contract Issue3674Test is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); function testNonceCreateSelect() public { - vm.createSelectFork("rpcAliasSepolia"); + vm.createSelectFork("sepolia"); - vm.createSelectFork("https://api.avax-test.network/ext/bc/C/rpc"); + vm.createSelectFork("avaxTestnet"); assert(vm.getNonce(msg.sender) > 0x17); } } diff --git a/testdata/default/repros/Issue3703.t.sol b/testdata/default/repros/Issue3703.t.sol index 06ce6bcbe..b6dc39f26 100644 --- a/testdata/default/repros/Issue3703.t.sol +++ b/testdata/default/repros/Issue3703.t.sol @@ -9,10 +9,8 @@ contract Issue3703Test is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); function setUp() public { - uint256 fork = vm.createSelectFork( - "https://polygon-mainnet.g.alchemy.com/v2/bVjX9v-FpmUhf5R_oHIgwJx2kXvYPRbx", - bytes32(0xbed0c8c1b9ff8bf0452979d170c52893bb8954f18a904aa5bcbd0f709be050b9) - ); + uint256 fork = + vm.createSelectFork("polygon", bytes32(0xbed0c8c1b9ff8bf0452979d170c52893bb8954f18a904aa5bcbd0f709be050b9)); } function poolState(address poolAddr, uint256 expectedSqrtPriceX96, uint256 expectedLiquidity) private { diff --git a/testdata/default/repros/Issue4586.t.sol b/testdata/default/repros/Issue4586.t.sol index a41ba7a04..29284ee1b 100644 --- a/testdata/default/repros/Issue4586.t.sol +++ b/testdata/default/repros/Issue4586.t.sol @@ -13,7 +13,7 @@ contract Issue4586Test is DSTest { InvariantHandler handler; function setUp() public { - vm.createSelectFork("rpcAlias", initialBlock); + vm.createSelectFork("mainnet", initialBlock); handler = new InvariantHandler(); } diff --git a/testdata/default/repros/Issue4640.t.sol b/testdata/default/repros/Issue4640.t.sol index a875d000d..eaa87c12c 100644 --- a/testdata/default/repros/Issue4640.t.sol +++ b/testdata/default/repros/Issue4640.t.sol @@ -10,7 +10,7 @@ contract Issue4640Test is DSTest { function testArbitrumBlockNumber() public { // - vm.createSelectFork("https://arb-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf", 75219831); + vm.createSelectFork("arbitrum", 75219831); // L1 block number assertEq(block.number, 16939475); } diff --git a/testdata/default/repros/Issue5739.t.sol b/testdata/default/repros/Issue5739.t.sol index 2864c0cbf..eafbabd93 100644 --- a/testdata/default/repros/Issue5739.t.sol +++ b/testdata/default/repros/Issue5739.t.sol @@ -14,7 +14,7 @@ contract Issue5739Test is DSTest { IERC20 dai; function setUp() public { - vm.createSelectFork("rpcAlias", 19000000); + vm.createSelectFork("mainnet", 19000000); dai = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); } diff --git a/testdata/default/repros/Issue5929.t.sol b/testdata/default/repros/Issue5929.t.sol index f1009f03b..cce676d25 100644 --- a/testdata/default/repros/Issue5929.t.sol +++ b/testdata/default/repros/Issue5929.t.sol @@ -9,7 +9,7 @@ contract Issue5929Test is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); function test_transact_not_working() public { - vm.createSelectFork("rpcAlias", 15625301); + vm.createSelectFork("mainnet", 15625301); // https://etherscan.io/tx/0x96a129768ec66fd7d65114bf182f4e173bf0b73a44219adaf71f01381a3d0143 vm.transact(hex"96a129768ec66fd7d65114bf182f4e173bf0b73a44219adaf71f01381a3d0143"); } diff --git a/testdata/default/repros/Issue5935.t.sol b/testdata/default/repros/Issue5935.t.sol index 95b6f8fd5..f783d12da 100644 --- a/testdata/default/repros/Issue5935.t.sol +++ b/testdata/default/repros/Issue5935.t.sol @@ -12,15 +12,12 @@ contract SimpleStorage { } } -/// @dev start anvil --port 35353 contract Issue5935Test is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); function testFork() public { - uint256 forkId1 = - vm.createFork("https://eth-mainnet.alchemyapi.io/v2/QC55XC151AgkS3FNtWvz9VZGeu9Xd9lb", 18234083); - uint256 forkId2 = - vm.createFork("https://eth-mainnet.alchemyapi.io/v2/QC55XC151AgkS3FNtWvz9VZGeu9Xd9lb", 18234083); + uint256 forkId1 = vm.createFork("mainnet", 18234083); + uint256 forkId2 = vm.createFork("mainnet", 18234083); vm.selectFork(forkId1); SimpleStorage myContract = new SimpleStorage(); myContract.set(42); diff --git a/testdata/default/repros/Issue6032.t.sol b/testdata/default/repros/Issue6032.t.sol index fc230c47e..75002a136 100644 --- a/testdata/default/repros/Issue6032.t.sol +++ b/testdata/default/repros/Issue6032.t.sol @@ -15,7 +15,7 @@ contract Issue6032Test is DSTest { address counterAddress = address(counter); // Enter the fork - vm.createSelectFork("rpcAlias"); + vm.createSelectFork("mainnet"); assert(counterAddress.code.length > 0); // `Counter` is not deployed on the fork, which is expected. diff --git a/testdata/default/repros/Issue6538.t.sol b/testdata/default/repros/Issue6538.t.sol index d83bbc850..2b8beb578 100644 --- a/testdata/default/repros/Issue6538.t.sol +++ b/testdata/default/repros/Issue6538.t.sol @@ -10,7 +10,7 @@ contract Issue6538Test is DSTest { function test_transact() public { bytes32 lastHash = 0xdbdce1d5c14a6ca17f0e527ab762589d6a73f68697606ae0bb90df7ac9ec5087; - vm.createSelectFork("rpcAlias", lastHash); + vm.createSelectFork("mainnet", lastHash); bytes32 txhash = 0xadbe5cf9269a001d50990d0c29075b402bcc3a0b0f3258821881621b787b35c6; vm.transact(txhash); } diff --git a/testdata/default/repros/Issue6616.t.sol b/testdata/default/repros/Issue6616.t.sol index 24fa00e21..f31a79ee6 100644 --- a/testdata/default/repros/Issue6616.t.sol +++ b/testdata/default/repros/Issue6616.t.sol @@ -9,12 +9,12 @@ contract Issue6616Test is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); function testCreateForkRollLatestBlock() public { - vm.createSelectFork("rpcAlias"); + vm.createSelectFork("mainnet"); uint256 startBlock = block.number; // this will create new forks and exit once a new latest block is found for (uint256 i; i < 10; i++) { vm.sleep(5000); - vm.createSelectFork("rpcAlias"); + vm.createSelectFork("mainnet"); if (block.number > startBlock) break; } assertGt(block.number, startBlock); diff --git a/testdata/default/repros/Issue6759.t.sol b/testdata/default/repros/Issue6759.t.sol index a8039035e..e528c63e1 100644 --- a/testdata/default/repros/Issue6759.t.sol +++ b/testdata/default/repros/Issue6759.t.sol @@ -9,9 +9,9 @@ contract Issue6759Test is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); function testCreateMulti() public { - uint256 fork1 = vm.createFork("rpcAlias", 10); - uint256 fork2 = vm.createFork("rpcAlias", 10); - uint256 fork3 = vm.createFork("rpcAlias", 10); + uint256 fork1 = vm.createFork("mainnet", 10); + uint256 fork2 = vm.createFork("mainnet", 10); + uint256 fork3 = vm.createFork("mainnet", 10); assert(fork1 != fork2); assert(fork1 != fork3); assert(fork2 != fork3); diff --git a/testdata/default/repros/Issue7481.t.sol b/testdata/default/repros/Issue7481.t.sol index eb568dc94..441758b4f 100644 --- a/testdata/default/repros/Issue7481.t.sol +++ b/testdata/default/repros/Issue7481.t.sol @@ -10,7 +10,7 @@ contract Issue7481Test is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); function testFailTransact() public { - vm.createSelectFork("rpcAlias", 19514903); + vm.createSelectFork("mainnet", 19514903); // Transfer some funds to sender of tx being transacted to ensure that it appears in journaled state payable(address(0x5C60cD7a3D50877Bfebd484750FBeb245D936dAD)).call{value: 1}(""); diff --git a/testdata/default/repros/Issue8004.t.sol b/testdata/default/repros/Issue8004.t.sol index 3e077e2a2..229dde5ad 100644 --- a/testdata/default/repros/Issue8004.t.sol +++ b/testdata/default/repros/Issue8004.t.sol @@ -9,18 +9,18 @@ contract NonPersistentHelper is DSTest { uint256 public curState; function createSelectFork() external { - vm.createSelectFork("rpcAlias"); + vm.createSelectFork("mainnet"); curState += 1; } function createSelectForkAtBlock() external { - vm.createSelectFork("rpcAlias", 19000000); + vm.createSelectFork("mainnet", 19000000); curState += 1; } function createSelectForkAtTx() external { vm.createSelectFork( - "rpcAlias", vm.parseBytes32("0xb5c978f15d01fcc9b4d78967e8189e35ecc21ff4e78315ea5d616f3467003c84") + "mainnet", vm.parseBytes32("0xb5c978f15d01fcc9b4d78967e8189e35ecc21ff4e78315ea5d616f3467003c84") ); curState += 1; } @@ -66,7 +66,7 @@ contract Issue8004CreateSelectForkTest is DSTest { } function testNonPersistentHelperSelectFork() external { - uint256 forkId = vm.createFork("rpcAlias"); + uint256 forkId = vm.createFork("mainnet"); helper.selectFork(forkId); assertEq(helper.curState(), 1); } @@ -78,7 +78,7 @@ contract Issue8004RollForkTest is DSTest { uint256 forkId; function setUp() public { - forkId = vm.createSelectFork("rpcAlias"); + forkId = vm.createSelectFork("mainnet"); helper = new NonPersistentHelper(); } diff --git a/testdata/default/repros/Issue8006.t.sol b/testdata/default/repros/Issue8006.t.sol index 1a45ddf97..078117d40 100644 --- a/testdata/default/repros/Issue8006.t.sol +++ b/testdata/default/repros/Issue8006.t.sol @@ -21,7 +21,7 @@ contract Issue8006Test is DSTest { bytes32 transaction = 0x67cbad73764049e228495a3f90144aab4a37cb4b5fd697dffc234aa5ed811ace; function setUp() public { - vm.createSelectFork("rpcAlias", 16261704); + vm.createSelectFork("mainnet", 16261704); dai = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); } diff --git a/testdata/default/repros/Issue8168.t.sol b/testdata/default/repros/Issue8168.t.sol index b9bd5757a..f11cef7bc 100644 --- a/testdata/default/repros/Issue8168.t.sol +++ b/testdata/default/repros/Issue8168.t.sol @@ -9,8 +9,8 @@ contract Issue8168Test is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); function testForkWarpRollPreserved() public { - uint256 fork1 = vm.createFork("rpcAlias"); - uint256 fork2 = vm.createFork("rpcAlias"); + uint256 fork1 = vm.createFork("mainnet"); + uint256 fork2 = vm.createFork("mainnet"); vm.selectFork(fork1); uint256 initial_fork1_number = block.number; diff --git a/testdata/default/repros/Issue8287.t.sol b/testdata/default/repros/Issue8287.t.sol index cedcb4043..86901c0fd 100644 --- a/testdata/default/repros/Issue8287.t.sol +++ b/testdata/default/repros/Issue8287.t.sol @@ -9,14 +9,14 @@ contract Issue8287Test is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); function testRpcBalance() public { - uint256 f2 = vm.createSelectFork("rpcAlias", 10); + uint256 f2 = vm.createSelectFork("mainnet", 10); bytes memory data = vm.rpc("eth_getBalance", "[\"0x551e7784778ef8e048e495df49f2614f84a4f1dc\",\"0x0\"]"); string memory m = vm.toString(data); assertEq(m, "0x2086ac351052600000"); } function testRpcStorage() public { - uint256 f2 = vm.createSelectFork("rpcAlias", 10); + uint256 f2 = vm.createSelectFork("mainnet", 10); bytes memory data = vm.rpc( "eth_getStorageAt", "[\"0x551e7784778ef8e048e495df49f2614f84a4f1dc\",\"0x40BdB4497614bAe1A67061EE20AAdE3c2067AC9e\",\"0x0\"]" From df5f45c8c1a7e138203f1f479940f1500752b3dc Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Fri, 26 Jul 2024 23:11:12 +0800 Subject: [PATCH 017/184] chore(deps): bump foundry-compilers (#8535) chore(deps): bump compilers --- Cargo.lock | 22 +++++++++++----------- Cargo.toml | 2 +- crates/config/src/lib.rs | 9 --------- crates/config/src/utils.rs | 1 + crates/forge/tests/cli/config.rs | 1 - 5 files changed, 13 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dccf91eef..29d628653 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3716,9 +3716,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea06d58fce261a37f7c90ec4d2f14af729825bf7abbda14b64f8d728bc701480" +checksum = "f7c47e9b1b142be502b089e9699922110b6f15906f739b8e612755167da4f5a1" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3754,9 +3754,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0a0b1e385a5f9d88b0d6c9c8c1a5d89e33e5465e62096034abcd0dfd48a8618" +checksum = "8a8e5d34b4b594806808c200340b24fa1a60b9f65b76ecf0df406c52c0e4019a" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3764,9 +3764,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff1e9120d86202a81a7729f33fabfc77c12beca7233765d36c49ebcd8da100b" +checksum = "17c7481a171c86d76cdd257eeb99e15d532f94502efa51f76858e2196d85840e" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3788,9 +3788,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b88e8a09b7689253885c5171512987d6dea96ac328b2384f6a0233a332ec8d9" +checksum = "32523fa23051d11d534493c93e00a482e27abbbb615ca6d646ebe30b749624db" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3803,9 +3803,9 @@ dependencies = [ [[package]] name = "foundry-compilers-core" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ad521e38281f70e99a5487883179b4bfb74c855abebe5d5390d8e6d993503a" +checksum = "1e03290dd4cd78b076a43e6f8528f240d3ec74c0a56795830327d6dd781a7ac0" dependencies = [ "alloy-primitives", "cfg-if", @@ -6699,7 +6699,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18bec9b0adc4eba778b33684b7ba3e7137789434769ee3ce3930463ef904cfca" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools 0.12.1", "proc-macro2", "quote", "syn 2.0.71", diff --git a/Cargo.toml b/Cargo.toml index 83e270668..a33e01642 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -158,7 +158,7 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.5.1", default-features = false } -foundry-compilers = { version = "0.10.0", default-features = false } +foundry-compilers = { version = "0.10.1", default-features = false } foundry-fork-db = "0.2" solang-parser = "=0.3.3" diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index f5222a78d..d9c762581 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -400,11 +400,6 @@ pub struct Config { /// This includes what operations can be executed (read, write) pub fs_permissions: FsPermissions, - /// Temporary config to enable [SpecId::PRAGUE] - /// - /// Should be removed once EvmVersion Prague is supported by solc - pub prague: bool, - /// Whether to enable call isolation. /// /// Useful for more correct gas accounting and EVM behavior in general. @@ -915,9 +910,6 @@ impl Config { /// Returns the [SpecId] derived from the configured [EvmVersion] #[inline] pub fn evm_spec_id(&self) -> SpecId { - if self.prague { - return SpecId::PRAGUE_EOF - } evm_spec_id(&self.evm_version) } @@ -2042,7 +2034,6 @@ impl Default for Config { Self { profile: Self::DEFAULT_PROFILE, fs_permissions: FsPermissions::new([PathPermission::read("out")]), - prague: false, #[cfg(not(feature = "isolate-by-default"))] isolate: false, #[cfg(feature = "isolate-by-default")] diff --git a/crates/config/src/utils.rs b/crates/config/src/utils.rs index b58565c4c..d8d23711c 100644 --- a/crates/config/src/utils.rs +++ b/crates/config/src/utils.rs @@ -310,6 +310,7 @@ pub fn evm_spec_id(evm_version: &EvmVersion) -> SpecId { EvmVersion::Paris => SpecId::MERGE, EvmVersion::Shanghai => SpecId::SHANGHAI, EvmVersion::Cancun => SpecId::CANCUN, + EvmVersion::Prague => SpecId::PRAGUE_EOF, } } diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index a5ec38ec1..fcec5f15a 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -137,7 +137,6 @@ forgetest!(can_extract_config_values, |prj, cmd| { bind_json: Default::default(), fs_permissions: Default::default(), labels: Default::default(), - prague: true, isolate: true, unchecked_cheatcode_artifacts: false, create2_library_salt: Config::DEFAULT_CREATE2_LIBRARY_SALT, From a416c1001c7dba772d008a5fc43e43b024a9d684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Vincent?= <28714795+leovct@users.noreply.github.com> Date: Sat, 27 Jul 2024 08:36:28 +0200 Subject: [PATCH 018/184] feat(cheatcodes): add `vm.getFoundryVersion()` (#8530) * feat: implement `vm.getFoundryVersion` * test: implement dummy test for `vm.getFoundryVersion` * chore: modify implementation to return cargo version and build timestamp * test: modify test * docs: add sample output * chore: cargo cheats * fix: failing test and vergen setup * test: update getFoundryVersion * docs: mention built timestamps issue --------- Co-authored-by: Matthias Seitz --- Cargo.lock | 2 ++ crates/cheatcodes/Cargo.toml | 10 ++++++- crates/cheatcodes/assets/cheatcodes.json | 20 ++++++++++++++ crates/cheatcodes/build.rs | 3 +++ crates/cheatcodes/spec/src/vm.rs | 9 +++++++ crates/cheatcodes/src/test.rs | 17 ++++++++++++ testdata/cheats/Vm.sol | 1 + .../default/cheats/GetFoundryVersion.t.sol | 26 +++++++++++++++++++ 8 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 crates/cheatcodes/build.rs create mode 100644 testdata/default/cheats/GetFoundryVersion.t.sol diff --git a/Cargo.lock b/Cargo.lock index 29d628653..a5f35ad65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3576,6 +3576,7 @@ dependencies = [ "alloy-signer-local", "alloy-sol-types", "base64 0.22.1", + "chrono", "dialoguer", "eyre", "foundry-cheatcodes-spec", @@ -3598,6 +3599,7 @@ dependencies = [ "thiserror", "toml 0.8.15", "tracing", + "vergen", "walkdir", ] diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index 60304a47d..80336d4e8 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -14,6 +14,13 @@ exclude.workspace = true [lints] workspace = true +[build-dependencies] +vergen = { workspace = true, default-features = false, features = [ + "build", + "git", + "gitcl", +] } + [dependencies] foundry-cheatcodes-spec.workspace = true foundry-common.workspace = true @@ -54,6 +61,7 @@ semver.workspace = true rustc-hash.workspace = true dialoguer = "0.11.0" rand = "0.8" +chrono.workspace = true [dev-dependencies] -proptest.workspace = true \ No newline at end of file +proptest.workspace = true diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index ec12113eb..61f626477 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -4991,6 +4991,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "getFoundryVersion", + "description": "Returns the Foundry version.\nFormat: ++\nSample output: 0.2.0+faa94c384+202407110019\nNote: Build timestamps may vary slightly across platforms due to separate CI jobs.\nFor reliable version comparisons, use YYYYMMDD0000 format (e.g., >= 202407110000)\nto compare timestamps while ignoring minor time differences.", + "declaration": "function getFoundryVersion() external view returns (string memory version);", + "visibility": "external", + "mutability": "view", + "signature": "getFoundryVersion()", + "selector": "0xea991bb5", + "selectorBytes": [ + 234, + 153, + 27, + 181 + ] + }, + "group": "testing", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "getLabel", diff --git a/crates/cheatcodes/build.rs b/crates/cheatcodes/build.rs new file mode 100644 index 000000000..c2f550fb6 --- /dev/null +++ b/crates/cheatcodes/build.rs @@ -0,0 +1,3 @@ +fn main() { + vergen::EmitBuilder::builder().build_timestamp().git_sha(true).emit().unwrap(); +} diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index ff9a92e58..935d44e53 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -702,6 +702,15 @@ interface Vm { #[cheatcode(group = Testing, safety = Safe)] function breakpoint(string calldata char, bool value) external; + /// Returns the Foundry version. + /// Format: ++ + /// Sample output: 0.2.0+faa94c384+202407110019 + /// Note: Build timestamps may vary slightly across platforms due to separate CI jobs. + /// For reliable version comparisons, use YYYYMMDD0000 format (e.g., >= 202407110000) + /// to compare timestamps while ignoring minor time differences. + #[cheatcode(group = Testing, safety = Safe)] + function getFoundryVersion() external view returns (string memory version); + /// Returns the RPC url for the given alias. #[cheatcode(group = Testing, safety = Safe)] function rpcUrl(string calldata rpcAlias) external view returns (string memory json); diff --git a/crates/cheatcodes/src/test.rs b/crates/cheatcodes/src/test.rs index 4bccabda2..279150dff 100644 --- a/crates/cheatcodes/src/test.rs +++ b/crates/cheatcodes/src/test.rs @@ -1,5 +1,8 @@ //! Implementations of [`Testing`](spec::Group::Testing) cheatcodes. +use chrono::DateTime; +use std::env; + use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Error, Result, Vm::*}; use alloy_primitives::Address; use alloy_sol_types::SolValue; @@ -33,6 +36,20 @@ impl Cheatcode for breakpoint_1Call { } } +impl Cheatcode for getFoundryVersionCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self {} = self; + let cargo_version = env!("CARGO_PKG_VERSION"); + let git_sha = env!("VERGEN_GIT_SHA"); + let build_timestamp = DateTime::parse_from_rfc3339(env!("VERGEN_BUILD_TIMESTAMP")) + .expect("Invalid build timestamp format") + .format("%Y%m%d%H%M") + .to_string(); + let foundry_version = format!("{cargo_version}+{git_sha}+{build_timestamp}"); + Ok(foundry_version.abi_encode()) + } +} + impl Cheatcode for rpcUrlCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { rpcAlias } = self; diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index dfe466d3a..f42fb2298 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -245,6 +245,7 @@ interface Vm { function getBlockTimestamp() external view returns (uint256 timestamp); function getCode(string calldata artifactPath) external view returns (bytes memory creationBytecode); function getDeployedCode(string calldata artifactPath) external view returns (bytes memory runtimeBytecode); + function getFoundryVersion() external view returns (string memory version); function getLabel(address account) external view returns (string memory currentLabel); function getMappingKeyAndParentOf(address target, bytes32 elementSlot) external returns (bool found, bytes32 key, bytes32 parent); function getMappingLength(address target, bytes32 mappingSlot) external returns (uint256 length); diff --git a/testdata/default/cheats/GetFoundryVersion.t.sol b/testdata/default/cheats/GetFoundryVersion.t.sol new file mode 100644 index 000000000..6f850561d --- /dev/null +++ b/testdata/default/cheats/GetFoundryVersion.t.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract GetFoundryVersionTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function testGetFoundryVersion() public view { + string memory fullVersionString = vm.getFoundryVersion(); + + string[] memory versionComponents = vm.split(fullVersionString, "+"); + require(versionComponents.length == 3, "Invalid version format"); + + string memory semanticVersion = versionComponents[0]; + require(bytes(semanticVersion).length > 0, "Semantic version is empty"); + + string memory commitHash = versionComponents[1]; + require(bytes(commitHash).length > 0, "Commit hash is empty"); + + uint256 buildUnixTimestamp = vm.parseUint(versionComponents[2]); + uint256 minimumAcceptableTimestamp = 202406111234; + require(buildUnixTimestamp >= minimumAcceptableTimestamp, "Build timestamp is too old"); + } +} From bdd1137374cda2bfbb2a3d126c476e2dfd1864a7 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sat, 27 Jul 2024 08:38:53 +0200 Subject: [PATCH 019/184] ci: enable anvil default feature for release (#8540) --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 35ca11d93..83d216c7f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -133,7 +133,7 @@ jobs: run: | set -eo pipefail target="${{ matrix.target }}" - flags=(--release --bins --no-default-features --features rustls,aws-kms) + flags=(--release --bins --no-default-features --features rustls,aws-kms,cli) # `jemalloc` and `keccak-asm` are not supported on MSVC or aarch64 Linux. if [[ "$target" != *msvc* && "$target" != "aarch64-unknown-linux-gnu" ]]; then From 66bc49e354b1d5e29f0c7c62a4b79722c9811579 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 28 Jul 2024 09:08:16 +0000 Subject: [PATCH 020/184] chore(deps): weekly `cargo update` (#8544) Locking 59 packages to latest compatible versions Updating anstream v0.6.14 -> v0.6.15 Updating anstyle v1.0.7 -> v1.0.8 Updating anstyle-parse v0.2.4 -> v0.2.5 Updating anstyle-query v1.1.0 -> v1.1.1 Updating anstyle-wincon v3.0.3 -> v3.0.4 Updating async-compression v0.4.11 -> v0.4.12 Updating aws-sdk-kms v1.36.0 -> v1.37.0 Updating aws-sdk-sso v1.35.0 -> v1.36.0 Updating aws-sdk-ssooidc v1.36.0 -> v1.37.0 Updating aws-sdk-sts v1.35.0 -> v1.36.0 Updating blst v0.3.12 -> v0.3.13 Removing bstr v0.2.17 Removing bstr v1.9.1 Adding bstr v1.10.0 Updating clap v4.5.9 -> v4.5.11 Updating clap_builder v4.5.9 -> v4.5.11 Updating clap_complete v4.5.8 -> v4.5.11 Updating clap_complete_fig v4.5.1 -> v4.5.2 Updating clap_derive v4.5.8 -> v4.5.11 Updating clap_lex v0.7.1 -> v0.7.2 Updating colorchoice v1.0.1 -> v1.0.2 Updating env_filter v0.1.0 -> v0.1.2 Updating env_logger v0.11.3 -> v0.11.5 Updating evmole v0.3.6 -> v0.3.7 Updating generator v0.8.1 -> v0.8.2 Updating gix-actor v0.31.4 -> v0.31.5 Updating gix-config-value v0.14.6 -> v0.14.7 Updating gix-glob v0.16.3 -> v0.16.4 Updating gix-sec v0.10.6 -> v0.10.7 Updating interprocess v2.2.0 -> v2.2.1 Updating is_terminal_polyfill v1.70.0 -> v1.70.1 Updating jobserver v0.1.31 -> v0.1.32 Adding mio v1.0.1 Updating openssl v0.10.65 -> v0.10.66 Updating predicates v3.1.0 -> v3.1.2 Updating predicates-core v1.0.6 -> v1.0.8 Updating predicates-tree v1.0.9 -> v1.0.11 Updating quinn-udp v0.5.2 -> v0.5.4 Updating revm-inspectors v0.5.3 -> v0.5.4 Updating rustls v0.23.11 -> v0.23.12 Updating rustls-webpki v0.102.5 -> v0.102.6 Updating scc v2.1.4 -> v2.1.5 Updating serde_spanned v0.6.6 -> v0.6.7 Updating sha1_smol v1.0.0 -> v1.0.1 Updating similar v2.5.0 -> v2.6.0 Updating snapbox v0.6.13 -> v0.6.16 Updating snapbox-macros v0.3.9 -> v0.3.10 Updating syn v2.0.71 -> v2.0.72 Updating tokio v1.38.1 -> v1.39.2 Updating tokio-macros v2.3.0 -> v2.4.0 Updating toml v0.8.15 -> v0.8.16 Updating toml_datetime v0.6.6 -> v0.6.7 Updating toml_edit v0.22.16 -> v0.22.17 Updating version_check v0.9.4 -> v0.9.5 Updating windows v0.54.0 -> v0.58.0 Updating windows-core v0.54.0 -> v0.58.0 Adding windows-implement v0.58.0 Adding windows-interface v0.58.0 Adding windows-result v0.2.0 Adding windows-strings v0.1.0 Updating winnow v0.6.14 -> v0.6.16 note: pass `--verbose` to see 150 unchanged dependencies behind latest Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> --- Cargo.lock | 506 +++++++++++++++++++++++++++++------------------------ 1 file changed, 274 insertions(+), 232 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a5f35ad65..1a37ebd14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -129,7 +129,7 @@ dependencies = [ "proptest", "serde", "serde_json", - "winnow 0.6.14", + "winnow 0.6.16", ] [[package]] @@ -308,7 +308,7 @@ checksum = "d83524c1f6162fcb5b0decf775498a125066c86dda6066ed609531b0e912f85a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -556,7 +556,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -573,7 +573,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", "syn-solidity", "tiny-keccak", ] @@ -591,7 +591,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.71", + "syn 2.0.72", "syn-solidity", ] @@ -602,7 +602,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbcba3ca07cf7975f15d871b721fb18031eec8bce51103907f6dcce00b255d98" dependencies = [ "serde", - "winnow 0.6.14", + "winnow 0.6.16", ] [[package]] @@ -683,7 +683,7 @@ dependencies = [ "alloy-transport", "futures", "http 1.1.0", - "rustls 0.23.11", + "rustls 0.23.12", "serde_json", "tokio", "tokio-tungstenite 0.23.1", @@ -743,9 +743,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.14" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", @@ -758,33 +758,33 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -1075,9 +1075,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd066d0b4ef8ecb03a55319dc13aa6910616d0f44008a045bb1835af830abff5" +checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa" dependencies = [ "flate2", "futures-core", @@ -1103,7 +1103,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1125,7 +1125,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1136,7 +1136,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1183,7 +1183,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1260,9 +1260,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.36.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bf39203c9fa4b177c5b58ebf19fc97bbfece1e17c3171c7c5e356ca5f6ea42c" +checksum = "d91f43512620f4b0d9e67ccf7d768fab5ed310ac2229ebb9422177abe99c36ba" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1282,9 +1282,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.35.0" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc3ef4ee9cdd19ec6e8b10d963b79637844bbf41c31177b77a188eaa941e69f7" +checksum = "6acca681c53374bf1d9af0e317a41d12a44902ca0f2d1e10e5cb5bb98ed74f35" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1304,9 +1304,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.36.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527f3da450ea1f09f95155dba6153bd0d83fe0923344a12e1944dfa5d0b32064" +checksum = "b79c6bdfe612503a526059c05c9ccccbf6bd9530b003673cb863e547fd7c0c9a" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1326,9 +1326,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.35.0" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94316606a4aa2cb7a302388411b8776b3fbd254e8506e2dc43918286d8212e9b" +checksum = "32e6ecdb2bd756f3b2383e6f0588dc10a4e65f5d551e70a56e0bfe0c884673ce" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1742,9 +1742,9 @@ dependencies = [ [[package]] name = "blst" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62dc83a094a71d43eeadd254b1ec2d24cb6a0bb6cadce00df51f0db594711a32" +checksum = "4378725facc195f1a538864863f6de233b500a8862747e7f165078a419d5e874" dependencies = [ "cc", "glob", @@ -1764,20 +1764,9 @@ dependencies = [ [[package]] name = "bstr" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" -dependencies = [ - "lazy_static", - "memchr", - "regex-automata 0.1.10", -] - -[[package]] -name = "bstr" -version = "1.9.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", "regex-automata 0.4.7", @@ -2092,9 +2081,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.9" +version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" +checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3" dependencies = [ "clap_builder", "clap_derive", @@ -2102,9 +2091,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.9" +version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" +checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa" dependencies = [ "anstream", "anstyle", @@ -2117,18 +2106,18 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.8" +version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b4be9c4c4b1f30b78d8a750e0822b6a6102d97e62061c583a6c1dea2dfb33ae" +checksum = "c6ae69fbb0833c6fcd5a8d4b8609f108c7ad95fc11e248d853ff2c42a90df26a" dependencies = [ "clap", ] [[package]] name = "clap_complete_fig" -version = "4.5.1" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4bc503cddc1cd320736fb555d6598309ad07c2ddeaa23891a10ffb759ee612" +checksum = "d494102c8ff3951810c72baf96910b980fb065ca5d3101243e6a8dc19747c86b" dependencies = [ "clap", "clap_complete", @@ -2136,21 +2125,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.8" +version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" +checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] name = "clap_lex" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "clearscreen" @@ -2279,9 +2268,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "comfy-table" @@ -2486,7 +2475,7 @@ dependencies = [ "bitflags 2.6.0", "crossterm_winapi", "libc", - "mio", + "mio 0.8.11", "parking_lot", "signal-hook", "signal-hook-mio", @@ -2570,7 +2559,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2581,7 +2570,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2653,7 +2642,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2674,7 +2663,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2684,7 +2673,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2697,7 +2686,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.0", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2804,7 +2793,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2940,14 +2929,14 @@ checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] name = "env_filter" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" dependencies = [ "log", "regex", @@ -2955,9 +2944,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.3" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" dependencies = [ "anstream", "anstyle", @@ -3090,8 +3079,8 @@ dependencies = [ "regex", "serde", "serde_json", - "syn 2.0.71", - "toml 0.8.15", + "syn 2.0.72", + "toml 0.8.16", "walkdir", ] @@ -3118,7 +3107,7 @@ dependencies = [ "serde", "serde_json", "strum", - "syn 2.0.71", + "syn 2.0.72", "tempfile", "thiserror", "tiny-keccak", @@ -3148,9 +3137,9 @@ dependencies = [ [[package]] name = "evmole" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce047d502545e3a726948bb8a532b8ea1446238f829e01448c802b2f10edbe70" +checksum = "628b1881ed2b7f83a32418f6de735c49292065dfc225067687ee1d4fc59a134e" dependencies = [ "ruint", ] @@ -3242,7 +3231,7 @@ dependencies = [ "pear", "serde", "tempfile", - "toml 0.8.15", + "toml 0.8.16", "uncased", "version_check", ] @@ -3387,8 +3376,8 @@ dependencies = [ "thiserror", "tikv-jemallocator", "tokio", - "toml 0.8.15", - "toml_edit 0.22.16", + "toml 0.8.16", + "toml_edit 0.22.17", "tower-http", "tracing", "tracing-subscriber", @@ -3419,7 +3408,7 @@ dependencies = [ "serde_json", "solang-parser", "thiserror", - "toml 0.8.15", + "toml 0.8.16", "tracing", ] @@ -3434,7 +3423,7 @@ dependencies = [ "similar-asserts", "solang-parser", "thiserror", - "toml 0.8.15", + "toml 0.8.16", "tracing", "tracing-subscriber", ] @@ -3496,7 +3485,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -3597,7 +3586,7 @@ dependencies = [ "semver 1.0.23", "serde_json", "thiserror", - "toml 0.8.15", + "toml 0.8.16", "tracing", "vergen", "walkdir", @@ -3750,7 +3739,7 @@ dependencies = [ "thiserror", "tokio", "tracing", - "winnow 0.6.14", + "winnow 0.6.16", "yansi", ] @@ -3856,8 +3845,8 @@ dependencies = [ "solang-parser", "tempfile", "thiserror", - "toml 0.8.15", - "toml_edit 0.22.16", + "toml 0.8.16", + "toml_edit 0.22.17", "tracing", "walkdir", ] @@ -4066,7 +4055,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -4225,7 +4214,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -4309,16 +4298,15 @@ dependencies = [ [[package]] name = "generator" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186014d53bc231d0090ef8d6f03e0920c54d85a5ed22f4f2f74315ec56cf83fb" +checksum = "979f00864edc7516466d6b3157706e06c032f22715700ddd878228a91d02bc56" dependencies = [ - "cc", "cfg-if", "libc", "log", "rustversion", - "windows 0.54.0", + "windows 0.58.0", ] [[package]] @@ -4353,16 +4341,16 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "gix-actor" -version = "0.31.4" +version = "0.31.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b8ee65074b2bbb91d9d97c15d172ea75043aefebf9869b5b329149dc76501c" +checksum = "a0e454357e34b833cc3a00b6efbbd3dd4d18b24b9fb0c023876ec2645e8aa3f2" dependencies = [ - "bstr 1.9.1", + "bstr", "gix-date", "gix-utils", "itoa", "thiserror", - "winnow 0.6.14", + "winnow 0.6.16", ] [[package]] @@ -4371,7 +4359,7 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7580e05996e893347ad04e1eaceb92e1c0e6a3ffe517171af99bf6b6df0ca6e5" dependencies = [ - "bstr 1.9.1", + "bstr", "gix-config-value", "gix-features", "gix-glob", @@ -4383,17 +4371,17 @@ dependencies = [ "smallvec", "thiserror", "unicode-bom", - "winnow 0.6.14", + "winnow 0.6.16", ] [[package]] name = "gix-config-value" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbd06203b1a9b33a78c88252a625031b094d9e1b647260070c25b09910c0a804" +checksum = "b328997d74dd15dc71b2773b162cb4af9a25c424105e4876e6d0686ab41c383e" dependencies = [ "bitflags 2.6.0", - "bstr 1.9.1", + "bstr", "gix-path", "libc", "thiserror", @@ -4405,7 +4393,7 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9eed6931f21491ee0aeb922751bd7ec97b4b2fe8fbfedcb678e2a2dce5f3b8c0" dependencies = [ - "bstr 1.9.1", + "bstr", "itoa", "thiserror", "time", @@ -4438,12 +4426,12 @@ dependencies = [ [[package]] name = "gix-glob" -version = "0.16.3" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a29ad0990cf02c48a7aac76ed0dbddeb5a0d070034b83675cc3bbf937eace4" +checksum = "fa7df15afa265cc8abe92813cd354d522f1ac06b29ec6dfa163ad320575cb447" dependencies = [ "bitflags 2.6.0", - "bstr 1.9.1", + "bstr", "gix-features", "gix-path", ] @@ -4475,7 +4463,7 @@ version = "0.42.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25da2f46b4e7c2fa7b413ce4dffb87f69eaf89c2057e386491f4c55cadbfe386" dependencies = [ - "bstr 1.9.1", + "bstr", "gix-actor", "gix-date", "gix-features", @@ -4485,7 +4473,7 @@ dependencies = [ "itoa", "smallvec", "thiserror", - "winnow 0.6.14", + "winnow 0.6.16", ] [[package]] @@ -4494,7 +4482,7 @@ version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d23d5bbda31344d8abc8de7c075b3cf26e5873feba7c4a15d916bce67382bd9" dependencies = [ - "bstr 1.9.1", + "bstr", "gix-trace", "home", "once_cell", @@ -4520,14 +4508,14 @@ dependencies = [ "gix-validate", "memmap2", "thiserror", - "winnow 0.6.14", + "winnow 0.6.16", ] [[package]] name = "gix-sec" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fddc27984a643b20dd03e97790555804f98cf07404e0e552c0ad8133266a79a1" +checksum = "1547d26fa5693a7f34f05b4a3b59a90890972922172653bcb891ab3f09f436df" dependencies = [ "bitflags 2.6.0", "gix-path", @@ -4570,7 +4558,7 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82c27dd34a49b1addf193c92070bcbf3beaf6e10f16a78544de6372e146a0acf" dependencies = [ - "bstr 1.9.1", + "bstr", "thiserror", ] @@ -4587,7 +4575,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" dependencies = [ "aho-corasick", - "bstr 1.9.1", + "bstr", "log", "regex-automata 0.4.7", "regex-syntax 0.8.4", @@ -4732,7 +4720,7 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -4895,7 +4883,7 @@ dependencies = [ "http 1.1.0", "hyper 1.4.1", "hyper-util", - "rustls 0.23.11", + "rustls 0.23.12", "rustls-native-certs 0.7.1", "rustls-pki-types", "tokio", @@ -5149,9 +5137,9 @@ dependencies = [ [[package]] name = "interprocess" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67bafc2f5dbdad79a6d925649758d5472647b416028099f0b829d1b67fdd47d3" +checksum = "d2f4e4a06d42fab3e85ab1b419ad32b09eab58b901d40c57935ff92db3287a13" dependencies = [ "doctest-file", "futures-core", @@ -5181,9 +5169,9 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.0" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" @@ -5229,9 +5217,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] @@ -5595,7 +5583,7 @@ checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -5641,6 +5629,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "mio" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" +dependencies = [ + "hermit-abi", + "libc", + "wasi", + "windows-sys 0.52.0", +] + [[package]] name = "mockall" version = "0.12.1" @@ -5665,7 +5665,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -5770,7 +5770,7 @@ dependencies = [ "kqueue", "libc", "log", - "mio", + "mio 0.8.11", "walkdir", "windows-sys 0.48.0", ] @@ -5903,7 +5903,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -5986,7 +5986,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8df34be653210fbe9ffaff41d3b92721c56ce82dfee58ee684f9afb5e3a90c0" dependencies = [ - "bstr 1.9.1", + "bstr", "dbus", "normpath", "windows-sys 0.52.0", @@ -5994,9 +5994,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.65" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2823eb4c6453ed64055057ea8bd416eda38c71018723869dd043a3b1186115e" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ "bitflags 2.6.0", "cfg-if", @@ -6015,7 +6015,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -6201,7 +6201,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -6260,7 +6260,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -6344,7 +6344,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -6402,7 +6402,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -6487,9 +6487,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "predicates" -version = "3.1.0" +version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" +checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" dependencies = [ "anstyle", "predicates-core", @@ -6497,15 +6497,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" +checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" [[package]] name = "predicates-tree" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" dependencies = [ "predicates-core", "termtree", @@ -6518,7 +6518,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -6594,7 +6594,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", "version_check", "yansi", ] @@ -6691,7 +6691,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -6701,10 +6701,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18bec9b0adc4eba778b33684b7ba3e7137789434769ee3ce3930463ef904cfca" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -6789,7 +6789,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash 1.1.0", - "rustls 0.23.11", + "rustls 0.23.12", "thiserror", "tokio", "tracing", @@ -6805,7 +6805,7 @@ dependencies = [ "rand", "ring", "rustc-hash 1.1.0", - "rustls 0.23.11", + "rustls 0.23.12", "slab", "thiserror", "tinyvec", @@ -6814,14 +6814,13 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9096629c45860fc7fb143e125eb826b5e721e10be3263160c7d60ca832cf8c46" +checksum = "8bffec3605b73c6f1754535084a85229fa8a30f86014e6c81aeec4abb68b0285" dependencies = [ "libc", "once_cell", "socket2", - "tracing", "windows-sys 0.52.0", ] @@ -7087,7 +7086,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.11", + "rustls 0.23.12", "rustls-native-certs 0.7.1", "rustls-pemfile 2.1.2", "rustls-pki-types", @@ -7126,9 +7125,9 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af2dc001e37ac3b061dc9087876aea91e28756c188a97cd99416d23a5562ca73" +checksum = "5296ccad8d7ccbeb6c5a037a57bfe1ff27e81d8c4efbd3ae7df0a554eb1a818a" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -7388,21 +7387,21 @@ dependencies = [ "log", "ring", "rustls-pki-types", - "rustls-webpki 0.102.5", + "rustls-webpki 0.102.6", "subtle", "zeroize", ] [[package]] name = "rustls" -version = "0.23.11" +version = "0.23.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4828ea528154ae444e5a642dbb7d5623354030dc9822b83fd9bb79683c7399d0" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.102.5", + "rustls-webpki 0.102.6", "subtle", "zeroize", ] @@ -7469,9 +7468,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.5" +version = "0.102.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78" +checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" dependencies = [ "ring", "rustls-pki-types", @@ -7569,9 +7568,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.1.4" +version = "2.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4465c22496331e20eb047ff46e7366455bc01c0c02015c4a376de0b2cd3a1af" +checksum = "1fadf67e3cf23f8b11a6c8c48a16cb2437381503615acd91094ec7b4686a5a53" dependencies = [ "sdd", ] @@ -7606,7 +7605,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -7768,7 +7767,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -7779,7 +7778,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -7822,14 +7821,14 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] name = "serde_spanned" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" dependencies = [ "serde", ] @@ -7881,7 +7880,7 @@ checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -7897,9 +7896,9 @@ dependencies = [ [[package]] name = "sha1_smol" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" [[package]] name = "sha2" @@ -7983,7 +7982,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" dependencies = [ "libc", - "mio", + "mio 0.8.11", "signal-hook", ] @@ -8014,11 +8013,11 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "similar" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" +checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" dependencies = [ - "bstr 0.2.17", + "bstr", "unicode-segmentation", ] @@ -8076,9 +8075,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "snapbox" -version = "0.6.13" +version = "0.6.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d656960fa127e80ade23c321d8c573bb9ba462c3a69e62ede635fc180ffc6cc" +checksum = "027c936207f85d10d015e21faf5c676c7e08c453ed371adf55c0874c443ca77a" dependencies = [ "anstream", "anstyle", @@ -8091,9 +8090,9 @@ dependencies = [ [[package]] name = "snapbox-macros" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f4c14672714436c09254801c934b203196a51182a5107fb76591c7cc56424d" +checksum = "16569f53ca23a41bb6f62e0a5084aa1661f4814a67fa33696a79073e03a664af" dependencies = [ "anstream", ] @@ -8142,8 +8141,8 @@ dependencies = [ "sha256", "simple-home-dir", "tokio", - "toml 0.8.15", - "toml_edit 0.22.16", + "toml 0.8.16", + "toml_edit 0.22.17", "uuid 1.10.0", "walkdir", "yansi", @@ -8175,7 +8174,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac" dependencies = [ "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -8241,7 +8240,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -8309,9 +8308,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.71" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -8327,7 +8326,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -8449,7 +8448,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -8560,21 +8559,20 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.38.1" +version = "1.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df" +checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" dependencies = [ "backtrace", "bytes", "libc", - "mio", - "num_cpus", + "mio 1.0.1", "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -8589,13 +8587,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -8635,7 +8633,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.11", + "rustls 0.23.12", "rustls-pki-types", "tokio", ] @@ -8672,7 +8670,7 @@ checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" dependencies = [ "futures-util", "log", - "rustls 0.23.11", + "rustls 0.23.12", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -8704,22 +8702,22 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac2caab0bf757388c6c0ae23b3293fdb463fee59434529014f85e3263b995c28" +checksum = "81967dd0dd2c1ab0bc3468bd7caecc32b8a4aa47d0c8c695d8c2b2108168d62c" dependencies = [ "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.16", + "toml_edit 0.22.17", ] [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db" dependencies = [ "serde", ] @@ -8737,15 +8735,15 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.16" +version = "0.22.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788" +checksum = "8d9f8729f5aea9562aac1cc0441f5d6de3cff1ee0c5d67293eeca5eb36ee7c16" dependencies = [ "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.14", + "winnow 0.6.16", ] [[package]] @@ -8874,7 +8872,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -9009,7 +9007,7 @@ dependencies = [ "httparse", "log", "rand", - "rustls 0.23.11", + "rustls 0.23.12", "rustls-pki-types", "sha1", "thiserror", @@ -9208,9 +9206,9 @@ dependencies = [ [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vsimd" @@ -9273,7 +9271,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", "wasm-bindgen-shared", ] @@ -9307,7 +9305,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -9464,21 +9462,21 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.54.0" +version = "0.56.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" +checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132" dependencies = [ - "windows-core 0.54.0", + "windows-core 0.56.0", "windows-targets 0.52.6", ] [[package]] name = "windows" -version = "0.56.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ - "windows-core 0.56.0", + "windows-core 0.58.0", "windows-targets 0.52.6", ] @@ -9493,23 +9491,26 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.54.0" +version = "0.56.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6" dependencies = [ - "windows-result", + "windows-implement 0.56.0", + "windows-interface 0.56.0", + "windows-result 0.1.2", "windows-targets 0.52.6", ] [[package]] name = "windows-core" -version = "0.56.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings", "windows-targets 0.52.6", ] @@ -9521,7 +9522,18 @@ checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", ] [[package]] @@ -9532,7 +9544,18 @@ checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", ] [[package]] @@ -9544,6 +9567,25 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -9694,9 +9736,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.14" +version = "0.6.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374ec40a2d767a3c1b4972d9475ecd557356637be906f2cb3f7fe17a6eb5e22f" +checksum = "b480ae9340fc261e6be3e95a1ba86d54ae3f9171132a73ce8d4bbaf68339507c" dependencies = [ "memchr", ] @@ -9798,7 +9840,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -9818,7 +9860,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] From 741db53f3dfdcbe8c935e52492e5a0b7058f4169 Mon Sep 17 00:00:00 2001 From: "Nathan H. Leung" Date: Sun, 28 Jul 2024 02:20:28 -0700 Subject: [PATCH 021/184] fix(cheatcodes): clarify vm.expectRevert error message (#8463) --- crates/cheatcodes/src/test/expect.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs index 4dc535508..80d2417ec 100644 --- a/crates/cheatcodes/src/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -555,7 +555,7 @@ pub(crate) fn handle_expect_revert( } }; - ensure!(!matches!(status, return_ok!()), "call did not revert as expected"); + ensure!(!matches!(status, return_ok!()), "next call did not revert as expected"); // If None, accept any revert let Some(expected_revert) = expected_revert else { From 682286017eea36ee6309fc659a41167f265c56db Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sun, 28 Jul 2024 12:18:09 +0200 Subject: [PATCH 022/184] fix(cheatcodes): get artifact code panic (#8546) --- crates/cheatcodes/src/fs.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 129f8a22e..177037c90 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -387,10 +387,10 @@ fn get_artifact_code(state: &Cheatcodes, path: &str, deployed: bool) -> Result>(); - let artifact = match filtered.len() { - 0 => Err(fmt_err!("No matching artifact found")), - 1 => Ok(filtered[0]), - _ => { + let artifact = match &filtered[..] { + [] => Err(fmt_err!("No matching artifact found")), + [artifact] => Ok(artifact), + filtered => { // If we know the current script/test contract solc version, try to filter by it state .config @@ -398,11 +398,10 @@ fn get_artifact_code(state: &Cheatcodes, path: &str, deployed: bool) -> Result>(); - - (filtered.len() == 1).then_some(filtered[0]) + (filtered.len() == 1).then(|| filtered[0]) }) .ok_or_else(|| fmt_err!("Multiple matching artifacts found")) } From adc2132633b873a66d87831e503ba4d1f61d2100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Vincent?= <28714795+leovct@users.noreply.github.com> Date: Mon, 29 Jul 2024 10:34:19 +0200 Subject: [PATCH 023/184] feat(cheatcodes): implement EIP-2098 in `vm.sign` (#8538) * feat: implement EIP-2098 in vm.sign * fix: clippy * test: fix * fix: forge-fmt * chore: implement new cheatcode vm.signEIP2098 * fix: typos * fix: another typo * fix * test: update sign tests * fix: typo * chore: rename `vm.signEIP2098()` into `vm.signCompact()` * chore: update `encode_compact_signature` impl * chore: update `signCompact` tests * chore: factorize * chore: rename fns * chore: nit * fix: issue * chore: nits --- crates/cheatcodes/assets/cheatcodes.json | 80 ++++++++++++++++++++++++ crates/cheatcodes/spec/src/vm.rs | 40 ++++++++++++ crates/cheatcodes/src/evm.rs | 33 +++++++++- crates/cheatcodes/src/utils.rs | 35 ++++++++--- testdata/cheats/Vm.sol | 4 ++ testdata/default/cheats/Sign.t.sol | 24 +++++++ testdata/default/cheats/Wallet.t.sol | 26 ++++++++ 7 files changed, 231 insertions(+), 11 deletions(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 61f626477..c66f3a5ec 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -7751,6 +7751,86 @@ "status": "stable", "safety": "unsafe" }, + { + "func": { + "id": "signCompact_0", + "description": "Signs `digest` with `privateKey` using the secp256k1 curve.\nReturns a compact signature (`r`, `vs`) as per EIP-2098, where `vs` encodes both the\nsignature's `s` value, and the recovery id `v` in a single bytes32.\nThis format reduces the signature size from 65 to 64 bytes.", + "declaration": "function signCompact(uint256 privateKey, bytes32 digest) external pure returns (bytes32 r, bytes32 vs);", + "visibility": "external", + "mutability": "pure", + "signature": "signCompact(uint256,bytes32)", + "selector": "0xcc2a781f", + "selectorBytes": [ + 204, + 42, + 120, + 31 + ] + }, + "group": "evm", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "signCompact_1", + "description": "Signs `digest` with signer provided to script using the secp256k1 curve.\nReturns a compact signature (`r`, `vs`) as per EIP-2098, where `vs` encodes both the\nsignature's `s` value, and the recovery id `v` in a single bytes32.\nThis format reduces the signature size from 65 to 64 bytes.\nIf `--sender` is provided, the signer with provided address is used, otherwise,\nif exactly one signer is provided to the script, that signer is used.\nRaises error if signer passed through `--sender` does not match any unlocked signers or\nif `--sender` is not provided and not exactly one signer is passed to the script.", + "declaration": "function signCompact(bytes32 digest) external pure returns (bytes32 r, bytes32 vs);", + "visibility": "external", + "mutability": "pure", + "signature": "signCompact(bytes32)", + "selector": "0xa282dc4b", + "selectorBytes": [ + 162, + 130, + 220, + 75 + ] + }, + "group": "evm", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "signCompact_2", + "description": "Signs `digest` with signer provided to script using the secp256k1 curve.\nReturns a compact signature (`r`, `vs`) as per EIP-2098, where `vs` encodes both the\nsignature's `s` value, and the recovery id `v` in a single bytes32.\nThis format reduces the signature size from 65 to 64 bytes.\nRaises error if none of the signers passed into the script have provided address.", + "declaration": "function signCompact(address signer, bytes32 digest) external pure returns (bytes32 r, bytes32 vs);", + "visibility": "external", + "mutability": "pure", + "signature": "signCompact(address,bytes32)", + "selector": "0x8e2f97bf", + "selectorBytes": [ + 142, + 47, + 151, + 191 + ] + }, + "group": "evm", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "signCompact_3", + "description": "Signs data with a `Wallet`.\nReturns a compact signature (`r`, `vs`) as per EIP-2098, where `vs` encodes both the\nsignature's `s` value, and the recovery id `v` in a single bytes32.\nThis format reduces the signature size from 65 to 64 bytes.", + "declaration": "function signCompact(Wallet calldata wallet, bytes32 digest) external returns (bytes32 r, bytes32 vs);", + "visibility": "external", + "mutability": "", + "signature": "signCompact((address,uint256,uint256,uint256),bytes32)", + "selector": "0x3d0e292f", + "selectorBytes": [ + 61, + 14, + 41, + 47 + ] + }, + "group": "utilities", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "signP256", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 935d44e53..685a6f751 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -286,6 +286,14 @@ interface Vm { #[cheatcode(group = Evm, safety = Safe)] function sign(uint256 privateKey, bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); + /// Signs `digest` with `privateKey` using the secp256k1 curve. + /// + /// Returns a compact signature (`r`, `vs`) as per EIP-2098, where `vs` encodes both the + /// signature's `s` value, and the recovery id `v` in a single bytes32. + /// This format reduces the signature size from 65 to 64 bytes. + #[cheatcode(group = Evm, safety = Safe)] + function signCompact(uint256 privateKey, bytes32 digest) external pure returns (bytes32 r, bytes32 vs); + /// Signs `digest` with signer provided to script using the secp256k1 curve. /// /// If `--sender` is provided, the signer with provided address is used, otherwise, @@ -296,12 +304,36 @@ interface Vm { #[cheatcode(group = Evm, safety = Safe)] function sign(bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); + /// Signs `digest` with signer provided to script using the secp256k1 curve. + /// + /// Returns a compact signature (`r`, `vs`) as per EIP-2098, where `vs` encodes both the + /// signature's `s` value, and the recovery id `v` in a single bytes32. + /// This format reduces the signature size from 65 to 64 bytes. + /// + /// If `--sender` is provided, the signer with provided address is used, otherwise, + /// if exactly one signer is provided to the script, that signer is used. + /// + /// Raises error if signer passed through `--sender` does not match any unlocked signers or + /// if `--sender` is not provided and not exactly one signer is passed to the script. + #[cheatcode(group = Evm, safety = Safe)] + function signCompact(bytes32 digest) external pure returns (bytes32 r, bytes32 vs); + /// Signs `digest` with signer provided to script using the secp256k1 curve. /// /// Raises error if none of the signers passed into the script have provided address. #[cheatcode(group = Evm, safety = Safe)] function sign(address signer, bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); + /// Signs `digest` with signer provided to script using the secp256k1 curve. + /// + /// Returns a compact signature (`r`, `vs`) as per EIP-2098, where `vs` encodes both the + /// signature's `s` value, and the recovery id `v` in a single bytes32. + /// This format reduces the signature size from 65 to 64 bytes. + /// + /// Raises error if none of the signers passed into the script have provided address. + #[cheatcode(group = Evm, safety = Safe)] + function signCompact(address signer, bytes32 digest) external pure returns (bytes32 r, bytes32 vs); + /// Signs `digest` with `privateKey` using the secp256r1 curve. #[cheatcode(group = Evm, safety = Safe)] function signP256(uint256 privateKey, bytes32 digest) external pure returns (bytes32 r, bytes32 s); @@ -2153,6 +2185,14 @@ interface Vm { #[cheatcode(group = Utilities)] function sign(Wallet calldata wallet, bytes32 digest) external returns (uint8 v, bytes32 r, bytes32 s); + /// Signs data with a `Wallet`. + /// + /// Returns a compact signature (`r`, `vs`) as per EIP-2098, where `vs` encodes both the + /// signature's `s` value, and the recovery id `v` in a single bytes32. + /// This format reduces the signature size from 65 to 64 bytes. + #[cheatcode(group = Utilities)] + function signCompact(Wallet calldata wallet, bytes32 digest) external returns (bytes32 r, bytes32 vs); + /// Derive a private key from a provided mnenomic string (or mnenomic file path) /// at the derivation path `m/44'/60'/0'/0/{index}`. #[cheatcode(group = Utilities)] diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 97923a948..5e24070a8 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -162,21 +162,48 @@ impl Cheatcode for dumpStateCall { impl Cheatcode for sign_0Call { fn apply_stateful(&self, _: &mut CheatsCtxt) -> Result { let Self { privateKey, digest } = self; - super::utils::sign(privateKey, digest) + let sig = super::utils::sign(privateKey, digest)?; + Ok(super::utils::encode_full_sig(sig)) + } +} + +impl Cheatcode for signCompact_0Call { + fn apply_stateful(&self, _: &mut CheatsCtxt) -> Result { + let Self { privateKey, digest } = self; + let sig = super::utils::sign(privateKey, digest)?; + Ok(super::utils::encode_compact_sig(sig)) } } impl Cheatcode for sign_1Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { digest } = self; - super::utils::sign_with_wallet(ccx, None, digest) + let sig = super::utils::sign_with_wallet(ccx, None, digest)?; + Ok(super::utils::encode_full_sig(sig)) + } +} + +impl Cheatcode for signCompact_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { digest } = self; + let sig = super::utils::sign_with_wallet(ccx, None, digest)?; + Ok(super::utils::encode_compact_sig(sig)) } } impl Cheatcode for sign_2Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { signer, digest } = self; - super::utils::sign_with_wallet(ccx, Some(*signer), digest) + let sig = super::utils::sign_with_wallet(ccx, Some(*signer), digest)?; + Ok(super::utils::encode_full_sig(sig)) + } +} + +impl Cheatcode for signCompact_2Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { signer, digest } = self; + let sig = super::utils::sign_with_wallet(ccx, Some(*signer), digest)?; + Ok(super::utils::encode_compact_sig(sig)) } } diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index 08e255341..67b154ff2 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -55,7 +55,16 @@ impl Cheatcode for getNonce_1Call { impl Cheatcode for sign_3Call { fn apply_stateful(&self, _: &mut CheatsCtxt) -> Result { let Self { wallet, digest } = self; - sign(&wallet.privateKey, digest) + let sig = sign(&wallet.privateKey, digest)?; + Ok(encode_full_sig(sig)) + } +} + +impl Cheatcode for signCompact_3Call { + fn apply_stateful(&self, _: &mut CheatsCtxt) -> Result { + let Self { wallet, digest } = self; + let sig = sign(&wallet.privateKey, digest)?; + Ok(encode_compact_sig(sig)) } } @@ -201,25 +210,35 @@ fn create_wallet(private_key: &U256, label: Option<&str>, state: &mut Cheatcodes .abi_encode()) } -fn encode_vrs(sig: alloy_primitives::Signature) -> Vec { - let v = sig.v().y_parity_byte_non_eip155().unwrap_or(sig.v().y_parity_byte()); +pub(super) fn encode_full_sig(sig: alloy_primitives::Signature) -> Vec { + // Retrieve v, r and s from signature. + let v = U256::from(sig.v().y_parity_byte_non_eip155().unwrap_or(sig.v().y_parity_byte())); + let r = B256::from(sig.r()); + let s = B256::from(sig.s()); + (v, r, s).abi_encode() +} - (U256::from(v), B256::from(sig.r()), B256::from(sig.s())).abi_encode() +pub(super) fn encode_compact_sig(sig: alloy_primitives::Signature) -> Vec { + // Implement EIP-2098 compact signature. + let r = B256::from(sig.r()); + let mut vs = sig.s(); + vs.set_bit(255, sig.v().y_parity()); + (r, vs).abi_encode() } -pub(super) fn sign(private_key: &U256, digest: &B256) -> Result { +pub(super) fn sign(private_key: &U256, digest: &B256) -> Result { // The `ecrecover` precompile does not use EIP-155. No chain ID is needed. let wallet = parse_wallet(private_key)?; let sig = wallet.sign_hash_sync(digest)?; debug_assert_eq!(sig.recover_address_from_prehash(digest)?, wallet.address()); - Ok(encode_vrs(sig)) + Ok(sig) } pub(super) fn sign_with_wallet( ccx: &mut CheatsCtxt, signer: Option
, digest: &B256, -) -> Result { +) -> Result { let Some(script_wallets) = ccx.state.script_wallets() else { bail!("no wallets are available"); }; @@ -244,7 +263,7 @@ pub(super) fn sign_with_wallet( let sig = foundry_common::block_on(wallet.sign_hash(digest))?; debug_assert_eq!(sig.recover_address_from_prehash(digest)?, signer); - Ok(encode_vrs(sig)) + Ok(sig) } pub(super) fn sign_p256(private_key: &U256, digest: &B256, _state: &mut Cheatcodes) -> Result { diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index f42fb2298..4a9d9cd69 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -383,6 +383,10 @@ interface Vm { function setEnv(string calldata name, string calldata value) external; function setNonce(address account, uint64 newNonce) external; function setNonceUnsafe(address account, uint64 newNonce) external; + function signCompact(uint256 privateKey, bytes32 digest) external pure returns (bytes32 r, bytes32 vs); + function signCompact(bytes32 digest) external pure returns (bytes32 r, bytes32 vs); + function signCompact(address signer, bytes32 digest) external pure returns (bytes32 r, bytes32 vs); + function signCompact(Wallet calldata wallet, bytes32 digest) external returns (bytes32 r, bytes32 vs); function signP256(uint256 privateKey, bytes32 digest) external pure returns (bytes32 r, bytes32 s); function sign(uint256 privateKey, bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); function sign(bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); diff --git a/testdata/default/cheats/Sign.t.sol b/testdata/default/cheats/Sign.t.sol index e46439b58..a257d6291 100644 --- a/testdata/default/cheats/Sign.t.sol +++ b/testdata/default/cheats/Sign.t.sol @@ -13,11 +13,35 @@ contract SignTest is DSTest { (uint8 v, bytes32 r, bytes32 s) = vm.sign(pk, digest); address expected = vm.addr(pk); address actual = ecrecover(digest, v, r, s); + assertEq(actual, expected, "digest signer did not match"); + } + + function testSignCompactDigest(uint248 pk, bytes32 digest) public { + vm.assume(pk != 0); + + (bytes32 r, bytes32 vs) = vm.signCompact(pk, digest); + + // Extract `s` from `vs`. + // Shift left by 1 bit to clear the leftmost bit, then shift right by 1 bit to restore the original position. + // This effectively clears the leftmost bit of `vs`, giving us `s`. + bytes32 s = bytes32((uint256(vs) << 1) >> 1); + // Extract `v` from `vs`. + // We shift `vs` right by 255 bits to isolate the leftmost bit. + // Converting this to uint8 gives us the parity bit (0 or 1). + // Adding 27 converts this parity bit to the correct `v` value (27 or 28). + uint8 v = uint8(uint256(vs) >> 255) + 27; + + address expected = vm.addr(pk); + address actual = ecrecover(digest, v, r, s); assertEq(actual, expected, "digest signer did not match"); } function testSignMessage(uint248 pk, bytes memory message) public { testSignDigest(pk, keccak256(message)); } + + function testSignCompactMessage(uint248 pk, bytes memory message) public { + testSignCompactDigest(pk, keccak256(message)); + } } diff --git a/testdata/default/cheats/Wallet.t.sol b/testdata/default/cheats/Wallet.t.sol index 8ecb707ae..5a7035876 100644 --- a/testdata/default/cheats/Wallet.t.sol +++ b/testdata/default/cheats/Wallet.t.sol @@ -104,10 +104,36 @@ contract WalletTest is DSTest { assertEq(recovered, wallet.addr); } + function testSignCompactWithWalletDigest(uint256 pkSeed, bytes32 digest) public { + uint256 pk = bound(pkSeed, 1, Q - 1); + + Vm.Wallet memory wallet = vm.createWallet(pk); + + (bytes32 r, bytes32 vs) = vm.signCompact(wallet, digest); + + // Extract `s` from `vs`. + // Shift left by 1 bit to clear the leftmost bit, then shift right by 1 bit to restore the original position. + // This effectively clears the leftmost bit of `vs`, giving us `s`. + bytes32 s = bytes32((uint256(vs) << 1) >> 1); + + // Extract `v` from `vs`. + // We shift `vs` right by 255 bits to isolate the leftmost bit. + // Converting this to uint8 gives us the parity bit (0 or 1). + // Adding 27 converts this parity bit to the correct `v` value (27 or 28). + uint8 v = uint8(uint256(vs) >> 255) + 27; + + address recovered = ecrecover(digest, v, r, s); + assertEq(recovered, wallet.addr); + } + function testSignWithWalletMessage(uint256 pkSeed, bytes memory message) public { testSignWithWalletDigest(pkSeed, keccak256(message)); } + function testSignCompactWithWalletMessage(uint256 pkSeed, bytes memory message) public { + testSignCompactWithWalletDigest(pkSeed, keccak256(message)); + } + function testGetNonceWallet(uint256 pkSeed) public { uint256 pk = bound(pkSeed, 1, Q - 1); From 108b8d1d2426424a4a1ced998db5a8640fd5ab8d Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 29 Jul 2024 17:34:46 +0800 Subject: [PATCH 024/184] fix(`verify-bytecode`): extract constructor arguments from creation code (#8547) fix verify-bytecode: extract constructor arguments from creation code --- crates/verify/src/bytecode.rs | 109 ++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 51 deletions(-) diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index a0b3a7c84..5834e961f 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -147,6 +147,44 @@ impl VerifyBytecodeArgs { }; let etherscan = Client::new(chain, key)?; + // Get creation tx hash + let creation_data = etherscan.contract_creation_data(self.address).await?; + + trace!(creation_tx_hash = ?creation_data.transaction_hash); + let mut transaction = provider + .get_transaction_by_hash(creation_data.transaction_hash) + .await + .or_else(|e| eyre::bail!("Couldn't fetch transaction from RPC: {:?}", e))? + .ok_or_else(|| { + eyre::eyre!("Transaction not found for hash {}", creation_data.transaction_hash) + })?; + let receipt = provider + .get_transaction_receipt(creation_data.transaction_hash) + .await + .or_else(|e| eyre::bail!("Couldn't fetch transaction receipt from RPC: {:?}", e))?; + + let receipt = if let Some(receipt) = receipt { + receipt + } else { + eyre::bail!( + "Receipt not found for transaction hash {}", + creation_data.transaction_hash + ); + }; + + // Extract creation code + let maybe_creation_code = + if receipt.to.is_none() && receipt.contract_address == Some(self.address) { + &transaction.input + } else if receipt.to == Some(DEFAULT_CREATE2_DEPLOYER) { + &transaction.input[32..] + } else { + eyre::bail!( + "Could not extract the creation code for contract at address {}", + self.address + ); + }; + // Get the constructor args using `source_code` endpoint let source_code = etherscan.contract_source_code(self.address).await?; @@ -168,6 +206,11 @@ impl VerifyBytecodeArgs { self.build_project(&config)? }; + let local_bytecode = artifact + .bytecode + .and_then(|b| b.into_bytes()) + .ok_or_eyre("Unlinked bytecode is not supported for verification")?; + // Get the constructor args from etherscan let mut constructor_args = if let Some(args) = source_code.items.first() { args.constructor_arguments.clone() @@ -201,63 +244,27 @@ impl VerifyBytecodeArgs { .or(self.encoded_constructor_args.to_owned().map(hex::decode).transpose()?); if let Some(provided) = provided_constructor_args { - if provided != constructor_args && !self.json { - println!( - "{}", - format!("The provided constructor args do not match the constructor args from etherscan ({constructor_args}).") - .yellow() - .bold(), - ); - } constructor_args = provided.into(); - } - - // Get creation tx hash - let creation_data = etherscan.contract_creation_data(self.address).await?; - - trace!(creation_tx_hash = ?creation_data.transaction_hash); - let mut transaction = provider - .get_transaction_by_hash(creation_data.transaction_hash) - .await - .or_else(|e| eyre::bail!("Couldn't fetch transaction from RPC: {:?}", e))? - .ok_or_else(|| { - eyre::eyre!("Transaction not found for hash {}", creation_data.transaction_hash) - })?; - let receipt = provider - .get_transaction_receipt(creation_data.transaction_hash) - .await - .or_else(|e| eyre::bail!("Couldn't fetch transaction receipt from RPC: {:?}", e))?; - - let receipt = if let Some(receipt) = receipt { - receipt } else { - eyre::bail!( - "Receipt not found for transaction hash {}", - creation_data.transaction_hash - ); - }; - - // Extract creation code - let maybe_creation_code = - if receipt.to.is_none() && receipt.contract_address == Some(self.address) { - &transaction.input - } else if receipt.to == Some(DEFAULT_CREATE2_DEPLOYER) { - &transaction.input[32..] - } else { - eyre::bail!( - "Could not extract the creation code for contract at address {}", - self.address - ); - }; + // In some cases, Etherscan will return incorrect constructor arguments. If this + // happens, try extracting arguments ourselves. + if !maybe_creation_code.ends_with(&constructor_args) { + trace!("mismatch of constructor args with etherscan"); + // If local bytecode is longer than on-chain one, this is probably not a match. + if maybe_creation_code.len() >= local_bytecode.len() { + constructor_args = + Bytes::copy_from_slice(&maybe_creation_code[local_bytecode.len()..]); + trace!( + "setting constructor args to latest {} bytes of bytecode", + constructor_args.len() + ); + } + } + } // If bytecode_hash is disabled then its always partial verification let has_metadata = config.bytecode_hash == BytecodeHash::None; - let local_bytecode = artifact - .bytecode - .and_then(|b| b.into_bytes()) - .ok_or_eyre("Unlinked bytecode is not supported for verification")?; - // Append constructor args to the local_bytecode trace!(%constructor_args); let mut local_bytecode_vec = local_bytecode.to_vec(); From 9b1c48d608031d5f7fe14f9314b5dafdc26d7ca6 Mon Sep 17 00:00:00 2001 From: Qiwei Yang Date: Mon, 29 Jul 2024 17:49:59 +0800 Subject: [PATCH 025/184] add `--max-persisted-states` to configure `max_on_disk_limit` (#8412) * set to limit * revert old changes * add max_persisted_states * fix fmt * add tests * fix * fix --------- Co-authored-by: Matthias Seitz --- crates/anvil/src/cmd.rs | 15 ++++++++ crates/anvil/src/config.rs | 14 ++++++++ crates/anvil/src/eth/backend/mem/mod.rs | 9 +++-- crates/anvil/src/eth/backend/mem/storage.rs | 39 ++++++++++++++++----- 4 files changed, 67 insertions(+), 10 deletions(-) diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index e12c6b13a..2897cebdd 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -162,9 +162,17 @@ pub struct NodeArgs { /// Don't keep full chain history. /// If a number argument is specified, at most this number of states is kept in memory. + /// + /// If enabled, no state will be persisted on disk, so `max_persisted_states` will be 0. #[arg(long)] pub prune_history: Option>, + /// Max number of states to persist on disk. + /// + /// Note that `prune_history` will overwrite `max_persisted_states` to 0. + #[arg(long)] + pub max_persisted_states: Option, + /// Number of blocks with transactions to keep in memory. #[arg(long)] pub transaction_block_keeper: Option, @@ -241,6 +249,7 @@ impl NodeArgs { .set_pruned_history(self.prune_history) .with_init_state(self.load_state.or_else(|| self.state.and_then(|s| s.state))) .with_transaction_block_keeper(self.transaction_block_keeper) + .with_max_persisted_states(self.max_persisted_states) .with_optimism(self.evm_opts.optimism) .with_disable_default_create2_deployer(self.evm_opts.disable_default_create2_deployer) .with_slots_in_an_epoch(self.slots_in_an_epoch) @@ -780,6 +789,12 @@ mod tests { assert_eq!(args.prune_history, Some(Some(100))); } + #[test] + fn can_parse_max_persisted_states_config() { + let args: NodeArgs = NodeArgs::parse_from(["anvil", "--max-persisted-states", "500"]); + assert_eq!(args.max_persisted_states, (Some(500))); + } + #[test] fn can_parse_disable_block_gas_limit() { let args: NodeArgs = NodeArgs::parse_from(["anvil", "--disable-block-gas-limit"]); diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 4ae1be0e9..28080f628 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -167,6 +167,8 @@ pub struct NodeConfig { /// /// If set to `Some(num)` keep latest num state in memory only. pub prune_history: PruneStateHistoryConfig, + /// Max number of states cached on disk. + pub max_persisted_states: Option, /// The file where to load the state from pub init_state: Option, /// max number of blocks with transactions in memory @@ -427,6 +429,7 @@ impl Default for NodeConfig { ipc_path: None, code_size_limit: None, prune_history: Default::default(), + max_persisted_states: None, init_state: None, transaction_block_keeper: None, disable_default_create2_deployer: false, @@ -552,6 +555,16 @@ impl NodeConfig { self } + /// Sets max number of states to cache on disk. + #[must_use] + pub fn with_max_persisted_states>( + mut self, + max_persisted_states: Option, + ) -> Self { + self.max_persisted_states = max_persisted_states.map(Into::into); + self + } + /// Sets max number of blocks with transactions to keep in memory #[must_use] pub fn with_transaction_block_keeper>( @@ -974,6 +987,7 @@ impl NodeConfig { self.enable_steps_tracing, self.print_logs, self.prune_history, + self.max_persisted_states, self.transaction_block_keeper, self.block_time, Arc::new(tokio::sync::RwLock::new(self.clone())), diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 804ef74f4..8f51ae04e 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -99,7 +99,7 @@ use std::{ sync::Arc, time::Duration, }; -use storage::{Blockchain, MinedTransaction}; +use storage::{Blockchain, MinedTransaction, DEFAULT_HISTORY_LIMIT}; use tokio::sync::RwLock as AsyncRwLock; pub mod cache; @@ -199,6 +199,7 @@ impl Backend { enable_steps_tracing: bool, print_logs: bool, prune_state_history_config: PruneStateHistoryConfig, + max_persisted_states: Option, transaction_block_keeper: Option, automine_block_time: Option, node_config: Arc>, @@ -225,9 +226,13 @@ impl Backend { // if prune state history is enabled, configure the state cache only for memory prune_state_history_config .max_memory_history - .map(InMemoryBlockStates::new) + .map(|limit| InMemoryBlockStates::new(limit, 0)) .unwrap_or_default() .memory_only() + } else if max_persisted_states.is_some() { + max_persisted_states + .map(|limit| InMemoryBlockStates::new(DEFAULT_HISTORY_LIMIT, limit)) + .unwrap_or_default() } else { Default::default() }; diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 692e669e5..aaa2f29e7 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -40,7 +40,7 @@ use std::{ // === various limits in number of blocks === -const DEFAULT_HISTORY_LIMIT: usize = 500; +pub const DEFAULT_HISTORY_LIMIT: usize = 500; const MIN_HISTORY_LIMIT: usize = 10; // 1hr of up-time at lowest 1s interval const MAX_ON_DISK_HISTORY_LIMIT: usize = 3_600; @@ -69,13 +69,13 @@ pub struct InMemoryBlockStates { impl InMemoryBlockStates { /// Creates a new instance with limited slots - pub fn new(limit: usize) -> Self { + pub fn new(in_memory_limit: usize, on_disk_limit: usize) -> Self { Self { states: Default::default(), on_disk_states: Default::default(), - in_memory_limit: limit, - min_in_memory_limit: limit.min(MIN_HISTORY_LIMIT), - max_on_disk_limit: MAX_ON_DISK_HISTORY_LIMIT, + in_memory_limit, + min_in_memory_limit: in_memory_limit.min(MIN_HISTORY_LIMIT), + max_on_disk_limit: on_disk_limit, oldest_on_disk: Default::default(), present: Default::default(), disk_cache: Default::default(), @@ -205,7 +205,7 @@ impl fmt::Debug for InMemoryBlockStates { impl Default for InMemoryBlockStates { fn default() -> Self { // enough in memory to store `DEFAULT_HISTORY_LIMIT` blocks in memory - Self::new(DEFAULT_HISTORY_LIMIT) + Self::new(DEFAULT_HISTORY_LIMIT, MAX_ON_DISK_HISTORY_LIMIT) } } @@ -545,9 +545,32 @@ mod tests { assert_eq!(storage.in_memory_limit, DEFAULT_HISTORY_LIMIT * 3); } + #[test] + fn test_init_state_limits() { + let mut storage = InMemoryBlockStates::default(); + assert_eq!(storage.in_memory_limit, DEFAULT_HISTORY_LIMIT); + assert_eq!(storage.min_in_memory_limit, MIN_HISTORY_LIMIT); + assert_eq!(storage.max_on_disk_limit, MAX_ON_DISK_HISTORY_LIMIT); + + storage = storage.memory_only(); + assert!(storage.is_memory_only()); + + storage = InMemoryBlockStates::new(1, 0); + assert!(storage.is_memory_only()); + assert_eq!(storage.in_memory_limit, 1); + assert_eq!(storage.min_in_memory_limit, 1); + assert_eq!(storage.max_on_disk_limit, 0); + + storage = InMemoryBlockStates::new(1, 2); + assert!(!storage.is_memory_only()); + assert_eq!(storage.in_memory_limit, 1); + assert_eq!(storage.min_in_memory_limit, 1); + assert_eq!(storage.max_on_disk_limit, 2); + } + #[tokio::test(flavor = "multi_thread")] async fn can_read_write_cached_state() { - let mut storage = InMemoryBlockStates::new(1); + let mut storage = InMemoryBlockStates::new(1, MAX_ON_DISK_HISTORY_LIMIT); let one = B256::from(U256::from(1)); let two = B256::from(U256::from(2)); @@ -573,7 +596,7 @@ mod tests { #[tokio::test(flavor = "multi_thread")] async fn can_decrease_state_cache_size() { let limit = 15; - let mut storage = InMemoryBlockStates::new(limit); + let mut storage = InMemoryBlockStates::new(limit, MAX_ON_DISK_HISTORY_LIMIT); let num_states = 30; for idx in 0..num_states { From dc23de3f5a880c0487bc11fd60b9152f9d7b12bb Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 29 Jul 2024 12:53:33 +0300 Subject: [PATCH 026/184] fix(anvil): apply Arbitrum specifics to API block (#8542) --- crates/anvil/src/eth/backend/mem/mod.rs | 23 ++++++++++++++++++++--- crates/anvil/tests/it/fork.rs | 5 +++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 8f51ae04e..b235646b5 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -67,6 +67,7 @@ use anvil_core::eth::{ }; use anvil_rpc::error::RpcError; +use alloy_chains::NamedChain; use flate2::{read::GzDecoder, write::GzEncoder, Compression}; use foundry_evm::{ backend::{DatabaseError, DatabaseResult, RevertSnapshotAction}, @@ -1645,7 +1646,7 @@ impl Backend { Some(block.into_full_block(transactions.into_iter().map(|t| t.inner).collect())) } - /// Takes a block as it's stored internally and returns the eth api conform block format + /// Takes a block as it's stored internally and returns the eth api conform block format. pub fn convert_block(&self, block: Block) -> AlloyBlock { let size = U256::from(alloy_rlp::encode(&block).len() as u32); @@ -1676,7 +1677,7 @@ impl Backend { parent_beacon_block_root, } = header; - AlloyBlock { + let mut block = AlloyBlock { header: AlloyHeader { hash: Some(hash), parent_hash, @@ -1709,7 +1710,23 @@ impl Backend { uncles: vec![], withdrawals: None, other: Default::default(), - } + }; + + // If Arbitrum, apply chain specifics to converted block. + if let Ok( + NamedChain::Arbitrum | + NamedChain::ArbitrumGoerli | + NamedChain::ArbitrumNova | + NamedChain::ArbitrumTestnet, + ) = NamedChain::try_from(self.env.read().env.cfg.chain_id) + { + // Block number is the best number. + block.header.number = Some(self.best_number()); + // Set `l1BlockNumber` field. + block.other.insert("l1BlockNumber".to_string(), number.into()); + } + + block } /// Converts the `BlockNumber` into a numeric value diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index b27b361b2..8a4542b48 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -1165,6 +1165,11 @@ async fn test_arbitrum_fork_block_number() { let block_number = api.block_number().unwrap().to::(); assert_eq!(block_number, initial_block_number + 1); + // test block by number API call returns proper block number and `l1BlockNumber` is set + let block_by_number = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); + assert_eq!(block_by_number.header.number.unwrap(), initial_block_number + 1); + assert!(block_by_number.other.get("l1BlockNumber").is_some()); + // revert to recorded snapshot and check block number assert!(api.evm_revert(snapshot).await.unwrap()); let block_number = api.block_number().unwrap().to::(); From d5ff3d3ecb86643bc40c72ad0687d1e1d0d05ab4 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Mon, 29 Jul 2024 13:04:21 +0200 Subject: [PATCH 027/184] fix: add `Mantle` + `Mantle testnet` as exceptions for gas calculation during deployment (#8553) --- crates/cli/src/utils/cmd.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index 3b5d2bdf8..c7f9c1c2b 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -164,13 +164,15 @@ pub fn has_different_gas_calc(chain_id: u64) -> bool { return matches!( chain, NamedChain::Arbitrum | - NamedChain::ArbitrumTestnet | NamedChain::ArbitrumGoerli | NamedChain::ArbitrumSepolia | - NamedChain::Moonbeam | - NamedChain::Moonriver | + NamedChain::ArbitrumTestnet | + NamedChain::Mantle | + NamedChain::MantleTestnet | NamedChain::Moonbase | - NamedChain::MoonbeamDev + NamedChain::Moonbeam | + NamedChain::MoonbeamDev | + NamedChain::Moonriver ); } false From d1b25134ad0144cc14f6a44a6c6df5f5423e7253 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 29 Jul 2024 14:22:23 +0300 Subject: [PATCH 028/184] fix(coverage): proper single path branch support (#8552) --- crates/evm/coverage/src/analysis.rs | 117 +++++++++++++++------------- crates/evm/coverage/src/lib.rs | 10 ++- crates/forge/src/coverage.rs | 7 ++ crates/forge/tests/cli/coverage.rs | 86 +++++++++++++++++--- 4 files changed, 158 insertions(+), 62 deletions(-) diff --git a/crates/evm/coverage/src/analysis.rs b/crates/evm/coverage/src/analysis.rs index 3eb16332d..e28346181 100644 --- a/crates/evm/coverage/src/analysis.rs +++ b/crates/evm/coverage/src/analysis.rs @@ -211,59 +211,64 @@ impl<'a> ContractVisitor<'a> { // branch ID as we do. self.branch_id += 1; - // The relevant source range for the true branch is the `if(...)` statement itself - // and the true body of the if statement. The false body of the - // statement (if any) is processed as its own thing. If this source - // range is not processed like this, it is virtually impossible to - // correctly map instructions back to branches that include more - // complex logic like conditional logic. - let true_branch_loc = &ast::LowFidelitySourceLocation { - start: node.src.start, - length: true_body - .src - .length - .map(|length| true_body.src.start - node.src.start + length), - index: node.src.index, - }; - - // Add the coverage item for branch 0 (true body). - self.push_item(CoverageItem { - kind: CoverageItemKind::Branch { branch_id, path_id: 0 }, - loc: self.source_location_for(true_branch_loc), - hits: 0, - }); - match node.attribute::("falseBody") { // Both if/else statements. Some(false_body) => { - // Add the coverage item for branch 1 (false body). - // The relevant source range for the false branch is the `else` statement - // itself and the false body of the else statement. - self.push_item(CoverageItem { - kind: CoverageItemKind::Branch { branch_id, path_id: 1 }, - loc: self.source_location_for(&ast::LowFidelitySourceLocation { - start: node.src.start, - length: false_body.src.length.map(|length| { - false_body.src.start - true_body.src.start + length + // Add branch coverage items only if one of true/branch bodies contains + // statements. + if has_statements(&true_body) || has_statements(&false_body) { + // Add the coverage item for branch 0 (true body). + // The relevant source range for the true branch is the `if(...)` + // statement itself and the true body of the if statement. + // + // The false body of the statement is processed as its own thing. + // If this source range is not processed like this, it is virtually + // impossible to correctly map instructions back to branches that + // include more complex logic like conditional logic. + self.push_item(CoverageItem { + kind: CoverageItemKind::Branch { branch_id, path_id: 0 }, + loc: self.source_location_for(&ast::LowFidelitySourceLocation { + start: node.src.start, + length: true_body.src.length.map(|length| { + true_body.src.start - node.src.start + length + }), + index: node.src.index, }), - index: node.src.index, - }), - hits: 0, - }); - // Process the true body. - self.visit_block_or_statement(&true_body)?; - // Process the false body. - self.visit_block_or_statement(&false_body)?; + hits: 0, + }); + // Add the coverage item for branch 1 (false body). + // The relevant source range for the false branch is the `else` + // statement itself and the false body of the else statement. + self.push_item(CoverageItem { + kind: CoverageItemKind::Branch { branch_id, path_id: 1 }, + loc: self.source_location_for(&ast::LowFidelitySourceLocation { + start: node.src.start, + length: false_body.src.length.map(|length| { + false_body.src.start - true_body.src.start + length + }), + index: node.src.index, + }), + hits: 0, + }); + + // Process the true body. + self.visit_block_or_statement(&true_body)?; + // Process the false body. + self.visit_block_or_statement(&false_body)?; + } } None => { - // Add the coverage item for branch 1 (same true body). - self.push_item(CoverageItem { - kind: CoverageItemKind::Branch { branch_id, path_id: 1 }, - loc: self.source_location_for(true_branch_loc), - hits: 0, - }); - // Process the true body. - self.visit_block_or_statement(&true_body)?; + // Add single branch coverage only if it contains statements. + if has_statements(&true_body) { + // Add the coverage item for branch 0 (true body). + self.push_item(CoverageItem { + kind: CoverageItemKind::SinglePathBranch { branch_id }, + loc: self.source_location_for(&true_body.src), + hits: 0, + }); + // Process the true body. + self.visit_block_or_statement(&true_body)?; + } } } @@ -321,9 +326,7 @@ impl<'a> ContractVisitor<'a> { // Add coverage for clause body only if it is not empty. if let Some(block) = clause.attribute::("block") { - let statements: Vec = - block.attribute("statements").unwrap_or_default(); - if !statements.is_empty() { + if has_statements(&block) { self.push_item(CoverageItem { kind: CoverageItemKind::Statement, loc: self.source_location_for(&block.src), @@ -502,8 +505,12 @@ impl<'a> ContractVisitor<'a> { let source_location = &item.loc; // Push a line item if we haven't already - if matches!(item.kind, CoverageItemKind::Statement | CoverageItemKind::Branch { .. }) && - self.last_line < source_location.line + if matches!( + item.kind, + CoverageItemKind::Statement | + CoverageItemKind::Branch { .. } | + CoverageItemKind::SinglePathBranch { .. } + ) && self.last_line < source_location.line { self.items.push(CoverageItem { kind: CoverageItemKind::Line, @@ -527,6 +534,12 @@ impl<'a> ContractVisitor<'a> { } } +/// Helper function to check if a given node contains any statement. +fn has_statements(node: &Node) -> bool { + let statements: Vec = node.attribute("statements").unwrap_or_default(); + !statements.is_empty() +} + /// [`SourceAnalyzer`] result type. #[derive(Debug)] pub struct SourceAnalysis { diff --git a/crates/evm/coverage/src/lib.rs b/crates/evm/coverage/src/lib.rs index 67822bd8e..24390aa12 100644 --- a/crates/evm/coverage/src/lib.rs +++ b/crates/evm/coverage/src/lib.rs @@ -295,6 +295,11 @@ pub enum CoverageItemKind { /// The first path has ID 0, the next ID 1, and so on. path_id: usize, }, + /// A branch in the code. + SinglePathBranch { + /// The ID that identifies the branch. + branch_id: usize, + }, /// A function in the code. Function { /// The name of the function. @@ -324,6 +329,9 @@ impl Display for CoverageItem { CoverageItemKind::Branch { branch_id, path_id } => { write!(f, "Branch (branch: {branch_id}, path: {path_id})")?; } + CoverageItemKind::SinglePathBranch { branch_id } => { + write!(f, "Branch (branch: {branch_id}, path: 0)")?; + } CoverageItemKind::Function { name } => { write!(f, r#"Function "{name}""#)?; } @@ -408,7 +416,7 @@ impl AddAssign<&CoverageItem> for CoverageSummary { self.statement_hits += 1; } } - CoverageItemKind::Branch { .. } => { + CoverageItemKind::Branch { .. } | CoverageItemKind::SinglePathBranch { .. } => { self.branch_count += 1; if item.hits > 0 { self.branch_hits += 1; diff --git a/crates/forge/src/coverage.rs b/crates/forge/src/coverage.rs index cab937488..cc83e1329 100644 --- a/crates/forge/src/coverage.rs +++ b/crates/forge/src/coverage.rs @@ -116,6 +116,13 @@ impl<'a> CoverageReporter for LcovReporter<'a> { if hits == 0 { "-".to_string() } else { hits.to_string() } )?; } + CoverageItemKind::SinglePathBranch { branch_id } => { + writeln!( + self.destination, + "BRDA:{line},{branch_id},0,{}", + if hits == 0 { "-".to_string() } else { hits.to_string() } + )?; + } // Statements are not in the LCOV format. // We don't add them in order to avoid doubling line hits. _ => {} diff --git a/crates/forge/tests/cli/coverage.rs b/crates/forge/tests/cli/coverage.rs index f9d591fe2..e8999a7f3 100644 --- a/crates/forge/tests/cli/coverage.rs +++ b/crates/forge/tests/cli/coverage.rs @@ -464,6 +464,27 @@ contract Foo { } return true; } + + function checkEmptyStatements(uint256 number, uint256[] memory arr) external pure returns (bool) { + // Check that empty statements are covered. + if (number >= arr[0]) { + // Do nothing + } else { + // Do nothing. + } + if (number >= arr[0]) {} + + return true; + } + + function singlePathCoverage(uint256 number) external pure { + if (number < 10) { + if (number < 5) { + number++; + } + number++; + } + } } "#, ) @@ -562,23 +583,70 @@ contract FooTest is DSTest { bool result = foo.checkLt(number, arr); assertTrue(result); } + + function test_issue_4314() external { + uint256[] memory arr = new uint256[](1); + arr[0] = 1; + foo.checkEmptyStatements(0, arr); + } + + function test_single_path_child_branch() external { + foo.singlePathCoverage(1); + } + + function test_single_path_parent_branch() external { + foo.singlePathCoverage(9); + } + + function test_single_path_branch() external { + foo.singlePathCoverage(15); + } } "#, ) .unwrap(); - // TODO: fix following issues for 100% coverage - // https://github.com/foundry-rs/foundry/issues/4309 - // https://github.com/foundry-rs/foundry/issues/4310 - // https://github.com/foundry-rs/foundry/issues/4315 - cmd.arg("coverage").args(["--summary".to_string()]).assert_success().stdout_eq(str![[r#" + // Assert no coverage for single path branch. 2 branches (parent and child) not covered. + cmd.arg("coverage") + .args([ + "--nmt".to_string(), + "test_single_path_child_branch|test_single_path_parent_branch".to_string(), + ]) + .assert_success() + .stdout_eq(str![[r#" ... -| File | % Lines | % Statements | % Branches | % Funcs | -|-------------|-----------------|-----------------|----------------|---------------| -| src/Foo.sol | 100.00% (20/20) | 100.00% (23/23) | 83.33% (15/18) | 100.00% (7/7) | -| Total | 100.00% (20/20) | 100.00% (23/23) | 83.33% (15/18) | 100.00% (7/7) | +| File | % Lines | % Statements | % Branches | % Funcs | +|-------------|----------------|----------------|----------------|---------------| +| src/Foo.sol | 88.89% (24/27) | 90.00% (27/30) | 87.50% (14/16) | 100.00% (9/9) | +| Total | 88.89% (24/27) | 90.00% (27/30) | 87.50% (14/16) | 100.00% (9/9) | "#]]); + + // Assert no coverage for single path child branch. 1 branch (child) not covered. + cmd.forge_fuse() + .arg("coverage") + .args(["--nmt".to_string(), "test_single_path_child_branch".to_string()]) + .assert_success() + .stdout_eq(str![[r#" +... +| File | % Lines | % Statements | % Branches | % Funcs | +|-------------|----------------|----------------|----------------|---------------| +| src/Foo.sol | 96.30% (26/27) | 96.67% (29/30) | 93.75% (15/16) | 100.00% (9/9) | +| Total | 96.30% (26/27) | 96.67% (29/30) | 93.75% (15/16) | 100.00% (9/9) | + +"#]]); + + // Assert 100% coverage. + cmd.forge_fuse().arg("coverage").args(["--summary".to_string()]).assert_success().stdout_eq( + str![[r#" +... +| File | % Lines | % Statements | % Branches | % Funcs | +|-------------|-----------------|-----------------|-----------------|---------------| +| src/Foo.sol | 100.00% (27/27) | 100.00% (30/30) | 100.00% (16/16) | 100.00% (9/9) | +| Total | 100.00% (27/27) | 100.00% (30/30) | 100.00% (16/16) | 100.00% (9/9) | + +"#]], + ); }); forgetest!(test_function_call_coverage, |prj, cmd| { From 5161091748185e047ef6f368bec2ce22f5a0b65b Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 29 Jul 2024 15:32:49 +0300 Subject: [PATCH 029/184] chore: potential fix flaky test_invariant_assert_shrink (#8554) --- crates/forge/tests/it/invariant.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index 72c3e7bd7..e4bf14ecd 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -285,23 +285,27 @@ async fn test_invariant_shrink() { #[tokio::test(flavor = "multi_thread")] #[cfg_attr(windows, ignore = "for some reason there's different rng")] async fn test_invariant_assert_shrink() { - // ensure assert and require shrinks to same sequence of 3 or less - test_shrink("invariant_with_assert").await; - test_shrink("invariant_with_require").await; + // ensure assert shrinks to same sequence of 2 as require + check_shrink_sequence("invariant_with_assert", 2).await; } -async fn test_shrink(test_pattern: &str) { - let mut opts = TEST_DATA_DEFAULT.test_opts.clone(); - opts.fuzz.seed = Some(U256::from(100u32)); +#[tokio::test(flavor = "multi_thread")] +#[cfg_attr(windows, ignore = "for some reason there's different rng")] +async fn test_invariant_require_shrink() { + // ensure require shrinks to same sequence of 2 as assert + check_shrink_sequence("invariant_with_require", 2).await; +} + +async fn check_shrink_sequence(test_pattern: &str, expected_len: usize) { let filter = Filter::new(test_pattern, ".*", ".*fuzz/invariant/common/InvariantShrinkWithAssert.t.sol"); let mut runner = TEST_DATA_DEFAULT.runner(); - runner.test_options = opts; + runner.test_options.fuzz.seed = Some(U256::from(100u32)); match get_counterexample!(runner, &filter) { CounterExample::Single(_) => panic!("CounterExample should be a sequence."), CounterExample::Sequence(sequence) => { - assert!(sequence.len() <= 3); + assert_eq!(sequence.len(), expected_len); } }; } From fdfaafd629faa2eea3362a8370eef7c1f8074710 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 29 Jul 2024 14:33:08 +0200 Subject: [PATCH 030/184] feat(cheatcodes): display cheatcode name in error message (#8533) * feat(cheatcodes): display cheatcode name in error message * fix: tests * fix * rm * fix * fix --- Cargo.lock | 1 + crates/cheatcodes/Cargo.toml | 19 +++---- crates/cheatcodes/README.md | 2 +- crates/cheatcodes/src/error.rs | 29 +++++----- crates/cheatcodes/src/evm.rs | 20 +++---- crates/cheatcodes/src/fs.rs | 10 ++-- crates/cheatcodes/src/inspector.rs | 53 ++++++++++++++----- crates/cheatcodes/src/lib.rs | 4 ++ crates/cheatcodes/src/string.rs | 4 +- crates/cheatcodes/src/test/expect.rs | 5 +- .../cheats/BroadcastRawTransaction.t.sol | 6 +-- testdata/default/cheats/Etch.t.sol | 2 +- testdata/default/cheats/Fs.t.sol | 10 ---- testdata/default/cheats/GetCode.t.sol | 2 +- testdata/default/cheats/GetDeployedCode.t.sol | 2 +- testdata/default/cheats/Load.t.sol | 8 +-- testdata/default/cheats/Store.t.sol | 8 +-- 17 files changed, 100 insertions(+), 85 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1a37ebd14..cd11b96f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3577,6 +3577,7 @@ dependencies = [ "itertools 0.13.0", "jsonpath_lib", "k256", + "memchr", "p256", "parking_lot", "proptest", diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index 80336d4e8..c061a6381 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -45,23 +45,24 @@ parking_lot.workspace = true alloy-consensus = { workspace = true, features = ["k256"] } alloy-rlp.workspace = true +base64.workspace = true +chrono.workspace = true +dialoguer = "0.11.0" eyre.workspace = true itertools.workspace = true jsonpath_lib.workspace = true +k256.workspace = true +memchr = "2.7" +p256 = "0.13.2" +rand = "0.8" revm.workspace = true +rustc-hash.workspace = true +semver.workspace = true serde_json.workspace = true -base64.workspace = true +thiserror.workspace = true toml = { workspace = true, features = ["preserve_order"] } tracing.workspace = true -k256.workspace = true walkdir.workspace = true -p256 = "0.13.2" -thiserror.workspace = true -semver.workspace = true -rustc-hash.workspace = true -dialoguer = "0.11.0" -rand = "0.8" -chrono.workspace = true [dev-dependencies] proptest.workspace = true diff --git a/crates/cheatcodes/README.md b/crates/cheatcodes/README.md index dfe53bceb..790f2553a 100644 --- a/crates/cheatcodes/README.md +++ b/crates/cheatcodes/README.md @@ -27,7 +27,7 @@ the Foundry cheatcodes externally. For example, here are some tools that make use of the JSON interface: - Internally, this is used to generate [a simple Solidity interface](../../testdata/cheats/Vm.sol) for testing -- (WIP) Used by [`forge-std`](https://github.com/foundry-rs/forge-std) to generate [user-friendly Solidity interfaces](https://github.com/foundry-rs/forge-std/blob/master/src/Vm.sol) +- Used by [`forge-std`](https://github.com/foundry-rs/forge-std) to generate [user-friendly Solidity interfaces](https://github.com/foundry-rs/forge-std/blob/master/src/Vm.sol) - (WIP) Used by [the Foundry book](https://github.com/foundry-rs/book) to generate [the cheatcodes reference](https://book.getfoundry.sh/cheatcodes) - ... diff --git a/crates/cheatcodes/src/error.rs b/crates/cheatcodes/src/error.rs index 26aba7348..85be519bf 100644 --- a/crates/cheatcodes/src/error.rs +++ b/crates/cheatcodes/src/error.rs @@ -68,17 +68,14 @@ macro_rules! ensure { macro_rules! ensure_not_precompile { ($address:expr, $ctxt:expr) => { if $ctxt.is_precompile($address) { - return Err($crate::error::precompile_error( - ::CHEATCODE.func.id, - $address, - )) + return Err($crate::error::precompile_error($address)); } }; } #[cold] -pub(crate) fn precompile_error(id: &'static str, address: &Address) -> Error { - fmt_err!("cannot call `{id}` on precompile {address}") +pub(crate) fn precompile_error(address: &Address) -> Error { + fmt_err!("cannot use precompile {address} as an argument") } /// Error thrown by cheatcodes. @@ -158,7 +155,7 @@ impl Error { } /// Returns the kind of this error. - #[inline(always)] + #[inline] pub fn kind(&self) -> ErrorKind<'_> { let data = self.data(); if self.is_str { @@ -170,32 +167,38 @@ impl Error { } /// Returns the raw data of this error. - #[inline(always)] + #[inline] pub fn data(&self) -> &[u8] { unsafe { &*self.data } } - #[inline(always)] + /// Returns `true` if this error is a human-readable string. + #[inline] + pub fn is_str(&self) -> bool { + self.is_str + } + + #[inline] fn new_str(data: &'static str) -> Self { Self::_new(true, false, data.as_bytes()) } - #[inline(always)] + #[inline] fn new_string(data: String) -> Self { Self::_new(true, true, Box::into_raw(data.into_boxed_str().into_boxed_bytes())) } - #[inline(always)] + #[inline] fn new_bytes(data: &'static [u8]) -> Self { Self::_new(false, false, data) } - #[inline(always)] + #[inline] fn new_vec(data: Vec) -> Self { Self::_new(false, true, Box::into_raw(data.into_boxed_slice())) } - #[inline(always)] + #[inline] fn _new(is_str: bool, drop: bool, data: *const [u8]) -> Self { debug_assert!(!data.is_null()); Self { is_str, drop, data } diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 5e24070a8..2d4031920 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -275,13 +275,10 @@ impl Cheatcode for resumeGasMeteringCall { impl Cheatcode for lastCallGasCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; - ensure!(state.last_call_gas.is_some(), "`lastCallGas` is only available after a call"); - Ok(state - .last_call_gas - .as_ref() - // This should never happen, as we ensure `last_call_gas` is `Some` above. - .expect("`lastCallGas` is only available after a call") - .abi_encode()) + let Some(last_call_gas) = &state.last_call_gas else { + bail!("no external call was made yet"); + }; + Ok(last_call_gas.abi_encode()) } } @@ -354,7 +351,7 @@ impl Cheatcode for blobhashesCall { let Self { hashes } = self; ensure!( ccx.ecx.spec_id() >= SpecId::CANCUN, - "`blobhash` is not supported before the Cancun hard fork; \ + "`blobhashes` is not supported before the Cancun hard fork; \ see EIP-4844: https://eips.ethereum.org/EIPS/eip-4844" ); ccx.ecx.env.tx.blob_hashes.clone_from(hashes); @@ -367,7 +364,7 @@ impl Cheatcode for getBlobhashesCall { let Self {} = self; ensure!( ccx.ecx.spec_id() >= SpecId::CANCUN, - "`blobhash` is not supported before the Cancun hard fork; \ + "`getBlobhashes` is not supported before the Cancun hard fork; \ see EIP-4844: https://eips.ethereum.org/EIPS/eip-4844" ); Ok(ccx.ecx.env.tx.blob_hashes.clone().abi_encode()) @@ -605,9 +602,8 @@ impl Cheatcode for broadcastRawTransactionCall { executor: &mut E, ) -> Result { let mut data = self.data.as_ref(); - let tx = TxEnvelope::decode(&mut data).map_err(|err| { - fmt_err!("broadcastRawTransaction: error decoding transaction ({err})") - })?; + let tx = TxEnvelope::decode(&mut data) + .map_err(|err| fmt_err!("failed to decode RLP-encoded transaction: {err}"))?; ccx.ecx.db.transact_from_tx( tx.clone().into(), diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 177037c90..5467cfcf6 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -352,7 +352,7 @@ fn get_artifact_code(state: &Cheatcodes, path: &str, deployed: bool) -> Result Result>(); let artifact = match &filtered[..] { - [] => Err(fmt_err!("No matching artifact found")), + [] => Err(fmt_err!("no matching artifact found")), [artifact] => Ok(artifact), filtered => { // If we know the current script/test contract solc version, try to filter by it @@ -403,7 +403,7 @@ fn get_artifact_code(state: &Cheatcodes, path: &str, deployed: bool) -> Result>(); (filtered.len() == 1).then(|| filtered[0]) }) - .ok_or_else(|| fmt_err!("Multiple matching artifacts found")) + .ok_or_else(|| fmt_err!("multiple matching artifacts found")) } }?; @@ -414,7 +414,7 @@ fn get_artifact_code(state: &Cheatcodes, path: &str, deployed: bool) -> Result Result(&data)?; let maybe_bytecode = if deployed { artifact.deployed_bytecode } else { artifact.bytecode }; - maybe_bytecode.ok_or_else(|| fmt_err!("No bytecode for contract. Is it abstract or unlinked?")) + maybe_bytecode.ok_or_else(|| fmt_err!("no bytecode for contract; is it abstract or unlinked?")) } impl Cheatcode for ffiCall { diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 13cc02df3..0d377a382 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -592,6 +592,7 @@ impl Cheatcodes { { let expected_revert = std::mem::take(&mut self.expected_revert).unwrap(); return match expect::handle_expect_revert( + false, true, expected_revert.reason.as_deref(), outcome.result.result, @@ -1035,7 +1036,7 @@ impl Inspector for Cheatcodes { // Handle expected reverts if let Some(expected_revert) = &self.expected_revert { if ecx.journaled_state.depth() <= expected_revert.depth { - let needs_processing: bool = match expected_revert.kind { + let needs_processing = match expected_revert.kind { ExpectedRevertKind::Default => !cheatcode_call, // `pending_processing` == true means that we're in the `call_end` hook for // `vm.expectCheatcodeRevert` and shouldn't expect revert here @@ -1047,6 +1048,7 @@ impl Inspector for Cheatcodes { if needs_processing { let expected_revert = std::mem::take(&mut self.expected_revert).unwrap(); return match expect::handle_expect_revert( + cheatcode_call, false, expected_revert.reason.as_deref(), outcome.result.result, @@ -1071,9 +1073,7 @@ impl Inspector for Cheatcodes { if let ExpectedRevertKind::Cheatcode { pending_processing } = &mut self.expected_revert.as_mut().unwrap().kind { - if *pending_processing { - *pending_processing = false; - } + *pending_processing = false; } } } @@ -1847,22 +1847,49 @@ fn apply_dispatch( }; } - let _guard = trace_span_and_call(calls); - let result = vm_calls!(dispatch); - trace_return(&result); + let mut dyn_cheat = DynCheatCache::new(calls); + let _guard = trace_span_and_call(&mut dyn_cheat); + let mut result = vm_calls!(dispatch); + fill_and_trace_return(&mut dyn_cheat, &mut result); result } -fn trace_span_and_call(calls: &Vm::VmCalls) -> tracing::span::EnteredSpan { - let mut cheat = None; - let mut get_cheat = || *cheat.get_or_insert_with(|| calls_as_dyn_cheatcode(calls)); - let span = debug_span!(target: "cheatcodes", "apply", id = %get_cheat().id()); +// Caches the result of `calls_as_dyn_cheatcode`. +// TODO: Remove this once Cheatcode is object-safe, as caching would not be necessary anymore. +struct DynCheatCache<'a> { + calls: &'a Vm::VmCalls, + slot: Option<&'a dyn DynCheatcode>, +} + +impl<'a> DynCheatCache<'a> { + fn new(calls: &'a Vm::VmCalls) -> Self { + Self { calls, slot: None } + } + + fn get(&mut self) -> &dyn DynCheatcode { + *self.slot.get_or_insert_with(|| calls_as_dyn_cheatcode(self.calls)) + } +} + +fn trace_span_and_call(dyn_cheat: &mut DynCheatCache) -> tracing::span::EnteredSpan { + let span = debug_span!(target: "cheatcodes", "apply", id = %dyn_cheat.get().id()); let entered = span.entered(); - trace!(target: "cheatcodes", cheat = ?get_cheat().as_debug(), "applying"); + trace!(target: "cheatcodes", cheat = ?dyn_cheat.get().as_debug(), "applying"); entered } -fn trace_return(result: &Result) { +fn fill_and_trace_return(dyn_cheat: &mut DynCheatCache, result: &mut Result) { + if let Err(e) = result { + if e.is_str() { + let name = dyn_cheat.get().name(); + // Skip showing the cheatcode name for: + // - assertions: too verbose, and can already be inferred from the error message + // - `rpcUrl`: forge-std relies on it in `getChainWithUpdatedRpcUrl` + if !name.contains("assert") && name != "rpcUrl" { + *e = fmt_err!("vm.{name}: {e}"); + } + } + } trace!( target: "cheatcodes", return = %match result { diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index 5b46fd7e5..b03dbc2d7 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -85,11 +85,15 @@ pub(crate) trait Cheatcode: CheatcodeDef + DynCheatcode { } pub(crate) trait DynCheatcode { + fn name(&self) -> &'static str; fn id(&self) -> &'static str; fn as_debug(&self) -> &dyn std::fmt::Debug; } impl DynCheatcode for T { + fn name(&self) -> &'static str { + T::CHEATCODE.func.signature.split('(').next().unwrap() + } fn id(&self) -> &'static str { T::CHEATCODE.func.id } diff --git a/crates/cheatcodes/src/string.rs b/crates/cheatcodes/src/string.rs index 7b7d9b505..e7435d541 100644 --- a/crates/cheatcodes/src/string.rs +++ b/crates/cheatcodes/src/string.rs @@ -186,9 +186,7 @@ fn parse_value_fallback(s: &str, ty: &DynSolType) -> Option false, s if s.eq_ignore_ascii_case("true") => true, s if s.eq_ignore_ascii_case("false") => false, - _ => { - return None; - } + _ => return None, }; return Some(Ok(DynSolValue::Bool(b))); } diff --git a/crates/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs index 80d2417ec..4c5bfe928 100644 --- a/crates/cheatcodes/src/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -542,6 +542,7 @@ fn expect_revert( } pub(crate) fn handle_expect_revert( + is_cheatcode: bool, is_create: bool, expected_revert: Option<&[u8]>, status: InstructionResult, @@ -578,7 +579,9 @@ pub(crate) fn handle_expect_revert( } } - if actual_revert == expected_revert { + if actual_revert == expected_revert || + (is_cheatcode && memchr::memmem::find(&actual_revert, expected_revert).is_some()) + { Ok(success_return()) } else { let stringify = |data: &[u8]| { diff --git a/testdata/default/cheats/BroadcastRawTransaction.t.sol b/testdata/default/cheats/BroadcastRawTransaction.t.sol index 7425e9d37..43e4ff632 100644 --- a/testdata/default/cheats/BroadcastRawTransaction.t.sol +++ b/testdata/default/cheats/BroadcastRawTransaction.t.sol @@ -8,17 +8,17 @@ contract BroadcastRawTransactionTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); function test_revert_not_a_tx() public { - vm.expectRevert("broadcastRawTransaction: error decoding transaction (unexpected string)"); + vm._expectCheatcodeRevert("failed to decode RLP-encoded transaction: unexpected string"); vm.broadcastRawTransaction(hex"0102"); } function test_revert_missing_signature() public { - vm.expectRevert("broadcastRawTransaction: error decoding transaction (input too short)"); + vm._expectCheatcodeRevert("failed to decode RLP-encoded transaction: input too short"); vm.broadcastRawTransaction(hex"dd806483030d40940993863c19b0defb183ca2b502db7d1b331ded757b80"); } function test_revert_wrong_chainid() public { - vm.expectRevert("transaction validation error: invalid chain ID"); + vm._expectCheatcodeRevert("transaction validation error: invalid chain ID"); vm.broadcastRawTransaction( hex"f860806483030d40946fd0a0cff9a87adf51695b40b4fa267855a8f4c6118025a03ebeabbcfe43c2c982e99b376b5fb6e765059d7f215533c8751218cac99bbd80a00a56cf5c382442466770a756e81272d06005c9e90fb8dbc5b53af499d5aca856" ); diff --git a/testdata/default/cheats/Etch.t.sol b/testdata/default/cheats/Etch.t.sol index 6e58fc13b..fbd0e62b9 100644 --- a/testdata/default/cheats/Etch.t.sol +++ b/testdata/default/cheats/Etch.t.sol @@ -17,7 +17,7 @@ contract EtchTest is DSTest { function testEtchNotAvailableOnPrecompiles() public { address target = address(1); bytes memory code = hex"1010"; - vm._expectCheatcodeRevert(bytes("cannot call `etch` on precompile 0x0000000000000000000000000000000000000001")); + vm._expectCheatcodeRevert("cannot use precompile 0x0000000000000000000000000000000000000001 as an argument"); vm.etch(target, code); } } diff --git a/testdata/default/cheats/Fs.t.sol b/testdata/default/cheats/Fs.t.sol index c48adefec..cb407641e 100644 --- a/testdata/default/cheats/Fs.t.sol +++ b/testdata/default/cheats/Fs.t.sol @@ -233,16 +233,6 @@ contract FsTest is DSTest { vm.fsMetadata("/etc/hosts"); } - // not testing file cheatcodes per se - function testCheatCodeErrorPrefix() public { - try vm.readFile("/etc/hosts") { - emit log("Error: reading /etc/hosts should revert"); - fail(); - } catch (bytes memory err) { - assertEq(err, abi.encodeWithSignature("CheatcodeError(string)", FOUNDRY_READ_ERR)); - } - } - function testExists() public { string memory validFilePath = "fixtures/File/read.txt"; assertTrue(vm.exists(validFilePath)); diff --git a/testdata/default/cheats/GetCode.t.sol b/testdata/default/cheats/GetCode.t.sol index 73980d7b2..b155a1873 100644 --- a/testdata/default/cheats/GetCode.t.sol +++ b/testdata/default/cheats/GetCode.t.sol @@ -79,7 +79,7 @@ contract GetCodeTest is DSTest { bytes memory code = vm.getCode("cheats/GetCode.t.sol:TestContract:0.8.18"); assertEq(type(TestContract).creationCode, code); - vm._expectCheatcodeRevert("No matching artifact found"); + vm._expectCheatcodeRevert("no matching artifact found"); vm.getCode("cheats/GetCode.t.sol:TestContract:0.8.19"); } diff --git a/testdata/default/cheats/GetDeployedCode.t.sol b/testdata/default/cheats/GetDeployedCode.t.sol index 8d95b243c..220e3f3c8 100644 --- a/testdata/default/cheats/GetDeployedCode.t.sol +++ b/testdata/default/cheats/GetDeployedCode.t.sol @@ -45,7 +45,7 @@ contract GetDeployedCodeTest is DSTest { assertEq(address(test).code, code); - vm._expectCheatcodeRevert("No matching artifact found"); + vm._expectCheatcodeRevert("no matching artifact found"); vm.getDeployedCode("cheats/GetDeployedCode.t.sol:TestContract:0.8.19"); } } diff --git a/testdata/default/cheats/Load.t.sol b/testdata/default/cheats/Load.t.sol index 37a2c80b2..0ed1cbbc2 100644 --- a/testdata/default/cheats/Load.t.sol +++ b/testdata/default/cheats/Load.t.sol @@ -27,12 +27,8 @@ contract LoadTest is DSTest { } function testLoadNotAvailableOnPrecompiles() public { - vm.expectRevert(bytes("cannot call `load` on precompile 0x0000000000000000000000000000000000000001")); - uint256 val = this.load(address(1), bytes32(0)); - } - - function load(address target, bytes32 slot) public returns (uint256) { - return uint256(vm.load(target, slot)); + vm._expectCheatcodeRevert("cannot use precompile 0x0000000000000000000000000000000000000001 as an argument"); + vm.load(address(1), bytes32(0)); } function testLoadOtherStorage() public { diff --git a/testdata/default/cheats/Store.t.sol b/testdata/default/cheats/Store.t.sol index 059952fca..5159a4ab8 100644 --- a/testdata/default/cheats/Store.t.sol +++ b/testdata/default/cheats/Store.t.sol @@ -30,12 +30,8 @@ contract StoreTest is DSTest { assertEq(store.slot0(), 10, "initial value for slot 0 is incorrect"); assertEq(store.slot1(), 20, "initial value for slot 1 is incorrect"); - vm.expectRevert(bytes("cannot call `store` on precompile 0x0000000000000000000000000000000000000001")); - this._store(address(1), bytes32(0), bytes32(uint256(1))); - } - - function _store(address target, bytes32 slot, bytes32 value) public { - vm.store(target, slot, value); + vm._expectCheatcodeRevert("cannot use precompile 0x0000000000000000000000000000000000000001 as an argument"); + vm.store(address(1), bytes32(0), bytes32(uint256(1))); } function testStoreFuzzed(uint256 slot0, uint256 slot1) public { From bb4b2a37b88d5471f8b406ec8ac95e7fd03bc427 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Tue, 30 Jul 2024 17:46:59 +0800 Subject: [PATCH 031/184] fix(verify-bytecode): fix metadata extraction and add tests (#8560) * fix(verify-bytecode): fix metadata extraction and add tests * fix * fix * clippy * clippy + fmt * move to tests/ --- Cargo.lock | 1 + crates/forge/tests/cli/main.rs | 1 + crates/forge/tests/cli/verify_bytecode.rs | 85 +++++++++++++++++++++++ crates/verify/Cargo.toml | 2 + crates/verify/src/bytecode.rs | 46 ++++++------ 5 files changed, 111 insertions(+), 24 deletions(-) create mode 100644 crates/forge/tests/cli/verify_bytecode.rs diff --git a/Cargo.lock b/Cargo.lock index cd11b96f3..7e714b9b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3498,6 +3498,7 @@ dependencies = [ "alloy-provider", "alloy-rpc-types", "async-trait", + "ciborium", "clap", "eyre", "foundry-block-explorers", diff --git a/crates/forge/tests/cli/main.rs b/crates/forge/tests/cli/main.rs index b8bc3db5a..e8c6f1cc0 100644 --- a/crates/forge/tests/cli/main.rs +++ b/crates/forge/tests/cli/main.rs @@ -20,5 +20,6 @@ mod soldeer; mod svm; mod test_cmd; mod verify; +mod verify_bytecode; mod ext_integration; diff --git a/crates/forge/tests/cli/verify_bytecode.rs b/crates/forge/tests/cli/verify_bytecode.rs new file mode 100644 index 000000000..846fcc47d --- /dev/null +++ b/crates/forge/tests/cli/verify_bytecode.rs @@ -0,0 +1,85 @@ +use foundry_compilers::artifacts::{BytecodeHash, EvmVersion}; +use foundry_config::Config; +use foundry_test_utils::{ + forgetest_async, + rpc::{next_etherscan_api_key, next_http_archive_rpc_endpoint}, + util::OutputExt, + TestCommand, TestProject, +}; + +fn test_verify_bytecode( + prj: TestProject, + mut cmd: TestCommand, + addr: &str, + contract_name: &str, + config: Config, + expected_matches: (&str, &str), +) { + let etherscan_key = next_etherscan_api_key(); + let rpc_url = next_http_archive_rpc_endpoint(); + + // fetch and flatten source code + let source_code = cmd + .cast_fuse() + .args(["etherscan-source", addr, "--flatten", "--etherscan-api-key", ðerscan_key]) + .assert_success() + .get_output() + .stdout_lossy(); + + prj.add_source(contract_name, &source_code).unwrap(); + prj.write_config(config); + + let output = cmd + .forge_fuse() + .args([ + "verify-bytecode", + addr, + contract_name, + "--etherscan-api-key", + ðerscan_key, + "--rpc-url", + &rpc_url, + ]) + .assert_success() + .get_output() + .stdout_lossy(); + + assert!(output + .contains(format!("Creation code matched with status {}", expected_matches.0).as_str())); + assert!(output + .contains(format!("Runtime code matched with status {}", expected_matches.1).as_str())); +} + +forgetest_async!(can_verify_bytecode_no_metadata, |prj, cmd| { + test_verify_bytecode( + prj, + cmd, + "0xba2492e52F45651B60B8B38d4Ea5E2390C64Ffb1", + "SystemConfig", + Config { + evm_version: EvmVersion::London, + optimizer_runs: 999999, + optimizer: true, + cbor_metadata: false, + bytecode_hash: BytecodeHash::None, + ..Default::default() + }, + ("full", "full"), + ); +}); + +forgetest_async!(can_verify_bytecode_with_metadata, |prj, cmd| { + test_verify_bytecode( + prj, + cmd, + "0xb8901acb165ed027e32754e0ffe830802919727f", + "L1_ETH_Bridge", + Config { + evm_version: EvmVersion::Paris, + optimizer_runs: 50000, + optimizer: true, + ..Default::default() + }, + ("partial", "partial"), + ); +}); diff --git a/crates/verify/Cargo.toml b/crates/verify/Cargo.toml index 97ff5e425..3ef9b4fb4 100644 --- a/crates/verify/Cargo.toml +++ b/crates/verify/Cargo.toml @@ -41,6 +41,8 @@ once_cell.workspace = true yansi.workspace = true itertools.workspace = true +ciborium = "0.2" + [dev-dependencies] tokio = { workspace = true, features = ["macros"] } foundry-test-utils.workspace = true diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index 5834e961f..aa3ffbe0e 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -11,14 +11,14 @@ use foundry_cli::{ }; use foundry_common::{abi::encode_args, compile::ProjectCompiler, provider::ProviderBuilder}; use foundry_compilers::{ - artifacts::{BytecodeHash, CompactContractBytecode, EvmVersion}, + artifacts::{CompactContractBytecode, EvmVersion}, info::ContractInfo, }; use foundry_config::{figment, filter::SkipBuildFilter, impl_figment_convert, Chain, Config}; use foundry_evm::{ constants::DEFAULT_CREATE2_DEPLOYER, executors::TracingExecutor, utils::configure_tx_env, }; -use revm_primitives::{db::Database, EnvWithHandlerCfg, HandlerCfg, SpecId}; +use revm_primitives::{db::Database, EnvWithHandlerCfg, HandlerCfg}; use semver::Version; use serde::{Deserialize, Serialize}; use std::{fmt, path::PathBuf, str::FromStr}; @@ -262,9 +262,6 @@ impl VerifyBytecodeArgs { } } - // If bytecode_hash is disabled then its always partial verification - let has_metadata = config.bytecode_hash == BytecodeHash::None; - // Append constructor args to the local_bytecode trace!(%constructor_args); let mut local_bytecode_vec = local_bytecode.to_vec(); @@ -276,7 +273,6 @@ impl VerifyBytecodeArgs { maybe_creation_code, &constructor_args, false, - has_metadata, ); let mut json_results: Vec = vec![]; @@ -369,7 +365,7 @@ impl VerifyBytecodeArgs { configure_tx_env(&mut env, &transaction); let env_with_handler = - EnvWithHandlerCfg::new(Box::new(env.clone()), HandlerCfg::new(SpecId::LATEST)); + EnvWithHandlerCfg::new(Box::new(env.clone()), HandlerCfg::new(config.evm_spec_id())); let contract_address = if let Some(to) = transaction.to { if to != DEFAULT_CREATE2_DEPLOYER { @@ -414,7 +410,6 @@ impl VerifyBytecodeArgs { &onchain_runtime_code, &constructor_args, true, - has_metadata, ); self.print_result( @@ -599,13 +594,12 @@ fn match_bytecodes( bytecode: &[u8], constructor_args: &[u8], is_runtime: bool, - has_metadata: bool, ) -> Option { // 1. Try full match if local_bytecode == bytecode { Some(VerificationType::Full) } else { - is_partial_match(local_bytecode, bytecode, constructor_args, is_runtime, has_metadata) + is_partial_match(local_bytecode, bytecode, constructor_args, is_runtime) .then_some(VerificationType::Partial) } } @@ -615,30 +609,23 @@ fn is_partial_match( mut bytecode: &[u8], constructor_args: &[u8], is_runtime: bool, - has_metadata: bool, ) -> bool { // 1. Check length of constructor args if constructor_args.is_empty() || is_runtime { // Assume metadata is at the end of the bytecode - return try_extract_and_compare_bytecode(local_bytecode, bytecode, has_metadata) + return try_extract_and_compare_bytecode(local_bytecode, bytecode) } // If not runtime, extract constructor args from the end of the bytecode bytecode = &bytecode[..bytecode.len() - constructor_args.len()]; local_bytecode = &local_bytecode[..local_bytecode.len() - constructor_args.len()]; - try_extract_and_compare_bytecode(local_bytecode, bytecode, has_metadata) + try_extract_and_compare_bytecode(local_bytecode, bytecode) } -fn try_extract_and_compare_bytecode( - mut local_bytecode: &[u8], - mut bytecode: &[u8], - has_metadata: bool, -) -> bool { - if has_metadata { - local_bytecode = extract_metadata_hash(local_bytecode); - bytecode = extract_metadata_hash(bytecode); - } +fn try_extract_and_compare_bytecode(mut local_bytecode: &[u8], mut bytecode: &[u8]) -> bool { + local_bytecode = extract_metadata_hash(local_bytecode); + bytecode = extract_metadata_hash(bytecode); // Now compare the local code and bytecode local_bytecode == bytecode @@ -650,8 +637,19 @@ fn extract_metadata_hash(bytecode: &[u8]) -> &[u8] { let metadata_len = &bytecode[bytecode.len() - 2..]; let metadata_len = u16::from_be_bytes([metadata_len[0], metadata_len[1]]); - // Now discard the metadata from the bytecode - &bytecode[..bytecode.len() - 2 - metadata_len as usize] + if metadata_len as usize <= bytecode.len() { + if ciborium::from_reader::( + &bytecode[bytecode.len() - 2 - metadata_len as usize..bytecode.len() - 2], + ) + .is_ok() + { + &bytecode[..bytecode.len() - 2 - metadata_len as usize] + } else { + bytecode + } + } else { + bytecode + } } fn find_mismatch_in_settings( From 26a7559758c192911dd39ce7d621a18ef0d419e6 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Tue, 30 Jul 2024 17:53:38 +0200 Subject: [PATCH 032/184] fix: error `intrinsic gas too low` when deploying contract on Mantle / Mantle Sepolia (follow-up) (#8562) * temp patch alloy-chains * not working yet, re-add as exception? * revert to alloy-chains 0.1, includes upstream fix * update --- Cargo.lock | 50 +++++++++++++++++++++---------------- crates/cli/src/utils/cmd.rs | 1 + 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7e714b9b5..2a3f08090 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,9 +68,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-chains" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1752d7d62e2665da650a36d84abbf239f812534475d51f072a49a533513b7cdd" +checksum = "47ff94ce0f141c2671c23d02c7b88990dd432856639595c5d010663d017c2c58" dependencies = [ "num_enum", "serde", @@ -1970,9 +1970,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" +checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" dependencies = [ "jobserver", "libc", @@ -2923,9 +2923,9 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] name = "enumn" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" +checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", @@ -5889,18 +5889,18 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -6477,9 +6477,13 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "2288c0e17cc8d342c712bb43a257a80ebffce59cdb33d5000d8348f3ec02528b" +dependencies = [ + "zerocopy", + "zerocopy-derive", +] [[package]] name = "precomputed-hash" @@ -7570,9 +7574,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.1.5" +version = "2.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fadf67e3cf23f8b11a6c8c48a16cb2437381503615acd91094ec7b4686a5a53" +checksum = "05ccfb12511cdb770157ace92d7dda771e498445b78f9886e8cdbc5140a4eced" dependencies = [ "sdd", ] @@ -7646,9 +7650,9 @@ dependencies = [ [[package]] name = "sdd" -version = "1.7.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85f05a494052771fc5bd0619742363b5e24e5ad72ab3111ec2e27925b8edc5f3" +checksum = "177258b64c0faaa9ffd3c65cd3262c2bc7e2588dbbd9c1641d0346145c1bbda8" [[package]] name = "sec1" @@ -7785,12 +7789,13 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" dependencies = [ "indexmap 2.2.6", "itoa", + "memchr", "ryu", "serde", ] @@ -7979,9 +7984,9 @@ dependencies = [ [[package]] name = "signal-hook-mio" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" dependencies = [ "libc", "mio 0.8.11", @@ -9415,9 +9420,9 @@ dependencies = [ [[package]] name = "which" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8211e4f58a2b2805adfbefbc07bab82958fc91e3836339b1ab7ae32465dce0d7" +checksum = "3d9c5ed668ee1f17edb3b627225343d210006a90bb1e3745ce1f30b1fb115075" dependencies = [ "either", "home", @@ -9831,6 +9836,7 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index c7f9c1c2b..6abd11fce 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -169,6 +169,7 @@ pub fn has_different_gas_calc(chain_id: u64) -> bool { NamedChain::ArbitrumTestnet | NamedChain::Mantle | NamedChain::MantleTestnet | + NamedChain::MantleSepolia | NamedChain::Moonbase | NamedChain::Moonbeam | NamedChain::MoonbeamDev | From 53bf620da067d87311aec194a05ba1b840c2665a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Vincent?= <28714795+leovct@users.noreply.github.com> Date: Wed, 31 Jul 2024 11:51:10 +0200 Subject: [PATCH 033/184] docs(`forge script`): improve `Mac Mismatch` error referring to failure to decrypt of keystore (#8572) * docs(`forge script`): improve `Mac Mismatch` error referring to failure to decrypt of keystore * chore: rename error --- Cargo.lock | 1 + crates/wallets/Cargo.toml | 1 + crates/wallets/src/error.rs | 2 ++ crates/wallets/src/wallet_signer.rs | 12 +++++++++++- 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 2a3f08090..f378f0e72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4103,6 +4103,7 @@ dependencies = [ "aws-sdk-kms", "clap", "derive_builder", + "eth-keystore", "eyre", "foundry-config", "gcloud-sdk", diff --git a/crates/wallets/Cargo.toml b/crates/wallets/Cargo.toml index f0e3ddc11..89c1ed2e9 100644 --- a/crates/wallets/Cargo.toml +++ b/crates/wallets/Cargo.toml @@ -45,6 +45,7 @@ rpassword = "7" serde.workspace = true thiserror.workspace = true tracing.workspace = true +eth-keystore = "0.5.0" [dev-dependencies] tokio = { workspace = true, features = ["macros"] } diff --git a/crates/wallets/src/error.rs b/crates/wallets/src/error.rs index a5ee5ec1c..9deb037b7 100644 --- a/crates/wallets/src/error.rs +++ b/crates/wallets/src/error.rs @@ -22,6 +22,8 @@ pub enum PrivateKeyError { pub enum WalletSignerError { #[error(transparent)] Local(#[from] LocalSignerError), + #[error("Failed to decrypt keystore: incorrect password")] + IncorrectKeystorePassword, #[error(transparent)] Ledger(#[from] LedgerError), #[error(transparent)] diff --git a/crates/wallets/src/wallet_signer.rs b/crates/wallets/src/wallet_signer.rs index 75441f683..a7bc1a312 100644 --- a/crates/wallets/src/wallet_signer.rs +++ b/crates/wallets/src/wallet_signer.rs @@ -263,7 +263,17 @@ impl PendingSigner { match self { Self::Keystore(path) => { let password = rpassword::prompt_password("Enter keystore password:")?; - Ok(WalletSigner::Local(PrivateKeySigner::decrypt_keystore(path, password)?)) + match PrivateKeySigner::decrypt_keystore(path, password) { + Ok(signer) => Ok(WalletSigner::Local(signer)), + Err(e) => match e { + // Catch the `MacMismatch` error, which indicates an incorrect password and + // return a more user-friendly `IncorrectKeystorePassword`. + alloy_signer_local::LocalSignerError::EthKeystoreError( + eth_keystore::KeystoreError::MacMismatch, + ) => Err(WalletSignerError::IncorrectKeystorePassword), + _ => Err(WalletSignerError::Local(e)), + }, + } } Self::Interactive => { let private_key = rpassword::prompt_password("Enter private key:")?; From c99854277c346fa6de7a8f9837230b36fd85850e Mon Sep 17 00:00:00 2001 From: James Kim Date: Wed, 31 Jul 2024 19:11:20 +0900 Subject: [PATCH 034/184] fix(anvil): fix incorrect op-stack deposit tx hashes (#8567) * fix encoding and hash for deposit tx * rename * add it test * remove comments * fix suggested changes --- crates/anvil/core/src/eth/transaction/mod.rs | 29 ++++- .../core/src/eth/transaction/optimism.rs | 110 ++++++++++++++++-- crates/anvil/tests/it/optimism.rs | 72 +++++++++++- 3 files changed, 197 insertions(+), 14 deletions(-) diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 5aeb2cd0d..878f4bf0f 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -925,7 +925,7 @@ impl TypedTransaction { /// This appends the `address` before hashing it #[cfg(feature = "impersonated-tx")] pub fn impersonated_hash(&self, sender: Address) -> B256 { - let mut buffer = Vec::::new(); + let mut buffer = Vec::new(); Encodable::encode(self, &mut buffer); buffer.extend_from_slice(sender.as_ref()); B256::from_slice(alloy_primitives::utils::keccak256(&buffer).as_slice()) @@ -1101,7 +1101,7 @@ impl Decodable for TypedTransaction { if ty != 0x7E { Ok(TxEnvelope::decode(buf)?.into()) } else { - Ok(Self::Deposit(DepositTransaction::decode(&mut h_decode_copy)?)) + Ok(Self::Deposit(DepositTransaction::decode_2718(buf)?)) } } } @@ -1133,8 +1133,7 @@ impl Encodable2718 for TypedTransaction { Self::EIP4844(tx) => TxEnvelope::from(tx.clone()).encode_2718(out), Self::EIP7702(tx) => tx.tx().encode_with_signature(tx.signature(), out, false), Self::Deposit(tx) => { - out.put_u8(0x7E); - tx.encode(out); + tx.encode_2718(out); } } } @@ -1697,6 +1696,28 @@ mod tests { assert_eq!(from, address!("A83C816D4f9b2783761a22BA6FADB0eB0606D7B2")); } + #[test] + fn test_decode_encode_deposit_tx() { + // https://sepolia-optimism.etherscan.io/tx/0xbf8b5f08c43e4b860715cd64fc0849bbce0d0ea20a76b269e7bc8886d112fca7 + let tx_hash: TxHash = "0xbf8b5f08c43e4b860715cd64fc0849bbce0d0ea20a76b269e7bc8886d112fca7" + .parse::() + .unwrap(); + + // https://sepolia-optimism.etherscan.io/getRawTx?tx=0xbf8b5f08c43e4b860715cd64fc0849bbce0d0ea20a76b269e7bc8886d112fca7 + let raw_tx = alloy_primitives::hex::decode( + "7ef861a0dfd7ae78bf3c414cfaa77f13c0205c82eb9365e217b2daa3448c3156b69b27ac94778f2146f48179643473b82931c4cd7b8f153efd94778f2146f48179643473b82931c4cd7b8f153efd872386f26fc10000872386f26fc10000830186a08080", + ) + .unwrap(); + let dep_tx = TypedTransaction::decode(&mut raw_tx.as_slice()).unwrap(); + + let mut encoded = Vec::new(); + dep_tx.encode_2718(&mut encoded); + + assert_eq!(raw_tx, encoded); + + assert_eq!(tx_hash, dep_tx.hash()); + } + #[test] fn can_recover_sender_not_normalized() { let bytes = hex::decode("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap(); diff --git a/crates/anvil/core/src/eth/transaction/optimism.rs b/crates/anvil/core/src/eth/transaction/optimism.rs index f2e4cff26..fb987ae3d 100644 --- a/crates/anvil/core/src/eth/transaction/optimism.rs +++ b/crates/anvil/core/src/eth/transaction/optimism.rs @@ -1,11 +1,14 @@ -use alloy_consensus::{SignableTransaction, Signed, Transaction, TxType}; +use alloy_consensus::{SignableTransaction, Signed, Transaction}; use alloy_primitives::{keccak256, Address, Bytes, ChainId, Signature, TxKind, B256, U256}; use alloy_rlp::{ length_of_length, Decodable, Encodable, Error as DecodeError, Header as RlpHeader, }; +use bytes::BufMut; use serde::{Deserialize, Serialize}; use std::mem; +pub const DEPOSIT_TX_TYPE_ID: u8 = 0x7E; + #[derive(Clone, Debug, PartialEq, Eq)] pub struct DepositTransactionRequest { pub source_hash: B256, @@ -20,13 +23,17 @@ pub struct DepositTransactionRequest { impl DepositTransactionRequest { pub fn hash(&self) -> B256 { - B256::from_slice(alloy_primitives::keccak256(alloy_rlp::encode(self)).as_slice()) + let mut encoded = Vec::new(); + encoded.put_u8(DEPOSIT_TX_TYPE_ID); + self.encode(&mut encoded); + + B256::from_slice(alloy_primitives::keccak256(encoded).as_slice()) } /// Encodes only the transaction's fields into the desired buffer, without a RLP header. pub(crate) fn encode_fields(&self, out: &mut dyn alloy_rlp::BufMut) { - self.from.encode(out); self.source_hash.encode(out); + self.from.encode(out); self.kind.encode(out); self.mint.encode(out); self.value.encode(out); @@ -103,8 +110,8 @@ impl DepositTransactionRequest { } /// Get transaction type - pub(crate) const fn tx_type(&self) -> TxType { - TxType::Eip1559 + pub(crate) const fn tx_type(&self) -> u8 { + DEPOSIT_TX_TYPE_ID } /// Calculates a heuristic for the in-memory size of the [DepositTransaction] transaction. @@ -121,7 +128,7 @@ impl DepositTransactionRequest { /// Encodes the legacy transaction in RLP for signing. pub(crate) fn encode_for_signing(&self, out: &mut dyn alloy_rlp::BufMut) { - out.put_u8(self.tx_type() as u8); + out.put_u8(self.tx_type()); alloy_rlp::Header { list: true, payload_length: self.fields_len() }.encode(out); self.encode_fields(out); } @@ -247,7 +254,9 @@ impl DepositTransaction { } pub fn hash(&self) -> B256 { - B256::from_slice(alloy_primitives::keccak256(alloy_rlp::encode(self)).as_slice()) + let mut encoded = Vec::new(); + self.encode_2718(&mut encoded); + B256::from_slice(alloy_primitives::keccak256(encoded).as_slice()) } // /// Recovers the Ethereum address which was used to sign the transaction. @@ -259,9 +268,13 @@ impl DepositTransaction { None } + pub(crate) fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) { + out.put_u8(DEPOSIT_TX_TYPE_ID); + self.encode(out); + } + /// Encodes only the transaction's fields into the desired buffer, without a RLP header. pub(crate) fn encode_fields(&self, out: &mut dyn alloy_rlp::BufMut) { - self.nonce.encode(out); self.source_hash.encode(out); self.from.encode(out); self.kind.encode(out); @@ -286,6 +299,20 @@ impl DepositTransaction { len } + pub fn decode_2718(buf: &mut &[u8]) -> Result { + use bytes::Buf; + + let tx_type = *buf.first().ok_or(alloy_rlp::Error::Custom("empty slice"))?; + + if tx_type != DEPOSIT_TX_TYPE_ID { + return Err(alloy_rlp::Error::Custom("invalid tx type: expected deposit tx type")); + } + + // Skip the tx type byte + buf.advance(1); + Self::decode(buf) + } + /// Decodes the inner fields from RLP bytes /// /// NOTE: This assumes a RLP header has already been decoded, and _just_ decodes the following @@ -325,7 +352,6 @@ impl Decodable for DepositTransaction { fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { let header = RlpHeader::decode(buf)?; let remaining_len = buf.len(); - if header.payload_length > remaining_len { return Err(alloy_rlp::Error::InputTooShort); } @@ -333,3 +359,69 @@ impl Decodable for DepositTransaction { Self::decode_inner(buf) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_encode_decode() { + let tx = DepositTransaction { + nonce: 0, + source_hash: B256::default(), + from: Address::default(), + kind: TxKind::Call(Address::default()), + mint: U256::from(100), + value: U256::from(100), + gas_limit: 50000, + is_system_tx: false, + input: Bytes::default(), + }; + + let encoded_tx: Vec = alloy_rlp::encode(&tx); + + let decoded_tx = DepositTransaction::decode(&mut encoded_tx.as_slice()).unwrap(); + + assert_eq!(tx, decoded_tx); + } + #[test] + fn test_encode_decode_2718() { + let tx = DepositTransaction { + nonce: 0, + source_hash: B256::default(), + from: Address::default(), + kind: TxKind::Call(Address::default()), + mint: U256::from(100), + value: U256::from(100), + gas_limit: 50000, + is_system_tx: false, + input: Bytes::default(), + }; + + let mut encoded_tx: Vec = Vec::new(); + tx.encode_2718(&mut encoded_tx); + + let decoded_tx = DepositTransaction::decode_2718(&mut encoded_tx.as_slice()).unwrap(); + + assert_eq!(tx, decoded_tx); + } + + #[test] + fn test_tx_request_hash_equals_tx_hash() { + let tx = DepositTransaction { + nonce: 0, + source_hash: B256::default(), + from: Address::default(), + kind: TxKind::Call(Address::default()), + mint: U256::from(100), + value: U256::from(100), + gas_limit: 50000, + is_system_tx: false, + input: Bytes::default(), + }; + + let tx_request = DepositTransactionRequest::from(tx.clone()); + + assert_eq!(tx.hash(), tx_request.hash()); + } +} diff --git a/crates/anvil/tests/it/optimism.rs b/crates/anvil/tests/it/optimism.rs index c355d11e8..8e23d7ded 100644 --- a/crates/anvil/tests/it/optimism.rs +++ b/crates/anvil/tests/it/optimism.rs @@ -3,7 +3,7 @@ use crate::utils::http_provider_with_signer; use alloy_eips::eip2718::Encodable2718; use alloy_network::{EthereumWallet, TransactionBuilder}; -use alloy_primitives::{b256, U256}; +use alloy_primitives::{b256, Address, TxHash, U256}; use alloy_provider::Provider; use alloy_rpc_types::{optimism::OptimismTransactionFields, TransactionRequest}; use alloy_serde::WithOtherFields; @@ -144,3 +144,73 @@ async fn test_send_value_raw_deposit_transaction() { let after_balance_to = provider.get_balance(to).await.unwrap(); assert_eq!(after_balance_to, before_balance_to + send_value); } + +#[tokio::test(flavor = "multi_thread")] +async fn test_deposit_transaction_hash_matches_sepolia() { + // enable the Optimism flag + let (api, handle) = + spawn(NodeConfig::test().with_optimism(true).with_hardfork(Some(Hardfork::Paris))).await; + + let accounts: Vec<_> = handle.dev_wallets().collect(); + let signer: EthereumWallet = accounts[0].clone().into(); + let sender_addr = accounts[0].address(); + + // https://sepolia-optimism.etherscan.io/tx/0xbf8b5f08c43e4b860715cd64fc0849bbce0d0ea20a76b269e7bc8886d112fca7 + let tx_hash: TxHash = "0xbf8b5f08c43e4b860715cd64fc0849bbce0d0ea20a76b269e7bc8886d112fca7" + .parse::() + .unwrap(); + + // https://sepolia-optimism.etherscan.io/getRawTx?tx=0xbf8b5f08c43e4b860715cd64fc0849bbce0d0ea20a76b269e7bc8886d112fca7 + let raw_deposit_tx = alloy_primitives::hex::decode( + "7ef861a0dfd7ae78bf3c414cfaa77f13c0205c82eb9365e217b2daa3448c3156b69b27ac94778f2146f48179643473b82931c4cd7b8f153efd94778f2146f48179643473b82931c4cd7b8f153efd872386f26fc10000872386f26fc10000830186a08080", + ) + .unwrap(); + let deposit_tx_from = "0x778F2146F48179643473B82931c4CD7B8F153eFd".parse::
().unwrap(); + + let provider = http_provider_with_signer(&handle.http_endpoint(), signer.clone()); + + // TODO: necessary right now because transaction validation fails for deposit tx + // with `from` account that doesn't have sufficient ETH balance. + // Should update the tx validation logic for deposit tx to + // 1. check if `tx.value > account.balance + tx.mint` + // 2. don't check `account.balance > gas * price + value` (the gas costs have been prepaid on + // L1) + // source: https://specs.optimism.io/protocol/deposits.html#execution + let fund_account_tx = TransactionRequest::default() + .with_chain_id(31337) + .with_nonce(0) + .with_from(sender_addr) + .with_to(deposit_tx_from) + .with_value(U256::from(1e18)) + .with_gas_limit(21_000) + .with_max_fee_per_gas(20_000_000_000) + .with_max_priority_fee_per_gas(1_000_000_000); + + provider + .send_transaction(WithOtherFields::new(fund_account_tx)) + .await + .unwrap() + .register() + .await + .unwrap(); + + // mine block + api.evm_mine(None).await.unwrap(); + + let pending = provider + .send_raw_transaction(raw_deposit_tx.as_slice()) + .await + .unwrap() + .register() + .await + .unwrap(); + + // mine block + api.evm_mine(None).await.unwrap(); + + let receipt = + provider.get_transaction_receipt(pending.tx_hash().to_owned()).await.unwrap().unwrap(); + + assert_eq!(pending.tx_hash(), &tx_hash); + assert_eq!(receipt.transaction_hash, tx_hash); +} From 0951fb55bf31789f1c6a24d944deea3eca09fd3a Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 1 Aug 2024 18:19:30 +0300 Subject: [PATCH 035/184] fix(fmt): surround with returns in fn with format disabled (#8582) fix(fmt): surround returns if fn format disabled --- crates/fmt/src/formatter.rs | 12 +++++++++++- .../FunctionDefinitionWithFunctionReturns/fmt.sol | 8 ++++++++ .../original.sol | 8 ++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/crates/fmt/src/formatter.rs b/crates/fmt/src/formatter.rs index 94052d9a9..7dda996f8 100644 --- a/crates/fmt/src/formatter.rs +++ b/crates/fmt/src/formatter.rs @@ -1537,7 +1537,17 @@ impl<'a, W: Write> Formatter<'a, W> { let returns_start_loc = func.returns.first().unwrap().0; let returns_loc = returns_start_loc.with_end_from(&func.returns.last().unwrap().0); if fmt.inline_config.is_disabled(returns_loc) { - fmt.indented(1, |fmt| fmt.visit_source(returns_loc))?; + fmt.indented(1, |fmt| { + fmt.surrounded( + SurroundingChunk::new("returns (", Some(returns_loc.start()), None), + SurroundingChunk::new(")", None, returns_end), + |fmt, _| { + fmt.visit_source(returns_loc)?; + Ok(()) + }, + )?; + Ok(()) + })?; } else { let mut returns = fmt.items_to_chunks( returns_end, diff --git a/crates/fmt/testdata/FunctionDefinitionWithFunctionReturns/fmt.sol b/crates/fmt/testdata/FunctionDefinitionWithFunctionReturns/fmt.sol index 556db3698..7b751e22e 100644 --- a/crates/fmt/testdata/FunctionDefinitionWithFunctionReturns/fmt.sol +++ b/crates/fmt/testdata/FunctionDefinitionWithFunctionReturns/fmt.sol @@ -11,3 +11,11 @@ contract ReturnFnFormat { ) {} } + +// https://github.com/foundry-rs/foundry/issues/7920 +contract ReturnFnDisableFormat { + // forgefmt: disable-next-line + function disableFnFormat() external returns (uint256) { + return 0; + } +} diff --git a/crates/fmt/testdata/FunctionDefinitionWithFunctionReturns/original.sol b/crates/fmt/testdata/FunctionDefinitionWithFunctionReturns/original.sol index 79008d44a..0c785cde8 100644 --- a/crates/fmt/testdata/FunctionDefinitionWithFunctionReturns/original.sol +++ b/crates/fmt/testdata/FunctionDefinitionWithFunctionReturns/original.sol @@ -10,4 +10,12 @@ contract ReturnFnFormat { internal pure returns (uint256) ) {} +} + +// https://github.com/foundry-rs/foundry/issues/7920 +contract ReturnFnDisableFormat { + // forgefmt: disable-next-line + function disableFnFormat() external returns (uint256) { + return 0; + } } \ No newline at end of file From 626221f5ef44b4af950a08e09bd714650d9eb77d Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 1 Aug 2024 19:38:15 +0200 Subject: [PATCH 036/184] chore: bump compilers 0.10.2 (#8583) chore: bump compilrs 0.10.2 --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f378f0e72..72854e518 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3709,9 +3709,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c47e9b1b142be502b089e9699922110b6f15906f739b8e612755167da4f5a1" +checksum = "97b8ffe1d5a00cd78a9461262377270d88b8d6a8a5f51b402996242bccef3994" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3747,9 +3747,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a8e5d34b4b594806808c200340b24fa1a60b9f65b76ecf0df406c52c0e4019a" +checksum = "9cdb80803e20447fc8c3f4ec97d47ad5fa37286648bb8224edbbc553ebe1a0f4" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3757,9 +3757,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c7481a171c86d76cdd257eeb99e15d532f94502efa51f76858e2196d85840e" +checksum = "3280cf657d802358856a397cb8465b18a0a6c09b1fa6422842e422a9aa21276d" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3781,9 +3781,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32523fa23051d11d534493c93e00a482e27abbbb615ca6d646ebe30b749624db" +checksum = "22ecc61aa540bff773d4441a94e0f158769fcedd61f61d3e91608a76d6bcd7aa" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3796,9 +3796,9 @@ dependencies = [ [[package]] name = "foundry-compilers-core" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e03290dd4cd78b076a43e6f8528f240d3ec74c0a56795830327d6dd781a7ac0" +checksum = "a14603a33a217e64cc38977c215b01b37b48a0cae0a739a9f9b3555f16938704" dependencies = [ "alloy-primitives", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index a33e01642..7c437ab63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -158,7 +158,7 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.5.1", default-features = false } -foundry-compilers = { version = "0.10.1", default-features = false } +foundry-compilers = { version = "0.10.2", default-features = false } foundry-fork-db = "0.2" solang-parser = "=0.3.3" From d856669d9b614a83834c664f443e18194fcdc781 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 2 Aug 2024 14:44:22 +0200 Subject: [PATCH 037/184] chore: bump alloy 0.2.1 (#8586) --- Cargo.lock | 280 +++++++++++++----------- Cargo.toml | 44 ++-- crates/anvil/src/eth/api.rs | 7 +- crates/anvil/src/eth/backend/fork.rs | 9 +- crates/anvil/src/eth/backend/mem/mod.rs | 16 +- crates/anvil/src/eth/otterscan/api.rs | 8 +- crates/anvil/tests/it/otterscan.rs | 2 +- crates/anvil/tests/it/traces.rs | 2 +- crates/anvil/tests/it/transaction.rs | 2 +- crates/common/fmt/src/ui.rs | 2 +- 10 files changed, 201 insertions(+), 171 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 72854e518..1498a39a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,7 +48,7 @@ dependencies = [ "getrandom", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -79,9 +79,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f58047cc851e58c26224521d1ecda466e3d746ebca0274cd5427aa660a88c353" +checksum = "04c309895995eaa4bfcc345f5515a39c7df9447798645cc8bf462b6c5bf1dc96" dependencies = [ "alloy-eips", "alloy-primitives", @@ -93,13 +93,14 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa5d42d9f87896536234b0fac1a84ad9d9dc7a4b27839cac35d0899e64ddf083" +checksum = "3f4e0ef72b0876ae3068b2ed7dfae9ae1779ce13cfaec2ee1f08f5bd0348dc57" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", "alloy-network", + "alloy-network-primitives", "alloy-primitives", "alloy-provider", "alloy-pubsub", @@ -129,14 +130,14 @@ dependencies = [ "proptest", "serde", "serde_json", - "winnow 0.6.16", + "winnow 0.6.18", ] [[package]] name = "alloy-eips" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a3e14fa0d152d00bd8daf605eb74ad397efb0f54bd7155585823dddb4401e" +checksum = "d9431c99a3b3fe606ede4b3d4043bdfbcb780c45b8d8d226c3804e2b75cfbe68" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -146,15 +147,16 @@ dependencies = [ "derive_more", "k256", "once_cell", + "rand", "serde", "sha2", ] [[package]] name = "alloy-genesis" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cb76c8a3913f2466c5488f3a915e3a15d15596bdc935558c1a9be75e9ec508" +checksum = "79614dfe86144328da11098edcc7bc1a3f25ad8d3134a9eb9e857e06f0d9840d" dependencies = [ "alloy-primitives", "alloy-serde", @@ -175,11 +177,12 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e76a9feec2352c78545d1a37415699817bae8dc41654bd1bfe57d6cdd5433bd" +checksum = "57e2865c4c3bb4cdad3f0d9ec1ab5c0c657ba69a375651bd35e32fb6c180ccc2" dependencies = [ "alloy-primitives", + "alloy-sol-types", "serde", "serde_json", "thiserror", @@ -188,13 +191,14 @@ dependencies = [ [[package]] name = "alloy-network" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3223d71dc78f464b2743418d0be8b5c894313e272105a6206ad5e867d67b3ce2" +checksum = "6e701fc87ef9a3139154b0b4ccb935b565d27ffd9de020fe541bf2dec5ae4ede" dependencies = [ "alloy-consensus", "alloy-eips", "alloy-json-rpc", + "alloy-network-primitives", "alloy-primitives", "alloy-rpc-types-eth", "alloy-serde", @@ -206,6 +210,17 @@ dependencies = [ "thiserror", ] +[[package]] +name = "alloy-network-primitives" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec9d5a0f9170b10988b6774498a022845e13eda94318440d17709d50687f67f9" +dependencies = [ + "alloy-primitives", + "alloy-serde", + "serde", +] + [[package]] name = "alloy-primitives" version = "0.7.7" @@ -235,15 +250,16 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29da7457d853cb8199ec04b227d5d2ef598be3e59fc2bbad70c8be213292f32" +checksum = "3f9c0ab10b93de601a6396fc7ff2ea10d3b28c46f079338fa562107ebf9857c8" dependencies = [ "alloy-chains", "alloy-consensus", "alloy-eips", "alloy-json-rpc", "alloy-network", + "alloy-network-primitives", "alloy-primitives", "alloy-pubsub", "alloy-rpc-client", @@ -272,9 +288,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64acfec654ade392cecfa9bba0408eb2a337d55f1b857925da79970cb70f3d6" +checksum = "3f5da2c55cbaf229bad3c5f8b00b5ab66c74ef093e5f3a753d874cfecf7d2281" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -313,9 +329,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a9e609524fa31c2c70eb24c0da60796809193ad4787a6dfe6d0db0d3ac112d" +checksum = "5b38e3ffdb285df5d9f60cb988d336d9b8e3505acb78750c3bc60336a7af41d3" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -338,9 +354,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e5d76f1e8b22f48b7b8f985782b68e7eb3938780e50e8b646a53e41a598cdf5" +checksum = "e6c31a3750b8f5a350d17354e46a52b0f2f19ec5f2006d816935af599dedc521" dependencies = [ "alloy-rpc-types-anvil", "alloy-rpc-types-engine", @@ -353,9 +369,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4282c002a4ae9f57887dae57083fcca6dca09cb6685bf98b8582ea93cb3df97d" +checksum = "52ab6509cd38b2e8c8da726e0f61c1e314a81df06a38d37ddec8bced3f8d25ed" dependencies = [ "alloy-primitives", "alloy-serde", @@ -364,9 +380,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73445fbc5c02258e3d0d977835c92366a4d91545fd456c3fc8601c61810bc9f6" +checksum = "ff63f51b2fb2f547df5218527fd0653afb1947bf7fead5b3ce58c75d170b30f7" dependencies = [ "alloy-consensus", "alloy-eips", @@ -382,12 +398,13 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "605fa8462732bb8fd0645a9941e12961e079d45ae6a44634c826f8229c187bdf" +checksum = "81e18424d962d7700a882fe423714bd5b9dde74c7a7589d4255ea64068773aef" dependencies = [ "alloy-consensus", "alloy-eips", + "alloy-network-primitives", "alloy-primitives", "alloy-rlp", "alloy-serde", @@ -400,9 +417,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f561a8cdd377b6ac3beab805b9df5ec2c7d99bb6139aab23c317f26df6fb346" +checksum = "a86eeb49ea0cc79f249faa1d35c20541bb1c317a59b5962cb07b1890355b0064" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -414,9 +431,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06a4bd39910631c11148c5b2c55e2c61f8626affd2a612e382c668d5e5971ce" +checksum = "c2342fed8175642b15a37a51f8729b05b2469281fbeb816f0ccbb0087e2dd74a" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -426,9 +443,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c5b9057acc02aee1b8aac2b5a0729cb0f73d080082c111313e5d1f92a96630" +checksum = "e33feda6a53e6079895aed1d08dcb98a1377b000d80d16370fbbdb8155d547ef" dependencies = [ "alloy-primitives", "arbitrary", @@ -438,9 +455,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37f10592696f4ab8b687d5a8ab55e998a14ea0ca5f8eb20ad74a96ad671bb54a" +checksum = "740a25b92e849ed7b0fa013951fe2f64be9af1ad5abe805037b44fb7770c5c47" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -454,9 +471,9 @@ dependencies = [ [[package]] name = "alloy-signer-aws" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49300a7aecbd28c364fbad6a9f886264f79ff4fed9a67c8caa27c39f99d52b2d" +checksum = "8b1a47bd8487fb2d715f8a203c3bfe7de0b7443eeacb00bd96d8d4eb0d67e184" dependencies = [ "alloy-consensus", "alloy-network", @@ -472,9 +489,9 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce638c267429ea7513be9fffc47d949d1f425a33c8813fc6a145e03b999e79f" +checksum = "0850956080821ee646461fde2ff17640a2babcd206046a867eda94d9a266fad9" dependencies = [ "alloy-consensus", "alloy-network", @@ -490,9 +507,9 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9450ae05631ac2a5eb180d91d7162bf71ea7a2bb6833cc7c25997e5a11dc38" +checksum = "0f4f7e76cb4f63dbb56857a74665510517a013fe18da82082f7c66c6d321531e" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -510,9 +527,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b537f3e55f30753578f4623d5f66ddad8fa582af3fa6b15bad23dd1b9775228" +checksum = "1b0707d4f63e4356a110b30ef3add8732ab6d181dd7be4607bf79b8777105cee" dependencies = [ "alloy-consensus", "alloy-network", @@ -530,9 +547,9 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6efa624373339e7cbdd597a785a69c5fcbc10d5368797a18b7cb3476eadf8c9" +checksum = "81147fb1a384f878653524b9473af71ef2820846bd64a473f26e49fca8e5f8f9" dependencies = [ "alloy-consensus", "alloy-network", @@ -569,7 +586,7 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.2.6", + "indexmap 2.3.0", "proc-macro-error", "proc-macro2", "quote", @@ -602,7 +619,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbcba3ca07cf7975f15d871b721fb18031eec8bce51103907f6dcce00b255d98" dependencies = [ "serde", - "winnow 0.6.16", + "winnow 0.6.18", ] [[package]] @@ -620,9 +637,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b44b0f6f4a2593b258fa7b6cae8968e6a4c404d9ef4f5bc74401f2d04fa23fa" +checksum = "3d0590afbdacf2f8cca49d025a2466f3b6584a016a8b28f532f29f8da1007bae" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -639,9 +656,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d8f1eefa8cb9e7550740ee330feba4fed303a77ad3085707546f9152a88c380" +checksum = "2437d145d80ea1aecde8574d2058cceb8b3c9cba05f6aea8e67907c660d46698" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -654,9 +671,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31007c56dc65bd81392112dda4a14c20ac7e30bb4cb2e9176192e8d9fab1983f" +checksum = "804494366e20468776db4e18f9eb5db7db0fe14f1271eb6dbf155d867233405c" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -675,9 +692,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15ccc1c8f8ae415e93ec0e7851bd4cdf4afdd48793d13a91b860317da1f36104" +checksum = "af855163e7df008799941aa6dd324a43ef2bf264b08ba4b22d44aad6ced65300" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -1793,9 +1810,9 @@ checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "bytemuck" -version = "1.16.1" +version = "1.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" +checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83" [[package]] name = "byteorder" @@ -1805,9 +1822,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.1" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" dependencies = [ "serde", ] @@ -2081,9 +2098,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.11" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3" +checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc" dependencies = [ "clap_builder", "clap_derive", @@ -2091,9 +2108,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.11" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa" +checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99" dependencies = [ "anstream", "anstyle", @@ -2106,9 +2123,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.11" +version = "4.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6ae69fbb0833c6fcd5a8d4b8609f108c7ad95fc11e248d853ff2c42a90df26a" +checksum = "a8670053e87c316345e384ca1f3eba3006fc6355ed8b8a1140d104e109e3df34" dependencies = [ "clap", ] @@ -2125,9 +2142,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.11" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" dependencies = [ "heck", "proc-macro2", @@ -3080,7 +3097,7 @@ dependencies = [ "serde", "serde_json", "syn 2.0.72", - "toml 0.8.16", + "toml 0.8.19", "walkdir", ] @@ -3231,7 +3248,7 @@ dependencies = [ "pear", "serde", "tempfile", - "toml 0.8.16", + "toml 0.8.19", "uncased", "version_check", ] @@ -3376,8 +3393,8 @@ dependencies = [ "thiserror", "tikv-jemallocator", "tokio", - "toml 0.8.16", - "toml_edit 0.22.17", + "toml 0.8.19", + "toml_edit 0.22.20", "tower-http", "tracing", "tracing-subscriber", @@ -3408,7 +3425,7 @@ dependencies = [ "serde_json", "solang-parser", "thiserror", - "toml 0.8.16", + "toml 0.8.19", "tracing", ] @@ -3423,7 +3440,7 @@ dependencies = [ "similar-asserts", "solang-parser", "thiserror", - "toml 0.8.16", + "toml 0.8.19", "tracing", "tracing-subscriber", ] @@ -3588,7 +3605,7 @@ dependencies = [ "semver 1.0.23", "serde_json", "thiserror", - "toml 0.8.16", + "toml 0.8.19", "tracing", "vergen", "walkdir", @@ -3741,7 +3758,7 @@ dependencies = [ "thiserror", "tokio", "tracing", - "winnow 0.6.16", + "winnow 0.6.18", "yansi", ] @@ -3847,8 +3864,8 @@ dependencies = [ "solang-parser", "tempfile", "thiserror", - "toml 0.8.16", - "toml_edit 0.22.17", + "toml 0.8.19", + "toml_edit 0.22.20", "tracing", "walkdir", ] @@ -3976,7 +3993,7 @@ dependencies = [ "foundry-evm-core", "foundry-evm-coverage", "foundry-evm-traces", - "indexmap 2.2.6", + "indexmap 2.3.0", "itertools 0.13.0", "parking_lot", "proptest", @@ -4353,7 +4370,7 @@ dependencies = [ "gix-utils", "itoa", "thiserror", - "winnow 0.6.16", + "winnow 0.6.18", ] [[package]] @@ -4374,7 +4391,7 @@ dependencies = [ "smallvec", "thiserror", "unicode-bom", - "winnow 0.6.16", + "winnow 0.6.18", ] [[package]] @@ -4476,7 +4493,7 @@ dependencies = [ "itoa", "smallvec", "thiserror", - "winnow 0.6.16", + "winnow 0.6.18", ] [[package]] @@ -4511,7 +4528,7 @@ dependencies = [ "gix-validate", "memmap2", "thiserror", - "winnow 0.6.16", + "winnow 0.6.18", ] [[package]] @@ -4607,7 +4624,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.2.6", + "indexmap 2.3.0", "slab", "tokio", "tokio-util", @@ -5073,9 +5090,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" dependencies = [ "equivalent", "hashbrown 0.14.5", @@ -5456,9 +5473,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ "hashbrown 0.14.5", ] @@ -6284,7 +6301,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.2.6", + "indexmap 2.3.0", ] [[package]] @@ -6478,12 +6495,11 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.19" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2288c0e17cc8d342c712bb43a257a80ebffce59cdb33d5000d8348f3ec02528b" +checksum = "dee4364d9f3b902ef14fab8a1ddffb783a1cb6b4bba3bfc1fa3922732c7de97f" dependencies = [ - "zerocopy", - "zerocopy-derive", + "zerocopy 0.6.6", ] [[package]] @@ -6613,7 +6629,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38ee68ae331824036479c84060534b18254c864fa73366c58d86db3b7b811619" dependencies = [ "futures", - "indexmap 2.2.6", + "indexmap 2.3.0", "nix 0.28.0", "tokio", "tracing", @@ -7132,9 +7148,9 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5296ccad8d7ccbeb6c5a037a57bfe1ff27e81d8c4efbd3ae7df0a554eb1a818a" +checksum = "54a785dafff303a335980e317669c4e9800cdd5dd2830c6880c3247022761e88" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -7790,11 +7806,11 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.121" +version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" +checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.3.0", "itoa", "memchr", "ryu", @@ -7859,7 +7875,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.3.0", "itoa", "ryu", "serde", @@ -8149,13 +8165,13 @@ dependencies = [ "sha256", "simple-home-dir", "tokio", - "toml 0.8.16", - "toml_edit 0.22.17", + "toml 0.8.19", + "toml_edit 0.22.20", "uuid 1.10.0", "walkdir", "yansi", "yash-fnmatch", - "zip 2.1.5", + "zip 2.1.6", "zip-extract", ] @@ -8287,7 +8303,7 @@ dependencies = [ "sha2", "thiserror", "url", - "zip 2.1.5", + "zip 2.1.6", ] [[package]] @@ -8710,22 +8726,22 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.16" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81967dd0dd2c1ab0bc3468bd7caecc32b8a4aa47d0c8c695d8c2b2108168d62c" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.3.0", "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.17", + "toml_edit 0.22.20", ] [[package]] name = "toml_datetime" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] @@ -8736,22 +8752,22 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.3.0", "toml_datetime", "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.22.17" +version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9f8729f5aea9562aac1cc0441f5d6de3cff1ee0c5d67293eeca5eb36ee7c16" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.3.0", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.16", + "winnow 0.6.18", ] [[package]] @@ -9744,9 +9760,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.16" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b480ae9340fc261e6be3e95a1ba86d54ae3f9171132a73ce8d4bbaf68339507c" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] @@ -9831,14 +9847,34 @@ dependencies = [ "thiserror", ] +[[package]] +name = "zerocopy" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6" +dependencies = [ + "byteorder", + "zerocopy-derive 0.6.6", +] + [[package]] name = "zerocopy" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy-derive" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", ] [[package]] @@ -9893,16 +9929,16 @@ dependencies = [ [[package]] name = "zip" -version = "2.1.5" +version = "2.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b895748a3ebcb69b9d38dcfdf21760859a4b0d0b0015277640c2ef4c69640e6f" +checksum = "40dd8c92efc296286ce1fbd16657c5dbefff44f1b4ca01cc5f517d8b7b3d3e2e" dependencies = [ "arbitrary", "crc32fast", "crossbeam-utils", "displaydoc", "flate2", - "indexmap 2.2.6", + "indexmap 2.3.0", "memchr", "thiserror", "zopfli", diff --git a/Cargo.toml b/Cargo.toml index 7c437ab63..d1646a070 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -172,28 +172,28 @@ revm-inspectors = { version = "0.5", features = ["serde"] } ethers-contract-abigen = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { version = "0.2.0", default-features = false } -alloy-contract = { version = "0.2.0", default-features = false } -alloy-eips = { version = "0.2.0", default-features = false } -alloy-genesis = { version = "0.2.0", default-features = false } -alloy-json-rpc = { version = "0.2.0", default-features = false } -alloy-network = { version = "0.2.0", default-features = false } -alloy-node-bindings = { version = "0.2.0", default-features = false } -alloy-provider = { version = "0.2.0", default-features = false } -alloy-pubsub = { version = "0.2.0", default-features = false } -alloy-rpc-client = { version = "0.2.0", default-features = false } -alloy-rpc-types = { version = "0.2.0", default-features = false } -alloy-serde = { version = "0.2.0", default-features = false } -alloy-signer = { version = "0.2.0", default-features = false } -alloy-signer-aws = { version = "0.2.0", default-features = false } -alloy-signer-gcp = { version = "0.2.0", default-features = false } -alloy-signer-ledger = { version = "0.2.0", default-features = false } -alloy-signer-local = { version = "0.2.0", default-features = false } -alloy-signer-trezor = { version = "0.2.0", default-features = false } -alloy-transport = { version = "0.2.0", default-features = false } -alloy-transport-http = { version = "0.2.0", default-features = false } -alloy-transport-ipc = { version = "0.2.0", default-features = false } -alloy-transport-ws = { version = "0.2.0", default-features = false } +alloy-consensus = { version = "0.2.1", default-features = false } +alloy-contract = { version = "0.2.1", default-features = false } +alloy-eips = { version = "0.2.1", default-features = false } +alloy-genesis = { version = "0.2.1", default-features = false } +alloy-json-rpc = { version = "0.2.1", default-features = false } +alloy-network = { version = "0.2.1", default-features = false } +alloy-node-bindings = { version = "0.2.1", default-features = false } +alloy-provider = { version = "0.2.1", default-features = false } +alloy-pubsub = { version = "0.2.1", default-features = false } +alloy-rpc-client = { version = "0.2.1", default-features = false } +alloy-rpc-types = { version = "0.2.1", default-features = false } +alloy-serde = { version = "0.2.1", default-features = false } +alloy-signer = { version = "0.2.1", default-features = false } +alloy-signer-aws = { version = "0.2.1", default-features = false } +alloy-signer-gcp = { version = "0.2.1", default-features = false } +alloy-signer-ledger = { version = "0.2.1", default-features = false } +alloy-signer-local = { version = "0.2.1", default-features = false } +alloy-signer-trezor = { version = "0.2.1", default-features = false } +alloy-transport = { version = "0.2.1", default-features = false } +alloy-transport-http = { version = "0.2.1", default-features = false } +alloy-transport-ipc = { version = "0.2.1", default-features = false } +alloy-transport-ws = { version = "0.2.1", default-features = false } alloy-dyn-abi = "0.7.7" alloy-json-abi = "0.7.7" diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index cd7485d5f..4f853cd0c 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -48,7 +48,7 @@ use alloy_rpc_types::{ parity::LocalizedTransactionTrace, }, txpool::{TxpoolContent, TxpoolInspect, TxpoolInspectSummary, TxpoolStatus}, - AccessList, AccessListWithGasUsed, Block, BlockId, BlockNumberOrTag as BlockNumber, + AccessList, AccessListResult, Block, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, EIP1186AccountProofResponse, FeeHistory, Filter, FilteredParams, Index, Log, Transaction, }; @@ -1084,7 +1084,7 @@ impl EthApi { &self, mut request: WithOtherFields, block_number: Option, - ) -> Result { + ) -> Result { node_info!("eth_createAccessList"); let block_request = self.block_request(block_number).await?; // check if the number predates the fork, if in fork mode @@ -1117,9 +1117,10 @@ impl EthApi { )?; ensure_return_ok(exit, &out)?; - Ok(AccessListWithGasUsed { + Ok(AccessListResult { access_list: AccessList(access_list.0), gas_used: U256::from(gas_used), + error: None, }) }) .await? diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index d4b51ae96..7f8146daa 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -2,6 +2,7 @@ use crate::eth::{backend::db::Db, error::BlockchainError, pool::transactions::PoolTransaction}; use alloy_consensus::Account; +use alloy_eips::eip2930::AccessListResult; use alloy_primitives::{Address, Bytes, StorageValue, B256, U256}; use alloy_provider::{ ext::{DebugApi, TraceApi}, @@ -13,7 +14,7 @@ use alloy_rpc_types::{ geth::{GethDebugTracingOptions, GethTrace}, parity::LocalizedTransactionTrace as Trace, }, - AccessListWithGasUsed, Block, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, + Block, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, EIP1186AccountProofResponse, FeeHistory, Filter, Log, Transaction, }; use alloy_serde::WithOtherFields; @@ -205,7 +206,7 @@ impl ClientFork { &self, request: &WithOtherFields, block: Option, - ) -> Result { + ) -> Result { self.provider().create_access_list(request).block_id(block.unwrap_or_default().into()).await } @@ -273,7 +274,7 @@ impl ClientFork { blocknumber: u64, ) -> Result { trace!(target: "backend::fork", "get_account={:?}", address); - self.provider().get_account(address).await.block_id(blocknumber.into()).await + self.provider().get_account(address).block_id(blocknumber.into()).await } pub async fn transaction_by_block_number_and_index( @@ -577,7 +578,7 @@ impl ClientFork { }; let mut transactions = Vec::with_capacity(block_txs_len); for tx in block.transactions.hashes() { - if let Some(tx) = storage.transactions.get(tx).cloned() { + if let Some(tx) = storage.transactions.get(&tx).cloned() { transactions.push(tx.inner); } } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index b235646b5..ff7ec5b5c 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -46,10 +46,7 @@ use alloy_rpc_types::{ GethDebugBuiltInTracerType, GethDebugTracerType, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, NoopFrame, }, - parity::{ - Action::{Call, Create, Reward, Selfdestruct}, - LocalizedTransactionTrace, - }, + parity::LocalizedTransactionTrace, }, AccessList, Block as AlloyBlock, BlockId, BlockNumberOrTag as BlockNumber, EIP1186AccountProofResponse as AccountProof, EIP1186StorageProof as StorageProof, Filter, @@ -2095,14 +2092,7 @@ impl Backend { // Execute tasks and filter traces let traces = futures::future::try_join_all(trace_tasks).await?; let filtered_traces = - traces.into_iter().flatten().filter(|trace| match &trace.trace.action { - Call(call) => matcher.matches(call.from, Some(call.to)), - Create(create) => matcher.matches(create.from, None), - Selfdestruct(self_destruct) => { - matcher.matches(self_destruct.address, Some(self_destruct.refund_address)) - } - Reward(reward) => matcher.matches(reward.author, None), - }); + traces.into_iter().flatten().filter(|trace| matcher.matches(&trace.trace)); // Apply after and count let filtered_traces: Vec<_> = if let Some(after) = filter.after { @@ -2126,7 +2116,7 @@ impl Backend { let mut receipts = Vec::new(); let storage = self.blockchain.storage.read(); for tx in block.transactions.hashes() { - let receipt = storage.transactions.get(tx)?.receipt.clone(); + let receipt = storage.transactions.get(&tx)?.receipt.clone(); receipts.push(receipt); } Some(receipts) diff --git a/crates/anvil/src/eth/otterscan/api.rs b/crates/anvil/src/eth/otterscan/api.rs index f174d79f5..9b6b54a80 100644 --- a/crates/anvil/src/eth/otterscan/api.rs +++ b/crates/anvil/src/eth/otterscan/api.rs @@ -349,8 +349,10 @@ impl EthApi { if block.transactions.is_uncle() { return Err(BlockchainError::DataUnavailable); } - let receipts_futs = - block.transactions.hashes().map(|hash| async { self.transaction_receipt(*hash).await }); + let receipts_futs = block + .transactions + .hashes() + .map(|hash| async move { self.transaction_receipt(hash).await }); // fetch all receipts let receipts = join_all(receipts_futs) @@ -398,7 +400,7 @@ impl EthApi { BlockTransactions::Uncle => unreachable!(), }; - let receipt_futs = block.transactions.hashes().map(|hash| self.transaction_receipt(*hash)); + let receipt_futs = block.transactions.hashes().map(|hash| self.transaction_receipt(hash)); let receipts = join_all(receipt_futs) .await diff --git a/crates/anvil/tests/it/otterscan.rs b/crates/anvil/tests/it/otterscan.rs index d0536a032..fc153f963 100644 --- a/crates/anvil/tests/it/otterscan.rs +++ b/crates/anvil/tests/it/otterscan.rs @@ -385,7 +385,7 @@ async fn ots_get_block_transactions() { result.receipts.iter().enumerate().for_each(|(i, receipt)| { let expected = hashes.pop_front(); assert_eq!(expected, Some(receipt.receipt.transaction_hash)); - assert_eq!(expected, result.fullblock.block.transactions.hashes().nth(i).copied()); + assert_eq!(expected, result.fullblock.block.transactions.hashes().nth(i)); }); } diff --git a/crates/anvil/tests/it/traces.rs b/crates/anvil/tests/it/traces.rs index 7f2846d5c..ac6f90fe3 100644 --- a/crates/anvil/tests/it/traces.rs +++ b/crates/anvil/tests/it/traces.rs @@ -806,7 +806,7 @@ async fn test_trace_filter() { } let traces = api.trace_filter(tracer).await.unwrap(); - assert_eq!(traces.len(), 3); + assert_eq!(traces.len(), 8); // Test Range Error let latest = provider.get_block_number().await.unwrap(); diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index 5f9abe80b..0737209c1 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -1233,6 +1233,6 @@ async fn can_mine_multiple_in_block() { let block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); - let txs = block.transactions.hashes().copied().collect::>(); + let txs = block.transactions.hashes().collect::>(); assert_eq!(txs, vec![first, second]); } diff --git a/crates/common/fmt/src/ui.rs b/crates/common/fmt/src/ui.rs index b9deffc7e..77bb22851 100644 --- a/crates/common/fmt/src/ui.rs +++ b/crates/common/fmt/src/ui.rs @@ -278,7 +278,7 @@ transactions: {}", } } -impl UIfmt for BlockTransactions { +impl UIfmt for BlockTransactions { fn pretty(&self) -> String { match self { Self::Hashes(hashes) => hashes.pretty(), From c600237f3e54604274bfdcba627f347493fd21d2 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 2 Aug 2024 17:04:26 +0300 Subject: [PATCH 038/184] fix(fmt): preserve function declaration if fmt disabled (#8587) * fix(fmt): preserve function declaration if fmt disabled * Add test for #3789 --- crates/fmt/src/formatter.rs | 83 +++++++++++++++---- crates/fmt/testdata/InlineDisable/fmt.sol | 17 ++++ .../fmt/testdata/InlineDisable/original.sol | 17 ++++ 3 files changed, 103 insertions(+), 14 deletions(-) diff --git a/crates/fmt/src/formatter.rs b/crates/fmt/src/formatter.rs index 7dda996f8..705e578c9 100644 --- a/crates/fmt/src/formatter.rs +++ b/crates/fmt/src/formatter.rs @@ -1459,7 +1459,8 @@ impl<'a, W: Write> Formatter<'a, W> { self.extend_loc_until(&mut loc, ')'); loc }; - if self.inline_config.is_disabled(params_loc) { + let params_disabled = self.inline_config.is_disabled(params_loc); + if params_disabled { let chunk = self.chunked(func.loc.start(), None, |fmt| fmt.visit_source(params_loc))?; params_multiline = chunk.content.contains('\n'); self.write_chunk(&chunk)?; @@ -1519,7 +1520,16 @@ impl<'a, W: Write> Formatter<'a, W> { .loc() .with_end_from(&func.attributes.last().unwrap().loc()); if fmt.inline_config.is_disabled(attrs_loc) { - fmt.indented(1, |fmt| fmt.visit_source(attrs_loc))?; + // If params are also disabled then write functions attributes on the same line. + if params_disabled { + fmt.write_whitespace_separator(false)?; + let attrs_src = + String::from_utf8(self.source.as_bytes()[attrs_loc.range()].to_vec()) + .map_err(FormatterError::custom)?; + fmt.write_raw(attrs_src)?; + } else { + fmt.indented(1, |fmt| fmt.visit_source(attrs_loc))?; + } } else { fmt.write_postfix_comments_before(attrs_loc.start())?; fmt.write_whitespace_separator(multiline)?; @@ -1537,17 +1547,11 @@ impl<'a, W: Write> Formatter<'a, W> { let returns_start_loc = func.returns.first().unwrap().0; let returns_loc = returns_start_loc.with_end_from(&func.returns.last().unwrap().0); if fmt.inline_config.is_disabled(returns_loc) { - fmt.indented(1, |fmt| { - fmt.surrounded( - SurroundingChunk::new("returns (", Some(returns_loc.start()), None), - SurroundingChunk::new(")", None, returns_end), - |fmt, _| { - fmt.visit_source(returns_loc)?; - Ok(()) - }, - )?; - Ok(()) - })?; + fmt.write_whitespace_separator(false)?; + let returns_src = + String::from_utf8(self.source.as_bytes()[returns_loc.range()].to_vec()) + .map_err(FormatterError::custom)?; + fmt.write_raw(format!("returns ({returns_src})"))?; } else { let mut returns = fmt.items_to_chunks( returns_end, @@ -3036,6 +3040,58 @@ impl<'a, W: Write> Visitor for Formatter<'a, W> { match &mut func.body { Some(body) => { let body_loc = body.loc(); + // Handle case where block / statements starts on disabled line. + if fmt.inline_config.is_disabled(body_loc.with_end(body_loc.start())) { + match body { + Statement::Block { statements, .. } if !statements.is_empty() => { + // TODO: move this logic in `visit_body` fn and reuse it. + // Retain enabled statements. + statements.retain(|stmt| { + !fmt.inline_config.is_disabled( + body_loc.with_end(CodeLocation::loc(stmt).start()), + ) + }); + + // Disabled statement stops where first enabled statement starts or + // where body ends (if no statement enabled). + let disabled_stmts_end = match statements.first() { + Some(stmt) => CodeLocation::loc(stmt).start(), + None => body_loc.end(), + }; + + // Write non formatted statements. This includes the curly bracket + // block start, comments and any other disabled statement. + let disabled_stmts_src = String::from_utf8( + fmt.source.as_bytes() + [body_loc.with_end(disabled_stmts_end).range()] + .to_vec(), + ) + .map_err(FormatterError::custom)?; + fmt.write_whitespace_separator(false)?; + fmt.write_raw(disabled_stmts_src.trim_end())?; + // Remove all comments as they're already included in disabled src. + let _ = fmt.comments.remove_all_comments_before(disabled_stmts_end); + + // Write enabled statements. + fmt.indented(1, |fmt| { + fmt.write_lined_visitable( + body_loc.with_start(disabled_stmts_end), + statements.iter_mut(), + |_, _| false, + )?; + Ok(()) + })?; + + // Write curly bracket block end. + fmt.write_whitespace_separator(true)?; + write_chunk!(fmt, body_loc.end(), "}}")?; + + return Ok(()) + } + _ => {} + } + } + let byte_offset = body_loc.start(); let body = fmt.visit_to_chunk(byte_offset, Some(body_loc.end()), body)?; fmt.write_whitespace_separator( @@ -3045,7 +3101,6 @@ impl<'a, W: Write> Visitor for Formatter<'a, W> { } None => fmt.write_semicolon()?, } - Ok(()) })?; diff --git a/crates/fmt/testdata/InlineDisable/fmt.sol b/crates/fmt/testdata/InlineDisable/fmt.sol index 31667c5e9..9211929e7 100644 --- a/crates/fmt/testdata/InlineDisable/fmt.sol +++ b/crates/fmt/testdata/InlineDisable/fmt.sol @@ -489,3 +489,20 @@ error TopLevelCustomErrorArgWithoutName (string); event Event1(uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a); // forgefmt: disable-stop + +function setNumber(uint256 newNumber /* param1 */, uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf /* param2 */) public view returns (bool,bool) { /* inline*/ number1 = newNumber1; // forgefmt: disable-line + number = newNumber; + return (true, true); +} + +function setNumber1(uint256 newNumber /* param1 */, uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf /* param2 */) public view returns (bool,bool) { /* inline*/ number1 = newNumber1; // forgefmt: disable-line +} + +// forgefmt: disable-next-line +function setNumber1(uint256 newNumber , uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf) public view returns (bool,bool) { number1 = newNumber1; +} + +function setNumber(uint256 newNumber, uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf) public { // forgefmt: disable-line + number = newNumber; + number1 = newNumber1; // forgefmt: disable-line +} diff --git a/crates/fmt/testdata/InlineDisable/original.sol b/crates/fmt/testdata/InlineDisable/original.sol index 83d4ee172..773167894 100644 --- a/crates/fmt/testdata/InlineDisable/original.sol +++ b/crates/fmt/testdata/InlineDisable/original.sol @@ -467,3 +467,20 @@ error TopLevelCustomErrorArgWithoutName (string); event Event1(uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a); // forgefmt: disable-stop + +function setNumber(uint256 newNumber /* param1 */, uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf /* param2 */) public view returns (bool,bool) { /* inline*/ number1 = newNumber1; // forgefmt: disable-line + number = newNumber; + return (true, true); +} + +function setNumber1(uint256 newNumber /* param1 */, uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf /* param2 */) public view returns (bool,bool) { /* inline*/ number1 = newNumber1; // forgefmt: disable-line +} + +// forgefmt: disable-next-line +function setNumber1(uint256 newNumber , uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf) public view returns (bool,bool) { number1 = newNumber1; +} + +function setNumber(uint256 newNumber, uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf) public { // forgefmt: disable-line + number = newNumber; + number1 = newNumber1; // forgefmt: disable-line +} From f348df381762c2379501c9ff42b8a4c86dd9c469 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 2 Aug 2024 19:01:03 +0300 Subject: [PATCH 039/184] fix(fmt): write prefix comments in if block (#8589) --- crates/fmt/src/formatter.rs | 1 + crates/fmt/testdata/Repros/fmt.sol | 21 +++++++++++++++++++++ crates/fmt/testdata/Repros/original.sol | 21 +++++++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/crates/fmt/src/formatter.rs b/crates/fmt/src/formatter.rs index 705e578c9..5a3b7f124 100644 --- a/crates/fmt/src/formatter.rs +++ b/crates/fmt/src/formatter.rs @@ -1630,6 +1630,7 @@ impl<'a, W: Write> Formatter<'a, W> { SurroundingChunk::new("if (", Some(loc.start()), Some(cond.loc().start())), SurroundingChunk::new(")", None, Some(if_branch.loc().start())), |fmt, _| { + fmt.write_prefix_comments_before(cond.loc().end())?; cond.visit(fmt)?; fmt.write_postfix_comments_before(if_branch.loc().start()) }, diff --git a/crates/fmt/testdata/Repros/fmt.sol b/crates/fmt/testdata/Repros/fmt.sol index dc1ac24eb..b2e232c19 100644 --- a/crates/fmt/testdata/Repros/fmt.sol +++ b/crates/fmt/testdata/Repros/fmt.sol @@ -17,3 +17,24 @@ function one() external { ) }); } + +// https://github.com/foundry-rs/foundry/issues/3979 +contract Format { + bool public test; + + function testing(uint256 amount) public payable { + if ( + // This is a comment + msg.value == amount + ) { + test = true; + } else { + test = false; + } + + if ( + // Another one + block.timestamp >= amount + ) {} + } +} diff --git a/crates/fmt/testdata/Repros/original.sol b/crates/fmt/testdata/Repros/original.sol index cee4fc97a..23e96ac63 100644 --- a/crates/fmt/testdata/Repros/original.sol +++ b/crates/fmt/testdata/Repros/original.sol @@ -17,3 +17,24 @@ function one() external { ) }); } + +// https://github.com/foundry-rs/foundry/issues/3979 +contract Format { + bool public test; + + function testing(uint256 amount) public payable { + if ( + // This is a comment + msg.value == amount + ) { + test = true; + } else { + test = false; + } + + if ( + // Another one + block.timestamp >= amount + ) {} + } +} From cce36f85c80946bd4a1868411aa99eda879a0e43 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 2 Aug 2024 22:37:38 +0200 Subject: [PATCH 040/184] feat(cheatcodes): extract crypto cheats into their own category (#8578) --- crates/cheatcodes/assets/cheatcodes.json | 122 +++---- .../cheatcodes/assets/cheatcodes.schema.json | 7 + crates/cheatcodes/spec/src/cheatcode.rs | 8 + crates/cheatcodes/spec/src/vm.rs | 142 ++++---- crates/cheatcodes/src/crypto.rs | 343 ++++++++++++++++++ crates/cheatcodes/src/evm.rs | 64 +--- crates/cheatcodes/src/lib.rs | 2 + crates/cheatcodes/src/script.rs | 2 +- crates/cheatcodes/src/utils.rs | 308 +--------------- testdata/cheats/Vm.sol | 4 +- 10 files changed, 506 insertions(+), 496 deletions(-) create mode 100644 crates/cheatcodes/src/crypto.rs diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index c66f3a5ec..6549b31af 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -3467,7 +3467,7 @@ 210 ] }, - "group": "utilities", + "group": "crypto", "status": "stable", "safety": "safe" }, @@ -3487,7 +3487,7 @@ 182 ] }, - "group": "utilities", + "group": "crypto", "status": "stable", "safety": "safe" }, @@ -3507,7 +3507,7 @@ 98 ] }, - "group": "utilities", + "group": "crypto", "status": "stable", "safety": "safe" }, @@ -3627,7 +3627,7 @@ 139 ] }, - "group": "utilities", + "group": "crypto", "status": "stable", "safety": "safe" }, @@ -3647,7 +3647,7 @@ 27 ] }, - "group": "utilities", + "group": "crypto", "status": "stable", "safety": "safe" }, @@ -3667,7 +3667,7 @@ 109 ] }, - "group": "utilities", + "group": "crypto", "status": "stable", "safety": "safe" }, @@ -3687,7 +3687,7 @@ 31 ] }, - "group": "utilities", + "group": "crypto", "status": "stable", "safety": "safe" }, @@ -5114,7 +5114,7 @@ { "func": { "id": "getNonce_1", - "description": "Get a `Wallet`'s nonce.", + "description": "Get the nonce of a `Wallet`.", "declaration": "function getNonce(Wallet calldata wallet) external returns (uint64 nonce);", "visibility": "external", "mutability": "", @@ -5127,7 +5127,7 @@ 173 ] }, - "group": "utilities", + "group": "evm", "status": "stable", "safety": "safe" }, @@ -6907,7 +6907,7 @@ 100 ] }, - "group": "utilities", + "group": "crypto", "status": "stable", "safety": "safe" }, @@ -7754,6 +7754,26 @@ { "func": { "id": "signCompact_0", + "description": "Signs data with a `Wallet`.\nReturns a compact signature (`r`, `vs`) as per EIP-2098, where `vs` encodes both the\nsignature's `s` value, and the recovery id `v` in a single bytes32.\nThis format reduces the signature size from 65 to 64 bytes.", + "declaration": "function signCompact(Wallet calldata wallet, bytes32 digest) external returns (bytes32 r, bytes32 vs);", + "visibility": "external", + "mutability": "", + "signature": "signCompact((address,uint256,uint256,uint256),bytes32)", + "selector": "0x3d0e292f", + "selectorBytes": [ + 61, + 14, + 41, + 47 + ] + }, + "group": "crypto", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "signCompact_1", "description": "Signs `digest` with `privateKey` using the secp256k1 curve.\nReturns a compact signature (`r`, `vs`) as per EIP-2098, where `vs` encodes both the\nsignature's `s` value, and the recovery id `v` in a single bytes32.\nThis format reduces the signature size from 65 to 64 bytes.", "declaration": "function signCompact(uint256 privateKey, bytes32 digest) external pure returns (bytes32 r, bytes32 vs);", "visibility": "external", @@ -7767,13 +7787,13 @@ 31 ] }, - "group": "evm", + "group": "crypto", "status": "stable", "safety": "safe" }, { "func": { - "id": "signCompact_1", + "id": "signCompact_2", "description": "Signs `digest` with signer provided to script using the secp256k1 curve.\nReturns a compact signature (`r`, `vs`) as per EIP-2098, where `vs` encodes both the\nsignature's `s` value, and the recovery id `v` in a single bytes32.\nThis format reduces the signature size from 65 to 64 bytes.\nIf `--sender` is provided, the signer with provided address is used, otherwise,\nif exactly one signer is provided to the script, that signer is used.\nRaises error if signer passed through `--sender` does not match any unlocked signers or\nif `--sender` is not provided and not exactly one signer is passed to the script.", "declaration": "function signCompact(bytes32 digest) external pure returns (bytes32 r, bytes32 vs);", "visibility": "external", @@ -7787,13 +7807,13 @@ 75 ] }, - "group": "evm", + "group": "crypto", "status": "stable", "safety": "safe" }, { "func": { - "id": "signCompact_2", + "id": "signCompact_3", "description": "Signs `digest` with signer provided to script using the secp256k1 curve.\nReturns a compact signature (`r`, `vs`) as per EIP-2098, where `vs` encodes both the\nsignature's `s` value, and the recovery id `v` in a single bytes32.\nThis format reduces the signature size from 65 to 64 bytes.\nRaises error if none of the signers passed into the script have provided address.", "declaration": "function signCompact(address signer, bytes32 digest) external pure returns (bytes32 r, bytes32 vs);", "visibility": "external", @@ -7807,27 +7827,7 @@ 191 ] }, - "group": "evm", - "status": "stable", - "safety": "safe" - }, - { - "func": { - "id": "signCompact_3", - "description": "Signs data with a `Wallet`.\nReturns a compact signature (`r`, `vs`) as per EIP-2098, where `vs` encodes both the\nsignature's `s` value, and the recovery id `v` in a single bytes32.\nThis format reduces the signature size from 65 to 64 bytes.", - "declaration": "function signCompact(Wallet calldata wallet, bytes32 digest) external returns (bytes32 r, bytes32 vs);", - "visibility": "external", - "mutability": "", - "signature": "signCompact((address,uint256,uint256,uint256),bytes32)", - "selector": "0x3d0e292f", - "selectorBytes": [ - 61, - 14, - 41, - 47 - ] - }, - "group": "utilities", + "group": "crypto", "status": "stable", "safety": "safe" }, @@ -7847,13 +7847,33 @@ 64 ] }, - "group": "evm", + "group": "crypto", "status": "stable", "safety": "safe" }, { "func": { "id": "sign_0", + "description": "Signs data with a `Wallet`.", + "declaration": "function sign(Wallet calldata wallet, bytes32 digest) external returns (uint8 v, bytes32 r, bytes32 s);", + "visibility": "external", + "mutability": "", + "signature": "sign((address,uint256,uint256,uint256),bytes32)", + "selector": "0xb25c5a25", + "selectorBytes": [ + 178, + 92, + 90, + 37 + ] + }, + "group": "crypto", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "sign_1", "description": "Signs `digest` with `privateKey` using the secp256k1 curve.", "declaration": "function sign(uint256 privateKey, bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s);", "visibility": "external", @@ -7867,13 +7887,13 @@ 164 ] }, - "group": "evm", + "group": "crypto", "status": "stable", "safety": "safe" }, { "func": { - "id": "sign_1", + "id": "sign_2", "description": "Signs `digest` with signer provided to script using the secp256k1 curve.\nIf `--sender` is provided, the signer with provided address is used, otherwise,\nif exactly one signer is provided to the script, that signer is used.\nRaises error if signer passed through `--sender` does not match any unlocked signers or\nif `--sender` is not provided and not exactly one signer is passed to the script.", "declaration": "function sign(bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s);", "visibility": "external", @@ -7887,13 +7907,13 @@ 51 ] }, - "group": "evm", + "group": "crypto", "status": "stable", "safety": "safe" }, { "func": { - "id": "sign_2", + "id": "sign_3", "description": "Signs `digest` with signer provided to script using the secp256k1 curve.\nRaises error if none of the signers passed into the script have provided address.", "declaration": "function sign(address signer, bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s);", "visibility": "external", @@ -7907,27 +7927,7 @@ 5 ] }, - "group": "evm", - "status": "stable", - "safety": "safe" - }, - { - "func": { - "id": "sign_3", - "description": "Signs data with a `Wallet`.", - "declaration": "function sign(Wallet calldata wallet, bytes32 digest) external returns (uint8 v, bytes32 r, bytes32 s);", - "visibility": "external", - "mutability": "", - "signature": "sign((address,uint256,uint256,uint256),bytes32)", - "selector": "0xb25c5a25", - "selectorBytes": [ - 178, - 92, - 90, - 37 - ] - }, - "group": "utilities", + "group": "crypto", "status": "stable", "safety": "safe" }, diff --git a/crates/cheatcodes/assets/cheatcodes.schema.json b/crates/cheatcodes/assets/cheatcodes.schema.json index cd66ecdc4..9301196d9 100644 --- a/crates/cheatcodes/assets/cheatcodes.schema.json +++ b/crates/cheatcodes/assets/cheatcodes.schema.json @@ -305,6 +305,13 @@ "toml" ] }, + { + "description": "Cryptography-related cheatcodes.\n\nExamples: `sign*`.\n\nSafety: safe.", + "type": "string", + "enum": [ + "crypto" + ] + }, { "description": "Generic, uncategorized utilities.\n\nExamples: `toString`, `parse*`, `serialize*`.\n\nSafety: safe.", "type": "string", diff --git a/crates/cheatcodes/spec/src/cheatcode.rs b/crates/cheatcodes/spec/src/cheatcode.rs index 91573d89e..207cac159 100644 --- a/crates/cheatcodes/spec/src/cheatcode.rs +++ b/crates/cheatcodes/spec/src/cheatcode.rs @@ -114,6 +114,12 @@ pub enum Group { /// /// Safety: safe. Toml, + /// Cryptography-related cheatcodes. + /// + /// Examples: `sign*`. + /// + /// Safety: safe. + Crypto, /// Generic, uncategorized utilities. /// /// Examples: `toString`, `parse*`, `serialize*`. @@ -137,6 +143,7 @@ impl Group { Self::String | Self::Json | Self::Toml | + Self::Crypto | Self::Utilities => Some(Safety::Safe), } } @@ -153,6 +160,7 @@ impl Group { Self::String => "string", Self::Json => "json", Self::Toml => "toml", + Self::Crypto => "crypto", Self::Utilities => "utilities", } } diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 685a6f751..a4dc63690 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -274,6 +274,10 @@ interface Vm { #[cheatcode(group = Evm, safety = Safe)] function getNonce(address account) external view returns (uint64 nonce); + /// Get the nonce of a `Wallet`. + #[cheatcode(group = Evm, safety = Safe)] + function getNonce(Wallet calldata wallet) external returns (uint64 nonce); + /// Loads a storage slot from an address. #[cheatcode(group = Evm, safety = Safe)] function load(address target, bytes32 slot) external view returns (bytes32 data); @@ -282,62 +286,6 @@ interface Vm { #[cheatcode(group = Evm, safety = Unsafe)] function loadAllocs(string calldata pathToAllocsJson) external; - /// Signs `digest` with `privateKey` using the secp256k1 curve. - #[cheatcode(group = Evm, safety = Safe)] - function sign(uint256 privateKey, bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); - - /// Signs `digest` with `privateKey` using the secp256k1 curve. - /// - /// Returns a compact signature (`r`, `vs`) as per EIP-2098, where `vs` encodes both the - /// signature's `s` value, and the recovery id `v` in a single bytes32. - /// This format reduces the signature size from 65 to 64 bytes. - #[cheatcode(group = Evm, safety = Safe)] - function signCompact(uint256 privateKey, bytes32 digest) external pure returns (bytes32 r, bytes32 vs); - - /// Signs `digest` with signer provided to script using the secp256k1 curve. - /// - /// If `--sender` is provided, the signer with provided address is used, otherwise, - /// if exactly one signer is provided to the script, that signer is used. - /// - /// Raises error if signer passed through `--sender` does not match any unlocked signers or - /// if `--sender` is not provided and not exactly one signer is passed to the script. - #[cheatcode(group = Evm, safety = Safe)] - function sign(bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); - - /// Signs `digest` with signer provided to script using the secp256k1 curve. - /// - /// Returns a compact signature (`r`, `vs`) as per EIP-2098, where `vs` encodes both the - /// signature's `s` value, and the recovery id `v` in a single bytes32. - /// This format reduces the signature size from 65 to 64 bytes. - /// - /// If `--sender` is provided, the signer with provided address is used, otherwise, - /// if exactly one signer is provided to the script, that signer is used. - /// - /// Raises error if signer passed through `--sender` does not match any unlocked signers or - /// if `--sender` is not provided and not exactly one signer is passed to the script. - #[cheatcode(group = Evm, safety = Safe)] - function signCompact(bytes32 digest) external pure returns (bytes32 r, bytes32 vs); - - /// Signs `digest` with signer provided to script using the secp256k1 curve. - /// - /// Raises error if none of the signers passed into the script have provided address. - #[cheatcode(group = Evm, safety = Safe)] - function sign(address signer, bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); - - /// Signs `digest` with signer provided to script using the secp256k1 curve. - /// - /// Returns a compact signature (`r`, `vs`) as per EIP-2098, where `vs` encodes both the - /// signature's `s` value, and the recovery id `v` in a single bytes32. - /// This format reduces the signature size from 65 to 64 bytes. - /// - /// Raises error if none of the signers passed into the script have provided address. - #[cheatcode(group = Evm, safety = Safe)] - function signCompact(address signer, bytes32 digest) external pure returns (bytes32 r, bytes32 vs); - - /// Signs `digest` with `privateKey` using the secp256r1 curve. - #[cheatcode(group = Evm, safety = Safe)] - function signP256(uint256 privateKey, bytes32 digest) external pure returns (bytes32 r, bytes32 s); - // -------- Record Storage -------- /// Records all storage reads and writes. @@ -2163,26 +2111,24 @@ interface Vm { #[cheatcode(group = Toml)] function writeToml(string calldata json, string calldata path, string calldata valueKey) external; + // ======== Cryptography ======== + // -------- Key Management -------- /// Derives a private key from the name, labels the account with that name, and returns the wallet. - #[cheatcode(group = Utilities)] + #[cheatcode(group = Crypto)] function createWallet(string calldata walletLabel) external returns (Wallet memory wallet); /// Generates a wallet from the private key and returns the wallet. - #[cheatcode(group = Utilities)] + #[cheatcode(group = Crypto)] function createWallet(uint256 privateKey) external returns (Wallet memory wallet); /// Generates a wallet from the private key, labels the account with that name, and returns the wallet. - #[cheatcode(group = Utilities)] + #[cheatcode(group = Crypto)] function createWallet(uint256 privateKey, string calldata walletLabel) external returns (Wallet memory wallet); - /// Get a `Wallet`'s nonce. - #[cheatcode(group = Utilities)] - function getNonce(Wallet calldata wallet) external returns (uint64 nonce); - /// Signs data with a `Wallet`. - #[cheatcode(group = Utilities)] + #[cheatcode(group = Crypto)] function sign(Wallet calldata wallet, bytes32 digest) external returns (uint8 v, bytes32 r, bytes32 s); /// Signs data with a `Wallet`. @@ -2190,37 +2136,93 @@ interface Vm { /// Returns a compact signature (`r`, `vs`) as per EIP-2098, where `vs` encodes both the /// signature's `s` value, and the recovery id `v` in a single bytes32. /// This format reduces the signature size from 65 to 64 bytes. - #[cheatcode(group = Utilities)] + #[cheatcode(group = Crypto)] function signCompact(Wallet calldata wallet, bytes32 digest) external returns (bytes32 r, bytes32 vs); + /// Signs `digest` with `privateKey` using the secp256k1 curve. + #[cheatcode(group = Crypto)] + function sign(uint256 privateKey, bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); + + /// Signs `digest` with `privateKey` using the secp256k1 curve. + /// + /// Returns a compact signature (`r`, `vs`) as per EIP-2098, where `vs` encodes both the + /// signature's `s` value, and the recovery id `v` in a single bytes32. + /// This format reduces the signature size from 65 to 64 bytes. + #[cheatcode(group = Crypto)] + function signCompact(uint256 privateKey, bytes32 digest) external pure returns (bytes32 r, bytes32 vs); + + /// Signs `digest` with signer provided to script using the secp256k1 curve. + /// + /// If `--sender` is provided, the signer with provided address is used, otherwise, + /// if exactly one signer is provided to the script, that signer is used. + /// + /// Raises error if signer passed through `--sender` does not match any unlocked signers or + /// if `--sender` is not provided and not exactly one signer is passed to the script. + #[cheatcode(group = Crypto)] + function sign(bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); + + /// Signs `digest` with signer provided to script using the secp256k1 curve. + /// + /// Returns a compact signature (`r`, `vs`) as per EIP-2098, where `vs` encodes both the + /// signature's `s` value, and the recovery id `v` in a single bytes32. + /// This format reduces the signature size from 65 to 64 bytes. + /// + /// If `--sender` is provided, the signer with provided address is used, otherwise, + /// if exactly one signer is provided to the script, that signer is used. + /// + /// Raises error if signer passed through `--sender` does not match any unlocked signers or + /// if `--sender` is not provided and not exactly one signer is passed to the script. + #[cheatcode(group = Crypto)] + function signCompact(bytes32 digest) external pure returns (bytes32 r, bytes32 vs); + + /// Signs `digest` with signer provided to script using the secp256k1 curve. + /// + /// Raises error if none of the signers passed into the script have provided address. + #[cheatcode(group = Crypto)] + function sign(address signer, bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); + + /// Signs `digest` with signer provided to script using the secp256k1 curve. + /// + /// Returns a compact signature (`r`, `vs`) as per EIP-2098, where `vs` encodes both the + /// signature's `s` value, and the recovery id `v` in a single bytes32. + /// This format reduces the signature size from 65 to 64 bytes. + /// + /// Raises error if none of the signers passed into the script have provided address. + #[cheatcode(group = Crypto)] + function signCompact(address signer, bytes32 digest) external pure returns (bytes32 r, bytes32 vs); + + /// Signs `digest` with `privateKey` using the secp256r1 curve. + #[cheatcode(group = Crypto)] + function signP256(uint256 privateKey, bytes32 digest) external pure returns (bytes32 r, bytes32 s); + /// Derive a private key from a provided mnenomic string (or mnenomic file path) /// at the derivation path `m/44'/60'/0'/0/{index}`. - #[cheatcode(group = Utilities)] + #[cheatcode(group = Crypto)] function deriveKey(string calldata mnemonic, uint32 index) external pure returns (uint256 privateKey); /// Derive a private key from a provided mnenomic string (or mnenomic file path) /// at `{derivationPath}{index}`. - #[cheatcode(group = Utilities)] + #[cheatcode(group = Crypto)] function deriveKey(string calldata mnemonic, string calldata derivationPath, uint32 index) external pure returns (uint256 privateKey); /// Derive a private key from a provided mnenomic string (or mnenomic file path) in the specified language /// at the derivation path `m/44'/60'/0'/0/{index}`. - #[cheatcode(group = Utilities)] + #[cheatcode(group = Crypto)] function deriveKey(string calldata mnemonic, uint32 index, string calldata language) external pure returns (uint256 privateKey); /// Derive a private key from a provided mnenomic string (or mnenomic file path) in the specified language /// at `{derivationPath}{index}`. - #[cheatcode(group = Utilities)] + #[cheatcode(group = Crypto)] function deriveKey(string calldata mnemonic, string calldata derivationPath, uint32 index, string calldata language) external pure returns (uint256 privateKey); /// Adds a private key to the local forge wallet and returns the address. - #[cheatcode(group = Utilities)] + #[cheatcode(group = Crypto)] function rememberKey(uint256 privateKey) external returns (address keyAddr); // -------- Uncategorized Utilities -------- diff --git a/crates/cheatcodes/src/crypto.rs b/crates/cheatcodes/src/crypto.rs new file mode 100644 index 000000000..aea346279 --- /dev/null +++ b/crates/cheatcodes/src/crypto.rs @@ -0,0 +1,343 @@ +//! Implementations of [`Crypto`](spec::Group::Crypto) Cheatcodes. + +use crate::{Cheatcode, Cheatcodes, Result, Vm::*}; +use alloy_primitives::{keccak256, Address, B256, U256}; +use alloy_signer::{Signer, SignerSync}; +use alloy_signer_local::{ + coins_bip39::{ + ChineseSimplified, ChineseTraditional, Czech, English, French, Italian, Japanese, Korean, + Portuguese, Spanish, Wordlist, + }, + MnemonicBuilder, PrivateKeySigner, +}; +use alloy_sol_types::SolValue; +use k256::{ + ecdsa::SigningKey, + elliptic_curve::{sec1::ToEncodedPoint, Curve}, + Secp256k1, +}; +use p256::ecdsa::{signature::hazmat::PrehashSigner, Signature, SigningKey as P256SigningKey}; + +/// The BIP32 default derivation path prefix. +const DEFAULT_DERIVATION_PATH_PREFIX: &str = "m/44'/60'/0'/0/"; + +impl Cheatcode for createWallet_0Call { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { walletLabel } = self; + create_wallet(&U256::from_be_bytes(keccak256(walletLabel).0), Some(walletLabel), state) + } +} + +impl Cheatcode for createWallet_1Call { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { privateKey } = self; + create_wallet(privateKey, None, state) + } +} + +impl Cheatcode for createWallet_2Call { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { privateKey, walletLabel } = self; + create_wallet(privateKey, Some(walletLabel), state) + } +} + +impl Cheatcode for sign_0Call { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { wallet, digest } = self; + let sig = sign(&wallet.privateKey, digest)?; + Ok(encode_full_sig(sig)) + } +} + +impl Cheatcode for signCompact_0Call { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { wallet, digest } = self; + let sig = sign(&wallet.privateKey, digest)?; + Ok(encode_compact_sig(sig)) + } +} + +impl Cheatcode for deriveKey_0Call { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { mnemonic, index } = self; + derive_key::(mnemonic, DEFAULT_DERIVATION_PATH_PREFIX, *index) + } +} + +impl Cheatcode for deriveKey_1Call { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { mnemonic, derivationPath, index } = self; + derive_key::(mnemonic, derivationPath, *index) + } +} + +impl Cheatcode for deriveKey_2Call { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { mnemonic, index, language } = self; + derive_key_str(mnemonic, DEFAULT_DERIVATION_PATH_PREFIX, *index, language) + } +} + +impl Cheatcode for deriveKey_3Call { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { mnemonic, derivationPath, index, language } = self; + derive_key_str(mnemonic, derivationPath, *index, language) + } +} + +impl Cheatcode for rememberKeyCall { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { privateKey } = self; + let wallet = parse_wallet(privateKey)?; + let address = wallet.address(); + if let Some(script_wallets) = state.script_wallets() { + script_wallets.add_local_signer(wallet); + } + Ok(address.abi_encode()) + } +} + +impl Cheatcode for sign_1Call { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { privateKey, digest } = self; + let sig = sign(privateKey, digest)?; + Ok(encode_full_sig(sig)) + } +} + +impl Cheatcode for signCompact_1Call { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { privateKey, digest } = self; + let sig = sign(privateKey, digest)?; + Ok(encode_compact_sig(sig)) + } +} + +impl Cheatcode for sign_2Call { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { digest } = self; + let sig = sign_with_wallet(state, None, digest)?; + Ok(encode_full_sig(sig)) + } +} + +impl Cheatcode for signCompact_2Call { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { digest } = self; + let sig = sign_with_wallet(state, None, digest)?; + Ok(encode_compact_sig(sig)) + } +} + +impl Cheatcode for sign_3Call { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { signer, digest } = self; + let sig = sign_with_wallet(state, Some(*signer), digest)?; + Ok(encode_full_sig(sig)) + } +} + +impl Cheatcode for signCompact_3Call { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { signer, digest } = self; + let sig = sign_with_wallet(state, Some(*signer), digest)?; + Ok(encode_compact_sig(sig)) + } +} + +impl Cheatcode for signP256Call { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { privateKey, digest } = self; + sign_p256(privateKey, digest) + } +} + +/// Using a given private key, return its public ETH address, its public key affine x and y +/// coordinates, and its private key (see the 'Wallet' struct) +/// +/// If 'label' is set to 'Some()', assign that label to the associated ETH address in state +fn create_wallet(private_key: &U256, label: Option<&str>, state: &mut Cheatcodes) -> Result { + let key = parse_private_key(private_key)?; + let addr = alloy_signer::utils::secret_key_to_address(&key); + + let pub_key = key.verifying_key().as_affine().to_encoded_point(false); + let pub_key_x = U256::from_be_bytes((*pub_key.x().unwrap()).into()); + let pub_key_y = U256::from_be_bytes((*pub_key.y().unwrap()).into()); + + if let Some(label) = label { + state.labels.insert(addr, label.into()); + } + + Ok(Wallet { addr, publicKeyX: pub_key_x, publicKeyY: pub_key_y, privateKey: *private_key } + .abi_encode()) +} + +fn encode_full_sig(sig: alloy_primitives::Signature) -> Vec { + // Retrieve v, r and s from signature. + let v = U256::from(sig.v().y_parity_byte_non_eip155().unwrap_or(sig.v().y_parity_byte())); + let r = B256::from(sig.r()); + let s = B256::from(sig.s()); + (v, r, s).abi_encode() +} + +fn encode_compact_sig(sig: alloy_primitives::Signature) -> Vec { + // Implement EIP-2098 compact signature. + let r = B256::from(sig.r()); + let mut vs = sig.s(); + vs.set_bit(255, sig.v().y_parity()); + (r, vs).abi_encode() +} + +fn sign(private_key: &U256, digest: &B256) -> Result { + // The `ecrecover` precompile does not use EIP-155. No chain ID is needed. + let wallet = parse_wallet(private_key)?; + let sig = wallet.sign_hash_sync(digest)?; + debug_assert_eq!(sig.recover_address_from_prehash(digest)?, wallet.address()); + Ok(sig) +} + +fn sign_with_wallet( + state: &mut Cheatcodes, + signer: Option
, + digest: &B256, +) -> Result { + let Some(script_wallets) = state.script_wallets() else { + bail!("no wallets are available"); + }; + + let mut script_wallets = script_wallets.inner.lock(); + let maybe_provided_sender = script_wallets.provided_sender; + let signers = script_wallets.multi_wallet.signers()?; + + let signer = if let Some(signer) = signer { + signer + } else if let Some(provided_sender) = maybe_provided_sender { + provided_sender + } else if signers.len() == 1 { + *signers.keys().next().unwrap() + } else { + bail!("could not determine signer"); + }; + + let wallet = signers + .get(&signer) + .ok_or_else(|| fmt_err!("signer with address {signer} is not available"))?; + + let sig = foundry_common::block_on(wallet.sign_hash(digest))?; + debug_assert_eq!(sig.recover_address_from_prehash(digest)?, signer); + Ok(sig) +} + +fn sign_p256(private_key: &U256, digest: &B256) -> Result { + ensure!(*private_key != U256::ZERO, "private key cannot be 0"); + let n = U256::from_limbs(*p256::NistP256::ORDER.as_words()); + ensure!( + *private_key < n, + format!("private key must be less than the secp256r1 curve order ({})", n), + ); + let bytes = private_key.to_be_bytes(); + let signing_key = P256SigningKey::from_bytes((&bytes).into())?; + let signature: Signature = signing_key.sign_prehash(digest.as_slice())?; + let r_bytes: [u8; 32] = signature.r().to_bytes().into(); + let s_bytes: [u8; 32] = signature.s().to_bytes().into(); + + Ok((r_bytes, s_bytes).abi_encode()) +} + +fn parse_private_key(private_key: &U256) -> Result { + ensure!(*private_key != U256::ZERO, "private key cannot be 0"); + ensure!( + *private_key < U256::from_limbs(*Secp256k1::ORDER.as_words()), + "private key must be less than the secp256k1 curve order \ + (115792089237316195423570985008687907852837564279074904382605163141518161494337)", + ); + let bytes = private_key.to_be_bytes(); + SigningKey::from_bytes((&bytes).into()).map_err(Into::into) +} + +pub(super) fn parse_wallet(private_key: &U256) -> Result { + parse_private_key(private_key).map(PrivateKeySigner::from) +} + +fn derive_key_str(mnemonic: &str, path: &str, index: u32, language: &str) -> Result { + match language { + "chinese_simplified" => derive_key::(mnemonic, path, index), + "chinese_traditional" => derive_key::(mnemonic, path, index), + "czech" => derive_key::(mnemonic, path, index), + "english" => derive_key::(mnemonic, path, index), + "french" => derive_key::(mnemonic, path, index), + "italian" => derive_key::(mnemonic, path, index), + "japanese" => derive_key::(mnemonic, path, index), + "korean" => derive_key::(mnemonic, path, index), + "portuguese" => derive_key::(mnemonic, path, index), + "spanish" => derive_key::(mnemonic, path, index), + _ => Err(fmt_err!("unsupported mnemonic language: {language:?}")), + } +} + +fn derive_key(mnemonic: &str, path: &str, index: u32) -> Result { + fn derive_key_path(path: &str, index: u32) -> String { + let mut out = path.to_string(); + if !out.ends_with('/') { + out.push('/'); + } + out.push_str(&index.to_string()); + out + } + + let wallet = MnemonicBuilder::::default() + .phrase(mnemonic) + .derivation_path(derive_key_path(path, index))? + .build()?; + let private_key = U256::from_be_bytes(wallet.credential().to_bytes().into()); + Ok(private_key.abi_encode()) +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_primitives::{hex::FromHex, FixedBytes}; + use p256::ecdsa::signature::hazmat::PrehashVerifier; + + #[test] + fn test_sign_p256() { + use p256::ecdsa::VerifyingKey; + + let pk_u256: U256 = "1".parse().unwrap(); + let signing_key = P256SigningKey::from_bytes(&pk_u256.to_be_bytes().into()).unwrap(); + let digest = FixedBytes::from_hex( + "0x44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56", + ) + .unwrap(); + + let result = sign_p256(&pk_u256, &digest).unwrap(); + let result_bytes: [u8; 64] = result.try_into().unwrap(); + let signature = Signature::from_bytes(&result_bytes.into()).unwrap(); + let verifying_key = VerifyingKey::from(&signing_key); + assert!(verifying_key.verify_prehash(digest.as_slice(), &signature).is_ok()); + } + + #[test] + fn test_sign_p256_pk_too_large() { + // max n from https://neuromancer.sk/std/secg/secp256r1 + let pk = + "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551".parse().unwrap(); + let digest = FixedBytes::from_hex( + "0x54705ba3baafdbdfba8c5f9a70f7a89bee98d906b53e31074da7baecdc0da9ad", + ) + .unwrap(); + let result = sign_p256(&pk, &digest); + assert_eq!(result.err().unwrap().to_string(), "private key must be less than the secp256r1 curve order (115792089210356248762697446949407573529996955224135760342422259061068512044369)"); + } + + #[test] + fn test_sign_p256_pk_0() { + let digest = FixedBytes::from_hex( + "0x54705ba3baafdbdfba8c5f9a70f7a89bee98d906b53e31074da7baecdc0da9ad", + ) + .unwrap(); + let result = sign_p256(&U256::ZERO, &digest); + assert_eq!(result.err().unwrap().to_string(), "private key cannot be 0"); + } +} diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 2d4031920..1c93afd0f 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -65,7 +65,7 @@ pub struct DealRecord { impl Cheatcode for addrCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { privateKey } = self; - let wallet = super::utils::parse_wallet(privateKey)?; + let wallet = super::crypto::parse_wallet(privateKey)?; Ok(wallet.address().abi_encode()) } } @@ -77,6 +77,13 @@ impl Cheatcode for getNonce_0Call { } } +impl Cheatcode for getNonce_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { wallet } = self; + get_nonce(ccx, &wallet.addr) + } +} + impl Cheatcode for loadCall { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { target, slot } = *self; @@ -159,61 +166,6 @@ impl Cheatcode for dumpStateCall { } } -impl Cheatcode for sign_0Call { - fn apply_stateful(&self, _: &mut CheatsCtxt) -> Result { - let Self { privateKey, digest } = self; - let sig = super::utils::sign(privateKey, digest)?; - Ok(super::utils::encode_full_sig(sig)) - } -} - -impl Cheatcode for signCompact_0Call { - fn apply_stateful(&self, _: &mut CheatsCtxt) -> Result { - let Self { privateKey, digest } = self; - let sig = super::utils::sign(privateKey, digest)?; - Ok(super::utils::encode_compact_sig(sig)) - } -} - -impl Cheatcode for sign_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { - let Self { digest } = self; - let sig = super::utils::sign_with_wallet(ccx, None, digest)?; - Ok(super::utils::encode_full_sig(sig)) - } -} - -impl Cheatcode for signCompact_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { - let Self { digest } = self; - let sig = super::utils::sign_with_wallet(ccx, None, digest)?; - Ok(super::utils::encode_compact_sig(sig)) - } -} - -impl Cheatcode for sign_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { - let Self { signer, digest } = self; - let sig = super::utils::sign_with_wallet(ccx, Some(*signer), digest)?; - Ok(super::utils::encode_full_sig(sig)) - } -} - -impl Cheatcode for signCompact_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { - let Self { signer, digest } = self; - let sig = super::utils::sign_with_wallet(ccx, Some(*signer), digest)?; - Ok(super::utils::encode_compact_sig(sig)) - } -} - -impl Cheatcode for signP256Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { - let Self { privateKey, digest } = self; - super::utils::sign_p256(privateKey, digest, ccx.state) - } -} - impl Cheatcode for recordCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index b03dbc2d7..4bd0b22a3 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -30,6 +30,8 @@ mod base64; mod config; +mod crypto; + mod env; pub use env::set_execution_context; diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index af4457f8e..82eef2354 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -171,7 +171,7 @@ fn broadcast_key( private_key: &U256, single_call: bool, ) -> Result { - let wallet = super::utils::parse_wallet(private_key)?; + let wallet = super::crypto::parse_wallet(private_key)?; let new_origin = wallet.address(); let result = broadcast(ccx, Some(&new_origin), single_call); diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index 67b154ff2..6edd700ec 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -1,113 +1,12 @@ //! Implementations of [`Utilities`](spec::Group::Utilities) cheatcodes. -use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*}; -use alloy_primitives::{keccak256, Address, B256, U256}; -use alloy_signer::{Signer, SignerSync}; -use alloy_signer_local::{ - coins_bip39::{ - ChineseSimplified, ChineseTraditional, Czech, English, French, Italian, Japanese, Korean, - Portuguese, Spanish, Wordlist, - }, - MnemonicBuilder, PrivateKeySigner, -}; +use crate::{Cheatcode, Cheatcodes, Result, Vm::*}; +use alloy_primitives::{Address, U256}; use alloy_sol_types::SolValue; use foundry_common::ens::namehash; use foundry_evm_core::constants::DEFAULT_CREATE2_DEPLOYER; -use k256::{ - ecdsa::SigningKey, - elliptic_curve::{sec1::ToEncodedPoint, Curve}, - Secp256k1, -}; -use p256::ecdsa::{signature::hazmat::PrehashSigner, Signature, SigningKey as P256SigningKey}; use rand::Rng; -/// The BIP32 default derivation path prefix. -const DEFAULT_DERIVATION_PATH_PREFIX: &str = "m/44'/60'/0'/0/"; - -impl Cheatcode for createWallet_0Call { - fn apply(&self, state: &mut Cheatcodes) -> Result { - let Self { walletLabel } = self; - create_wallet(&U256::from_be_bytes(keccak256(walletLabel).0), Some(walletLabel), state) - } -} - -impl Cheatcode for createWallet_1Call { - fn apply(&self, state: &mut Cheatcodes) -> Result { - let Self { privateKey } = self; - create_wallet(privateKey, None, state) - } -} - -impl Cheatcode for createWallet_2Call { - fn apply(&self, state: &mut Cheatcodes) -> Result { - let Self { privateKey, walletLabel } = self; - create_wallet(privateKey, Some(walletLabel), state) - } -} - -impl Cheatcode for getNonce_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { - let Self { wallet } = self; - super::evm::get_nonce(ccx, &wallet.addr) - } -} - -impl Cheatcode for sign_3Call { - fn apply_stateful(&self, _: &mut CheatsCtxt) -> Result { - let Self { wallet, digest } = self; - let sig = sign(&wallet.privateKey, digest)?; - Ok(encode_full_sig(sig)) - } -} - -impl Cheatcode for signCompact_3Call { - fn apply_stateful(&self, _: &mut CheatsCtxt) -> Result { - let Self { wallet, digest } = self; - let sig = sign(&wallet.privateKey, digest)?; - Ok(encode_compact_sig(sig)) - } -} - -impl Cheatcode for deriveKey_0Call { - fn apply(&self, _state: &mut Cheatcodes) -> Result { - let Self { mnemonic, index } = self; - derive_key::(mnemonic, DEFAULT_DERIVATION_PATH_PREFIX, *index) - } -} - -impl Cheatcode for deriveKey_1Call { - fn apply(&self, _state: &mut Cheatcodes) -> Result { - let Self { mnemonic, derivationPath, index } = self; - derive_key::(mnemonic, derivationPath, *index) - } -} - -impl Cheatcode for deriveKey_2Call { - fn apply(&self, _state: &mut Cheatcodes) -> Result { - let Self { mnemonic, index, language } = self; - derive_key_str(mnemonic, DEFAULT_DERIVATION_PATH_PREFIX, *index, language) - } -} - -impl Cheatcode for deriveKey_3Call { - fn apply(&self, _state: &mut Cheatcodes) -> Result { - let Self { mnemonic, derivationPath, index, language } = self; - derive_key_str(mnemonic, derivationPath, *index, language) - } -} - -impl Cheatcode for rememberKeyCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { - let Self { privateKey } = self; - let wallet = parse_wallet(privateKey)?; - let address = wallet.address(); - if let Some(script_wallets) = ccx.state.script_wallets() { - script_wallets.add_local_signer(wallet); - } - Ok(address.abi_encode()) - } -} - impl Cheatcode for labelCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { account, newLabel } = self; @@ -189,206 +88,3 @@ impl Cheatcode for randomAddressCall { Ok(addr.abi_encode()) } } - -/// Using a given private key, return its public ETH address, its public key affine x and y -/// coordinates, and its private key (see the 'Wallet' struct) -/// -/// If 'label' is set to 'Some()', assign that label to the associated ETH address in state -fn create_wallet(private_key: &U256, label: Option<&str>, state: &mut Cheatcodes) -> Result { - let key = parse_private_key(private_key)?; - let addr = alloy_signer::utils::secret_key_to_address(&key); - - let pub_key = key.verifying_key().as_affine().to_encoded_point(false); - let pub_key_x = U256::from_be_bytes((*pub_key.x().unwrap()).into()); - let pub_key_y = U256::from_be_bytes((*pub_key.y().unwrap()).into()); - - if let Some(label) = label { - state.labels.insert(addr, label.into()); - } - - Ok(Wallet { addr, publicKeyX: pub_key_x, publicKeyY: pub_key_y, privateKey: *private_key } - .abi_encode()) -} - -pub(super) fn encode_full_sig(sig: alloy_primitives::Signature) -> Vec { - // Retrieve v, r and s from signature. - let v = U256::from(sig.v().y_parity_byte_non_eip155().unwrap_or(sig.v().y_parity_byte())); - let r = B256::from(sig.r()); - let s = B256::from(sig.s()); - (v, r, s).abi_encode() -} - -pub(super) fn encode_compact_sig(sig: alloy_primitives::Signature) -> Vec { - // Implement EIP-2098 compact signature. - let r = B256::from(sig.r()); - let mut vs = sig.s(); - vs.set_bit(255, sig.v().y_parity()); - (r, vs).abi_encode() -} - -pub(super) fn sign(private_key: &U256, digest: &B256) -> Result { - // The `ecrecover` precompile does not use EIP-155. No chain ID is needed. - let wallet = parse_wallet(private_key)?; - let sig = wallet.sign_hash_sync(digest)?; - debug_assert_eq!(sig.recover_address_from_prehash(digest)?, wallet.address()); - Ok(sig) -} - -pub(super) fn sign_with_wallet( - ccx: &mut CheatsCtxt, - signer: Option
, - digest: &B256, -) -> Result { - let Some(script_wallets) = ccx.state.script_wallets() else { - bail!("no wallets are available"); - }; - - let mut script_wallets = script_wallets.inner.lock(); - let maybe_provided_sender = script_wallets.provided_sender; - let signers = script_wallets.multi_wallet.signers()?; - - let signer = if let Some(signer) = signer { - signer - } else if let Some(provided_sender) = maybe_provided_sender { - provided_sender - } else if signers.len() == 1 { - *signers.keys().next().unwrap() - } else { - bail!("could not determine signer"); - }; - - let wallet = signers - .get(&signer) - .ok_or_else(|| fmt_err!("signer with address {signer} is not available"))?; - - let sig = foundry_common::block_on(wallet.sign_hash(digest))?; - debug_assert_eq!(sig.recover_address_from_prehash(digest)?, signer); - Ok(sig) -} - -pub(super) fn sign_p256(private_key: &U256, digest: &B256, _state: &mut Cheatcodes) -> Result { - ensure!(*private_key != U256::ZERO, "private key cannot be 0"); - let n = U256::from_limbs(*p256::NistP256::ORDER.as_words()); - ensure!( - *private_key < n, - format!("private key must be less than the secp256r1 curve order ({})", n), - ); - let bytes = private_key.to_be_bytes(); - let signing_key = P256SigningKey::from_bytes((&bytes).into())?; - let signature: Signature = signing_key.sign_prehash(digest.as_slice())?; - let r_bytes: [u8; 32] = signature.r().to_bytes().into(); - let s_bytes: [u8; 32] = signature.s().to_bytes().into(); - - Ok((r_bytes, s_bytes).abi_encode()) -} - -pub(super) fn parse_private_key(private_key: &U256) -> Result { - ensure!(*private_key != U256::ZERO, "private key cannot be 0"); - ensure!( - *private_key < U256::from_limbs(*Secp256k1::ORDER.as_words()), - "private key must be less than the secp256k1 curve order \ - (115792089237316195423570985008687907852837564279074904382605163141518161494337)", - ); - let bytes = private_key.to_be_bytes(); - SigningKey::from_bytes((&bytes).into()).map_err(Into::into) -} - -pub(super) fn parse_wallet(private_key: &U256) -> Result { - parse_private_key(private_key).map(PrivateKeySigner::from) -} - -fn derive_key_str(mnemonic: &str, path: &str, index: u32, language: &str) -> Result { - match language { - "chinese_simplified" => derive_key::(mnemonic, path, index), - "chinese_traditional" => derive_key::(mnemonic, path, index), - "czech" => derive_key::(mnemonic, path, index), - "english" => derive_key::(mnemonic, path, index), - "french" => derive_key::(mnemonic, path, index), - "italian" => derive_key::(mnemonic, path, index), - "japanese" => derive_key::(mnemonic, path, index), - "korean" => derive_key::(mnemonic, path, index), - "portuguese" => derive_key::(mnemonic, path, index), - "spanish" => derive_key::(mnemonic, path, index), - _ => Err(fmt_err!("unsupported mnemonic language: {language:?}")), - } -} - -fn derive_key(mnemonic: &str, path: &str, index: u32) -> Result { - fn derive_key_path(path: &str, index: u32) -> String { - let mut out = path.to_string(); - if !out.ends_with('/') { - out.push('/'); - } - out.push_str(&index.to_string()); - out - } - - let wallet = MnemonicBuilder::::default() - .phrase(mnemonic) - .derivation_path(derive_key_path(path, index))? - .build()?; - let private_key = U256::from_be_bytes(wallet.credential().to_bytes().into()); - Ok(private_key.abi_encode()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::CheatsConfig; - use alloy_primitives::{hex::FromHex, FixedBytes}; - use p256::ecdsa::signature::hazmat::PrehashVerifier; - use std::{path::PathBuf, sync::Arc}; - - fn cheats() -> Cheatcodes { - let config = CheatsConfig { - ffi: true, - root: PathBuf::from(&env!("CARGO_MANIFEST_DIR")), - ..Default::default() - }; - Cheatcodes { config: Arc::new(config), ..Default::default() } - } - - #[test] - fn test_sign_p256() { - use p256::ecdsa::VerifyingKey; - - let pk_u256: U256 = "1".parse().unwrap(); - let signing_key = P256SigningKey::from_bytes(&pk_u256.to_be_bytes().into()).unwrap(); - let digest = FixedBytes::from_hex( - "0x44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56", - ) - .unwrap(); - let mut cheats = cheats(); - - let result = sign_p256(&pk_u256, &digest, &mut cheats).unwrap(); - let result_bytes: [u8; 64] = result.try_into().unwrap(); - let signature = Signature::from_bytes(&result_bytes.into()).unwrap(); - let verifying_key = VerifyingKey::from(&signing_key); - assert!(verifying_key.verify_prehash(digest.as_slice(), &signature).is_ok()); - } - - #[test] - fn test_sign_p256_pk_too_large() { - // max n from https://neuromancer.sk/std/secg/secp256r1 - let pk = - "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551".parse().unwrap(); - let digest = FixedBytes::from_hex( - "0x54705ba3baafdbdfba8c5f9a70f7a89bee98d906b53e31074da7baecdc0da9ad", - ) - .unwrap(); - let mut cheats = cheats(); - let result = sign_p256(&pk, &digest, &mut cheats); - assert_eq!(result.err().unwrap().to_string(), "private key must be less than the secp256r1 curve order (115792089210356248762697446949407573529996955224135760342422259061068512044369)"); - } - - #[test] - fn test_sign_p256_pk_0() { - let digest = FixedBytes::from_hex( - "0x54705ba3baafdbdfba8c5f9a70f7a89bee98d906b53e31074da7baecdc0da9ad", - ) - .unwrap(); - let mut cheats = cheats(); - let result = sign_p256(&U256::ZERO, &digest, &mut cheats); - assert_eq!(result.err().unwrap().to_string(), "private key cannot be 0"); - } -} diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 4a9d9cd69..0e731d88e 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -383,15 +383,15 @@ interface Vm { function setEnv(string calldata name, string calldata value) external; function setNonce(address account, uint64 newNonce) external; function setNonceUnsafe(address account, uint64 newNonce) external; + function signCompact(Wallet calldata wallet, bytes32 digest) external returns (bytes32 r, bytes32 vs); function signCompact(uint256 privateKey, bytes32 digest) external pure returns (bytes32 r, bytes32 vs); function signCompact(bytes32 digest) external pure returns (bytes32 r, bytes32 vs); function signCompact(address signer, bytes32 digest) external pure returns (bytes32 r, bytes32 vs); - function signCompact(Wallet calldata wallet, bytes32 digest) external returns (bytes32 r, bytes32 vs); function signP256(uint256 privateKey, bytes32 digest) external pure returns (bytes32 r, bytes32 s); + function sign(Wallet calldata wallet, bytes32 digest) external returns (uint8 v, bytes32 r, bytes32 s); function sign(uint256 privateKey, bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); function sign(bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); function sign(address signer, bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); - function sign(Wallet calldata wallet, bytes32 digest) external returns (uint8 v, bytes32 r, bytes32 s); function skip(bool skipTest) external; function sleep(uint256 duration) external; function snapshot() external returns (uint256 snapshotId); From 00ff3673a1c8cd41076779f717d2827c16a004af Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sat, 3 Aug 2024 01:03:00 +0200 Subject: [PATCH 041/184] test: future proof deposit weth test (#8593) --- testdata/default/fork/LaunchFork.t.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testdata/default/fork/LaunchFork.t.sol b/testdata/default/fork/LaunchFork.t.sol index da5b365da..710ac97f5 100644 --- a/testdata/default/fork/LaunchFork.t.sol +++ b/testdata/default/fork/LaunchFork.t.sol @@ -72,7 +72,8 @@ contract ForkTest is DSTest { function testDepositWeth() public { IWETH WETH = IWETH(WETH_TOKEN_ADDR); + uint256 current = WETH.balanceOf(address(this)); WETH.deposit{value: 1000}(); - assertEq(WETH.balanceOf(address(this)), 1000, "WETH balance is not equal to deposited amount."); + assertEq(WETH.balanceOf(address(this)) - current, 1000, "WETH balance is not equal to deposited amount."); } } From 910850011c09437b57e99f8b94b25f03610f9e8a Mon Sep 17 00:00:00 2001 From: Evan Gray <56235822+evan-gray@users.noreply.github.com> Date: Fri, 2 Aug 2024 23:12:58 -0400 Subject: [PATCH 042/184] fix(anvil): support overlapping anvil_mine calls (#8594) * fix(anvil): support overlapping anvil_mine calls * no manual drop --------- Co-authored-by: Matthias Seitz --- crates/anvil/src/eth/backend/mem/mod.rs | 4 +++ crates/anvil/tests/it/api.rs | 42 +++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index ff7ec5b5c..c3285e25f 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -183,6 +183,8 @@ pub struct Backend { slots_in_an_epoch: u64, /// Precompiles to inject to the EVM. precompile_factory: Option>, + /// Prevent race conditions during mining + mining: Arc>, } impl Backend { @@ -259,6 +261,7 @@ impl Backend { node_config, slots_in_an_epoch, precompile_factory, + mining: Arc::new(tokio::sync::Mutex::new(())), }; if let Some(interval_block_time) = automine_block_time { @@ -927,6 +930,7 @@ impl Backend { &self, pool_transactions: Vec>, ) -> MinedBlockOutcome { + let _mining_guard = self.mining.lock().await; trace!(target: "backend", "creating new block with {} transactions", pool_transactions.len()); let (outcome, header, block_hash) = { diff --git a/crates/anvil/tests/it/api.rs b/crates/anvil/tests/it/api.rs index 9e640beb9..8843efddc 100644 --- a/crates/anvil/tests/it/api.rs +++ b/crates/anvil/tests/it/api.rs @@ -13,6 +13,7 @@ use alloy_rpc_types::{ }; use alloy_serde::WithOtherFields; use anvil::{eth::api::CLIENT_VERSION, spawn, NodeConfig, CHAIN_ID}; +use futures::join; use std::{collections::HashMap, time::Duration}; #[tokio::test(flavor = "multi_thread")] @@ -375,3 +376,44 @@ async fn can_call_with_state_override() { // `value` *is* changed with state assert_eq!(value, ""); } + +#[tokio::test(flavor = "multi_thread")] +async fn can_mine_while_mining() { + let (api, _) = spawn(NodeConfig::test()).await; + + let total_blocks = 200; + + let block_number = api + .block_by_number(BlockNumberOrTag::Latest) + .await + .unwrap() + .unwrap() + .header + .number + .unwrap(); + assert_eq!(block_number, 0); + + let block = api.block_by_number(BlockNumberOrTag::Number(block_number)).await.unwrap().unwrap(); + assert_eq!(block.header.number.unwrap(), 0); + + let result = join!( + api.anvil_mine(Some(U256::from(total_blocks / 2)), None), + api.anvil_mine(Some(U256::from(total_blocks / 2)), None) + ); + result.0.unwrap(); + result.1.unwrap(); + tokio::time::sleep(Duration::from_millis(100)).await; + + let block_number = api + .block_by_number(BlockNumberOrTag::Latest) + .await + .unwrap() + .unwrap() + .header + .number + .unwrap(); + assert_eq!(block_number, total_blocks); + + let block = api.block_by_number(BlockNumberOrTag::Number(block_number)).await.unwrap().unwrap(); + assert_eq!(block.header.number.unwrap(), total_blocks); +} From d54e94e54e5c655ec00b75e5cbd2b6036a3b12ba Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Sat, 3 Aug 2024 05:58:21 +0200 Subject: [PATCH 043/184] fix: add `--compiler-version` as alias of `use` in `forge create` (#8588) add compiler-version as alias of `use` --- crates/cli/src/opts/build/core.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/cli/src/opts/build/core.rs b/crates/cli/src/opts/build/core.rs index 81fbdf4cb..d575f9ae8 100644 --- a/crates/cli/src/opts/build/core.rs +++ b/crates/cli/src/opts/build/core.rs @@ -58,7 +58,12 @@ pub struct CoreBuildArgs { /// Specify the solc version, or a path to a local solc, to build with. /// /// Valid values are in the format `x.y.z`, `solc:x.y.z` or `path/to/solc`. - #[arg(long = "use", help_heading = "Compiler options", value_name = "SOLC_VERSION")] + #[arg( + long = "use", + alias = "compiler-version", + help_heading = "Compiler options", + value_name = "SOLC_VERSION" + )] #[serde(skip)] pub use_solc: Option, From 2cb388d4b31874a02cd9cd8efa289cdb7af7bf8a Mon Sep 17 00:00:00 2001 From: Ryan Schneider Date: Fri, 2 Aug 2024 21:00:30 -0700 Subject: [PATCH 044/184] feat(cast) tx: pretty print new transaction fields (#8569) --- crates/common/fmt/src/ui.rs | 397 ++++++++++++++++++++++++++++++++++-- 1 file changed, 378 insertions(+), 19 deletions(-) diff --git a/crates/common/fmt/src/ui.rs b/crates/common/fmt/src/ui.rs index 77bb22851..bd969fecd 100644 --- a/crates/common/fmt/src/ui.rs +++ b/crates/common/fmt/src/ui.rs @@ -3,7 +3,8 @@ use alloy_consensus::{AnyReceiptEnvelope, Eip658Value, Receipt, ReceiptWithBloom, TxType}; use alloy_primitives::{hex, Address, Bloom, Bytes, FixedBytes, Uint, B256, I256, U256, U64}; use alloy_rpc_types::{ - AnyTransactionReceipt, Block, BlockTransactions, Log, Transaction, TransactionReceipt, + AccessListItem, AnyTransactionReceipt, Block, BlockTransactions, Log, Transaction, + TransactionReceipt, }; use alloy_serde::OtherFields; use serde::Deserialize; @@ -306,10 +307,188 @@ impl UIfmt for OtherFields { } } +impl UIfmt for AccessListItem { + fn pretty(&self) -> String { + let mut s = String::with_capacity(42 + self.storage_keys.len() * 66); + s.push_str(self.address.pretty().as_str()); + s.push_str(" => "); + s.push_str(self.storage_keys.pretty().as_str()); + s + } +} + impl UIfmt for Transaction { fn pretty(&self) -> String { - format!( - " + match self.transaction_type { + Some(1) => format!( + " +accessList {} +blockHash {} +blockNumber {} +chainId {} +from {} +gasLimit {} +gasPrice {} +hash {} +input {} +nonce {} +r {} +s {} +to {} +transactionIndex {} +type {} +value {} +yParity {}{}", + self.access_list.as_deref().unwrap().pretty(), + self.block_hash.pretty(), + self.block_number.pretty(), + self.chain_id.pretty(), + self.from.pretty(), + self.gas.pretty(), + self.gas_price.pretty(), + self.hash.pretty(), + self.input.pretty(), + self.nonce.pretty(), + self.signature.map(|s| s.r.to_be_bytes_vec()).pretty(), + self.signature.map(|s| s.s.to_be_bytes_vec()).pretty(), + self.to.pretty(), + self.transaction_index.pretty(), + self.transaction_type.unwrap(), + self.value.pretty(), + self.signature.map(|s| s.v).pretty(), + self.other.pretty() + ), + Some(2) => format!( + " +accessList {} +blockHash {} +blockNumber {} +chainId {} +from {} +gasLimit {} +hash {} +input {} +maxFeePerGas {} +maxPriorityFeePerGas {} +nonce {} +r {} +s {} +to {} +transactionIndex {} +type {} +value {} +yParity {}{}", + self.access_list.as_deref().unwrap().pretty(), + self.block_hash.pretty(), + self.block_number.pretty(), + self.chain_id.pretty(), + self.from.pretty(), + self.gas.pretty(), + self.hash.pretty(), + self.input.pretty(), + self.max_fee_per_gas.pretty(), + self.max_priority_fee_per_gas.pretty(), + self.nonce.pretty(), + self.signature.map(|s| s.r.to_be_bytes_vec()).pretty(), + self.signature.map(|s| s.s.to_be_bytes_vec()).pretty(), + self.to.pretty(), + self.transaction_index.pretty(), + self.transaction_type.unwrap(), + self.value.pretty(), + self.signature.map(|s| s.v).pretty(), + self.other.pretty() + ), + Some(3) => format!( + " +accessList {} +blobVersionedHashes {} +blockHash {} +blockNumber {} +chainId {} +from {} +gasLimit {} +hash {} +input {} +maxFeePerBlobGas {} +maxFeePerGas {} +maxPriorityFeePerGas {} +nonce {} +r {} +s {} +to {} +transactionIndex {} +type {} +value {} +yParity {}{}", + self.access_list.as_deref().unwrap().pretty(), + self.blob_versioned_hashes.as_deref().unwrap().pretty(), + self.block_hash.pretty(), + self.block_number.pretty(), + self.chain_id.pretty(), + self.from.pretty(), + self.gas.pretty(), + self.hash.pretty(), + self.input.pretty(), + self.max_fee_per_blob_gas.pretty(), + self.max_fee_per_gas.pretty(), + self.max_priority_fee_per_gas.pretty(), + self.nonce.pretty(), + self.signature.map(|s| s.r.to_be_bytes_vec()).pretty(), + self.signature.map(|s| s.s.to_be_bytes_vec()).pretty(), + self.to.pretty(), + self.transaction_index.pretty(), + self.transaction_type.unwrap(), + self.value.pretty(), + self.signature.map(|s| s.v).pretty(), + self.other.pretty() + ), + Some(4) => format!( + " +accessList {} +authorizationList {} +blockHash {} +blockNumber {} +chainId {} +from {} +gasLimit {} +hash {} +input {} +maxFeePerGas {} +maxPriorityFeePerGas {} +nonce {} +r {} +s {} +to {} +transactionIndex {} +type {} +value {} +yParity {}{}", + self.access_list.as_deref().unwrap().pretty(), + self.authorization_list + .as_ref() + .map(|l| serde_json::to_string(&l).unwrap()) + .unwrap_or_default(), + self.block_hash.pretty(), + self.block_number.pretty(), + self.chain_id.pretty(), + self.from.pretty(), + self.gas.pretty(), + self.hash.pretty(), + self.input.pretty(), + self.max_fee_per_gas.pretty(), + self.max_priority_fee_per_gas.pretty(), + self.nonce.pretty(), + self.signature.map(|s| s.r.to_be_bytes_vec()).pretty(), + self.signature.map(|s| s.s.to_be_bytes_vec()).pretty(), + self.to.pretty(), + self.transaction_index.pretty(), + self.transaction_type.unwrap(), + self.value.pretty(), + self.signature.map(|s| s.v).pretty(), + self.other.pretty() + ), + _ => format!( + " blockHash {} blockNumber {} from {} @@ -324,22 +503,23 @@ to {} transactionIndex {} v {} value {}{}", - self.block_hash.pretty(), - self.block_number.pretty(), - self.from.pretty(), - self.gas.pretty(), - self.gas_price.pretty(), - self.hash.pretty(), - self.input.pretty(), - self.nonce, - self.signature.map(|s| s.r.to_be_bytes_vec()).pretty(), - self.signature.map(|s| s.s.to_be_bytes_vec()).pretty(), - self.to.pretty(), - self.transaction_index.pretty(), - self.signature.map(|s| s.v).pretty(), - self.value.pretty(), - self.other.pretty() - ) + self.block_hash.pretty(), + self.block_number.pretty(), + self.from.pretty(), + self.gas.pretty(), + self.gas_price.pretty(), + self.hash.pretty(), + self.input.pretty(), + self.nonce, + self.signature.map(|s| s.r.to_be_bytes_vec()).pretty(), + self.signature.map(|s| s.s.to_be_bytes_vec()).pretty(), + self.to.pretty(), + self.transaction_index.pretty(), + self.signature.map(|s| s.v).pretty(), + self.value.pretty(), + self.other.pretty() + ), + } } } @@ -560,6 +740,185 @@ txType 0 ); } + #[test] + fn can_pretty_print_eip2930() { + let s = r#"{ + "type": "0x1", + "blockHash": "0x2b27fe2bbc8ce01ac7ae8bf74f793a197cf7edbe82727588811fa9a2c4776f81", + "blockNumber": "0x12b1d", + "from": "0x2b371c0262ceab27face32fbb5270ddc6aa01ba4", + "gas": "0x6bdf", + "gasPrice": "0x3b9aca00", + "hash": "0xbddbb685774d8a3df036ed9fb920b48f876090a57e9e90ee60921e0510ef7090", + "input": "0x9c0e3f7a0000000000000000000000000000000000000000000000000000000000000078000000000000000000000000000000000000000000000000000000000000002a", + "nonce": "0x1c", + "to": "0x8e730df7c70d33118d9e5f79ab81aed0be6f6635", + "transactionIndex": "0x2", + "value": "0x0", + "v": "0x1", + "r": "0x2a98c51c2782f664d3ce571fef0491b48f5ebbc5845fa513192e6e6b24ecdaa1", + "s": "0x29b8e0c67aa9c11327e16556c591dc84a7aac2f6fc57c7f93901be8ee867aebc", + "chainId": "0x66a", + "accessList": [ + { "address": "0x2b371c0262ceab27face32fbb5270ddc6aa01ba4", "storageKeys": ["0x1122334455667788990011223344556677889900112233445566778899001122", "0x0000000000000000000000000000000000000000000000000000000000000000"] }, + { "address": "0x8e730df7c70d33118d9e5f79ab81aed0be6f6635", "storageKeys": [] } + ] + } + "#; + + let tx: Transaction = serde_json::from_str(s).unwrap(); + assert_eq!(tx.pretty().trim(), + r" +accessList [ + 0x2b371c0262CEAb27fAcE32FBB5270dDc6Aa01ba4 => [ + 0x1122334455667788990011223344556677889900112233445566778899001122 + 0x0000000000000000000000000000000000000000000000000000000000000000 + ] + 0x8E730Df7C70D33118D9e5F79ab81aEd0bE6F6635 => [] +] +blockHash 0x2b27fe2bbc8ce01ac7ae8bf74f793a197cf7edbe82727588811fa9a2c4776f81 +blockNumber 76573 +chainId 1642 +from 0x2b371c0262CEAb27fAcE32FBB5270dDc6Aa01ba4 +gasLimit 27615 +gasPrice 1000000000 +hash 0xbddbb685774d8a3df036ed9fb920b48f876090a57e9e90ee60921e0510ef7090 +input 0x9c0e3f7a0000000000000000000000000000000000000000000000000000000000000078000000000000000000000000000000000000000000000000000000000000002a +nonce 28 +r 0x2a98c51c2782f664d3ce571fef0491b48f5ebbc5845fa513192e6e6b24ecdaa1 +s 0x29b8e0c67aa9c11327e16556c591dc84a7aac2f6fc57c7f93901be8ee867aebc +to 0x8E730Df7C70D33118D9e5F79ab81aEd0bE6F6635 +transactionIndex 2 +type 1 +value 0 +yParity 1 +".trim() + ); + } + + #[test] + fn can_pretty_print_eip1559() { + let s = r#"{ + "type": "0x2", + "blockHash": "0x61abbe5e22738de0462046f5a5d6c4cd6bc1f3a6398e4457d5e293590e721125", + "blockNumber": "0x7647", + "from": "0xbaadf00d42264eeb3fafe6799d0b56cf55df0f00", + "gas": "0x186a0", + "hash": "0xa7231d4da0576fade5d3b9481f4cd52459ec59b9bbdbf4f60d6cd726b2a3a244", + "input": "0x48600055323160015500", + "nonce": "0x12c", + "to": null, + "transactionIndex": "0x41", + "value": "0x0", + "v": "0x1", + "yParity": "0x1", + "r": "0x396864e5f9132327defdb1449504252e1fa6bce73feb8cd6f348a342b198af34", + "s": "0x44dbba72e6d3304104848277143252ee43627c82f02d1ef8e404e1bf97c70158", + "gasPrice": "0x4a817c800", + "maxFeePerGas": "0x4a817c800", + "maxPriorityFeePerGas": "0x4a817c800", + "chainId": "0x66a", + "accessList": [ + { + "address": "0xc141a9a7463e6c4716d9fc0c056c054f46bb2993", + "storageKeys": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ] + } + ] + } +"#; + let tx: Transaction = serde_json::from_str(s).unwrap(); + assert_eq!( + tx.pretty().trim(), + r" +accessList [ + 0xC141a9A7463e6C4716d9FC0C056C054F46Bb2993 => [ + 0x0000000000000000000000000000000000000000000000000000000000000000 + ] +] +blockHash 0x61abbe5e22738de0462046f5a5d6c4cd6bc1f3a6398e4457d5e293590e721125 +blockNumber 30279 +chainId 1642 +from 0xBaaDF00d42264eEb3FAFe6799d0b56cf55DF0F00 +gasLimit 100000 +hash 0xa7231d4da0576fade5d3b9481f4cd52459ec59b9bbdbf4f60d6cd726b2a3a244 +input 0x48600055323160015500 +maxFeePerGas 20000000000 +maxPriorityFeePerGas 20000000000 +nonce 300 +r 0x396864e5f9132327defdb1449504252e1fa6bce73feb8cd6f348a342b198af34 +s 0x44dbba72e6d3304104848277143252ee43627c82f02d1ef8e404e1bf97c70158 +to +transactionIndex 65 +type 2 +value 0 +yParity 1 +" + .trim() + ); + } + + #[test] + fn can_pretty_print_eip4884() { + let s = r#"{ + "blockHash": "0xfc2715ff196e23ae613ed6f837abd9035329a720a1f4e8dce3b0694c867ba052", + "blockNumber": "0x2a1cb", + "from": "0xad01b55d7c3448b8899862eb335fbb17075d8de2", + "gas": "0x5208", + "gasPrice": "0x1d1a94a201c", + "maxFeePerGas": "0x1d1a94a201c", + "maxPriorityFeePerGas": "0x1d1a94a201c", + "maxFeePerBlobGas": "0x3e8", + "hash": "0x5ceec39b631763ae0b45a8fb55c373f38b8fab308336ca1dc90ecd2b3cf06d00", + "input": "0x", + "nonce": "0x1b483", + "to": "0x000000000000000000000000000000000000f1c1", + "transactionIndex": "0x0", + "value": "0x0", + "type": "0x3", + "accessList": [], + "chainId": "0x1a1f0ff42", + "blobVersionedHashes": [ + "0x01a128c46fc61395706686d6284f83c6c86dfc15769b9363171ea9d8566e6e76" + ], + "v": "0x0", + "r": "0x343c6239323a81ef61293cb4a4d37b6df47fbf68114adb5dd41581151a077da1", + "s": "0x48c21f6872feaf181d37cc4f9bbb356d3f10b352ceb38d1c3b190d749f95a11b", + "yParity": "0x0" + } +"#; + let tx: Transaction = serde_json::from_str(s).unwrap(); + assert_eq!( + tx.pretty().trim(), + r" +accessList [] +blobVersionedHashes [ + 0x01a128c46fc61395706686d6284f83c6c86dfc15769b9363171ea9d8566e6e76 +] +blockHash 0xfc2715ff196e23ae613ed6f837abd9035329a720a1f4e8dce3b0694c867ba052 +blockNumber 172491 +chainId 7011893058 +from 0xAD01b55d7c3448B8899862eb335FBb17075d8DE2 +gasLimit 21000 +hash 0x5ceec39b631763ae0b45a8fb55c373f38b8fab308336ca1dc90ecd2b3cf06d00 +input 0x +maxFeePerBlobGas 1000 +maxFeePerGas 2000000000028 +maxPriorityFeePerGas 2000000000028 +nonce 111747 +r 0x343c6239323a81ef61293cb4a4d37b6df47fbf68114adb5dd41581151a077da1 +s 0x48c21f6872feaf181d37cc4f9bbb356d3f10b352ceb38d1c3b190d749f95a11b +to 0x000000000000000000000000000000000000f1C1 +transactionIndex 0 +type 3 +value 0 +yParity 0 +" + .trim() + ); + } + #[test] fn print_block_w_txs() { let block = r#"{"number":"0x3","hash":"0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972","parentHash":"0x689c70c080ca22bc0e681694fa803c1aba16a69c8b6368fed5311d279eb9de90","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x7270c1c4440180f2bd5215809ee3d545df042b67329499e1ab97eb759d31610d","stateRoot":"0x29f32984517a7d25607da485b23cefabfd443751422ca7e603395e1de9bc8a4b","receiptsRoot":"0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2","miner":"0x0000000000000000000000000000000000000000","difficulty":"0x0","totalDifficulty":"0x0","extraData":"0x","size":"0x3e8","gasLimit":"0x6691b7","gasUsed":"0x5208","timestamp":"0x5ecedbb9","transactions":[{"hash":"0xc3c5f700243de37ae986082fd2af88d2a7c2752a0c0f7b9d6ac47c729d45e067","nonce":"0x2","blockHash":"0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972","blockNumber":"0x3","transactionIndex":"0x0","from":"0xfdcedc3bfca10ecb0890337fbdd1977aba84807a","to":"0xdca8ce283150ab773bcbeb8d38289bdb5661de1e","value":"0x0","gas":"0x15f90","gasPrice":"0x4a817c800","input":"0x","v":"0x25","r":"0x19f2694eb9113656dbea0b925e2e7ceb43df83e601c4116aee9c0dd99130be88","s":"0x73e5764b324a4f7679d890a198ba658ba1c8cd36983ff9797e10b1b89dbb448e"}],"uncles":[]}"#; From 55bf41564f605cae3ca4c95ac5d468b1f14447f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Vincent?= <28714795+leovct@users.noreply.github.com> Date: Sat, 3 Aug 2024 07:40:01 +0200 Subject: [PATCH 045/184] feat(forge): modify `events` in `forge inspect` to return event signature (#8561) * feat(forge): add `eventIdentifiers` to `forge inspect` * chore: remove quotes from output * fix: test * fix: output 32 bytes of the event identifier * fix: add back `0x` prefix * chore: modify forge inspect events instead of implementing a new cmd --- crates/forge/bin/cmd/inspect.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/forge/bin/cmd/inspect.rs b/crates/forge/bin/cmd/inspect.rs index ac0ed41b3..c1c9f7db8 100644 --- a/crates/forge/bin/cmd/inspect.rs +++ b/crates/forge/bin/cmd/inspect.rs @@ -1,4 +1,4 @@ -use alloy_primitives::Address; +use alloy_primitives::{hex, keccak256, Address}; use clap::Parser; use comfy_table::{presets::ASCII_MARKDOWN, Table}; use eyre::{Context, Result}; @@ -133,7 +133,7 @@ impl InspectArgs { let mut out = serde_json::Map::new(); if let Some(abi) = &artifact.abi { let abi = &abi; - // Print the signature of all errors + // Print the signature of all errors. for er in abi.errors.iter().flat_map(|(_, errors)| errors) { let types = er.inputs.iter().map(|p| p.ty.clone()).collect::>(); let sig = format!("{:x}", er.selector()); @@ -150,13 +150,13 @@ impl InspectArgs { let mut out = serde_json::Map::new(); if let Some(abi) = &artifact.abi { let abi = &abi; - - // print the signature of all events including anonymous + // Print the topic of all events including anonymous. for ev in abi.events.iter().flat_map(|(_, events)| events) { let types = ev.inputs.iter().map(|p| p.ty.clone()).collect::>(); + let topic = hex::encode(keccak256(ev.signature())); out.insert( format!("{}({})", ev.name, types.join(",")), - format!("{:?}", ev.signature()).into(), + format!("0x{topic}").into(), ); } } From 0b73b426d3aeb1563eeab4d5f2f8134d1c3902e3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 4 Aug 2024 07:31:48 +0000 Subject: [PATCH 046/184] chore(deps): weekly `cargo update` (#8598) --- Cargo.lock | 88 ++++++++++++++++++++++++------------------------------ 1 file changed, 39 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1498a39a5..0fccb0dd3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,7 +48,7 @@ dependencies = [ "getrandom", "once_cell", "version_check", - "zerocopy 0.7.35", + "zerocopy", ] [[package]] @@ -68,9 +68,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-chains" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47ff94ce0f141c2671c23d02c7b88990dd432856639595c5d010663d017c2c58" +checksum = "3312b2a48f29abe7c3ea7c7fbc1f8cc6ea09b85d74b6232e940df35f2f3826fd" dependencies = [ "num_enum", "serde", @@ -3286,9 +3286,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920" dependencies = [ "crc32fast", "miniz_oxide", @@ -5769,11 +5769,11 @@ checksum = "f5438dd2b2ff4c6df6e1ce22d825ed2fa93ee2922235cc45186991717f0a892d" [[package]] name = "normpath" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5831952a9476f2fed74b77d74182fa5ddc4d21c72ec45a333b250e3ed0272804" +checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -6495,11 +6495,11 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.18" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee4364d9f3b902ef14fab8a1ddffb783a1cb6b4bba3bfc1fa3922732c7de97f" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy 0.6.6", + "zerocopy", ] [[package]] @@ -6988,9 +6988,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.5" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -7111,7 +7111,7 @@ dependencies = [ "quinn", "rustls 0.23.12", "rustls-native-certs 0.7.1", - "rustls-pemfile 2.1.2", + "rustls-pemfile 2.1.3", "rustls-pki-types", "serde", "serde_json", @@ -7448,7 +7448,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a88d6d420651b496bdd98684116959239430022a115c1240e6c3993be0b15fba" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.2", + "rustls-pemfile 2.1.3", "rustls-pki-types", "schannel", "security-framework", @@ -7465,9 +7465,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" dependencies = [ "base64 0.22.1", "rustls-pki-types", @@ -7591,9 +7591,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.1.6" +version = "2.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ccfb12511cdb770157ace92d7dda771e498445b78f9886e8cdbc5140a4eced" +checksum = "a870e34715d5d59c8536040d4d4e7a41af44d527dc50237036ba4090db7996fc" dependencies = [ "sdd", ] @@ -8394,12 +8394,13 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.10.1" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "b8fcd239983515c23a32fb82099f97d0b11b8c72f654ed659363a95c3dad7a53" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", "windows-sys 0.52.0", ] @@ -8790,7 +8791,7 @@ dependencies = [ "pin-project 1.1.5", "prost 0.12.6", "rustls-native-certs 0.7.1", - "rustls-pemfile 2.1.2", + "rustls-pemfile 2.1.3", "rustls-pki-types", "tokio", "tokio-rustls 0.25.0", @@ -9471,11 +9472,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -9628,6 +9629,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -9847,34 +9857,14 @@ dependencies = [ "thiserror", ] -[[package]] -name = "zerocopy" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6" -dependencies = [ - "byteorder", - "zerocopy-derive 0.6.6", -] - [[package]] name = "zerocopy" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "zerocopy-derive 0.7.35", -] - -[[package]] -name = "zerocopy-derive" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.72", + "byteorder", + "zerocopy-derive", ] [[package]] @@ -9990,9 +9980,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.12+zstd.1.5.6" +version = "2.0.13+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" dependencies = [ "cc", "pkg-config", From 42d44bf4841b897107e0a8c41b44154cb3a65ce5 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 5 Aug 2024 22:01:07 +0300 Subject: [PATCH 047/184] fix(coverage): account if/else statements without brackets (#8608) fix(coverage): recognize if/else statements without brackets --- crates/evm/coverage/src/analysis.rs | 19 +++++-- crates/forge/tests/cli/coverage.rs | 83 +++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 3 deletions(-) diff --git a/crates/evm/coverage/src/analysis.rs b/crates/evm/coverage/src/analysis.rs index e28346181..15e2bab30 100644 --- a/crates/evm/coverage/src/analysis.rs +++ b/crates/evm/coverage/src/analysis.rs @@ -534,10 +534,23 @@ impl<'a> ContractVisitor<'a> { } } -/// Helper function to check if a given node contains any statement. +/// Helper function to check if a given node is or contains any statement. fn has_statements(node: &Node) -> bool { - let statements: Vec = node.attribute("statements").unwrap_or_default(); - !statements.is_empty() + match node.node_type { + NodeType::DoWhileStatement | + NodeType::EmitStatement | + NodeType::ExpressionStatement | + NodeType::ForStatement | + NodeType::IfStatement | + NodeType::RevertStatement | + NodeType::TryStatement | + NodeType::VariableDeclarationStatement | + NodeType::WhileStatement => true, + _ => { + let statements: Vec = node.attribute("statements").unwrap_or_default(); + !statements.is_empty() + } + } } /// [`SourceAnalyzer`] result type. diff --git a/crates/forge/tests/cli/coverage.rs b/crates/forge/tests/cli/coverage.rs index e8999a7f3..a2ee110ee 100644 --- a/crates/forge/tests/cli/coverage.rs +++ b/crates/forge/tests/cli/coverage.rs @@ -1030,3 +1030,86 @@ contract FooTest is DSTest { "#]], ); }); + +// https://github.com/foundry-rs/foundry/issues/8605 +forgetest!(test_single_statement_coverage, |prj, cmd| { + prj.insert_ds_test(); + prj.add_source( + "AContract.sol", + r#" +contract AContract { + event IsTrue(bool isTrue); + event IsFalse(bool isFalse); + + function ifElseStatementIgnored(bool flag) external { + if (flag) emit IsTrue(true); + else emit IsFalse(false); + + if (flag) flag = true; + else flag = false; + } +} + "#, + ) + .unwrap(); + + prj.add_source( + "AContractTest.sol", + r#" +import "./test.sol"; +import {AContract} from "./AContract.sol"; + +contract AContractTest is DSTest { + function testTrueCoverage() external { + AContract a = new AContract(); + a.ifElseStatementIgnored(true); + } + + function testFalseCoverage() external { + AContract a = new AContract(); + a.ifElseStatementIgnored(false); + } +} + "#, + ) + .unwrap(); + + // Assert 50% coverage for true branches. + cmd.arg("coverage") + .args(["--mt".to_string(), "testTrueCoverage".to_string()]) + .assert_success() + .stdout_eq(str![[r#" +... +| File | % Lines | % Statements | % Branches | % Funcs | +|-------------------|--------------|--------------|--------------|---------------| +| src/AContract.sol | 50.00% (2/4) | 50.00% (2/4) | 50.00% (2/4) | 100.00% (1/1) | +| Total | 50.00% (2/4) | 50.00% (2/4) | 50.00% (2/4) | 100.00% (1/1) | + +"#]]); + + // Assert 50% coverage for false branches. + cmd.forge_fuse() + .arg("coverage") + .args(["--mt".to_string(), "testFalseCoverage".to_string()]) + .assert_success() + .stdout_eq(str![[r#" +... +| File | % Lines | % Statements | % Branches | % Funcs | +|-------------------|---------------|--------------|--------------|---------------| +| src/AContract.sol | 100.00% (4/4) | 50.00% (2/4) | 50.00% (2/4) | 100.00% (1/1) | +| Total | 100.00% (4/4) | 50.00% (2/4) | 50.00% (2/4) | 100.00% (1/1) | + +"#]]); + + // Assert 100% coverage (true/false branches properly covered). + cmd.forge_fuse().arg("coverage").args(["--summary".to_string()]).assert_success().stdout_eq( + str![[r#" +... +| File | % Lines | % Statements | % Branches | % Funcs | +|-------------------|---------------|---------------|---------------|---------------| +| src/AContract.sol | 100.00% (4/4) | 100.00% (4/4) | 100.00% (4/4) | 100.00% (1/1) | +| Total | 100.00% (4/4) | 100.00% (4/4) | 100.00% (4/4) | 100.00% (1/1) | + +"#]], + ); +}); From f2518c92c8743777a4941a91e4eb56dd3a96ff0f Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 5 Aug 2024 22:52:56 +0200 Subject: [PATCH 048/184] ci: enable asm-keccak on more platforms (#8609) --- .github/workflows/release.yml | 6 ++-- Cargo.lock | 52 ++++++++++++++++------------------- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 83d216c7f..9c846ed8c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -133,11 +133,11 @@ jobs: run: | set -eo pipefail target="${{ matrix.target }}" - flags=(--release --bins --no-default-features --features rustls,aws-kms,cli) + flags=(--release --bins --no-default-features --features rustls,aws-kms,cli,asm-keccak) - # `jemalloc` and `keccak-asm` are not supported on MSVC or aarch64 Linux. + # `jemalloc` is not fully supported on MSVC or aarch64 Linux. if [[ "$target" != *msvc* && "$target" != "aarch64-unknown-linux-gnu" ]]; then - flags+=(--features asm-keccak,jemalloc) + flags+=(--features jemalloc) fi [[ "$target" == *windows* ]] && exe=".exe" diff --git a/Cargo.lock b/Cargo.lock index 0fccb0dd3..3d1e00b94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2833,9 +2833,9 @@ checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" [[package]] name = "dunce" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "dyn-clone" @@ -3379,7 +3379,7 @@ dependencies = [ "regex", "reqwest 0.12.5", "revm-inspectors", - "rustc-hash 2.0.0", + "rustc-hash", "semver 1.0.23", "serde", "serde_json", @@ -3601,7 +3601,7 @@ dependencies = [ "proptest", "rand", "revm", - "rustc-hash 2.0.0", + "rustc-hash", "semver 1.0.23", "serde_json", "thiserror", @@ -3691,7 +3691,7 @@ dependencies = [ "num-format", "once_cell", "reqwest 0.12.5", - "rustc-hash 2.0.0", + "rustc-hash", "semver 1.0.23", "serde", "serde_json", @@ -3925,7 +3925,7 @@ dependencies = [ "foundry-test-utils", "itertools 0.13.0", "once_cell", - "rustc-hash 2.0.0", + "rustc-hash", ] [[package]] @@ -3954,7 +3954,7 @@ dependencies = [ "parking_lot", "revm", "revm-inspectors", - "rustc-hash 2.0.0", + "rustc-hash", "serde", "serde_json", "thiserror", @@ -3973,7 +3973,7 @@ dependencies = [ "foundry-evm-core", "rayon", "revm", - "rustc-hash 2.0.0", + "rustc-hash", "semver 1.0.23", "tracing", ] @@ -4025,7 +4025,7 @@ dependencies = [ "rayon", "revm", "revm-inspectors", - "rustc-hash 2.0.0", + "rustc-hash", "serde", "solang-parser", "tempfile", @@ -4048,7 +4048,7 @@ dependencies = [ "futures", "parking_lot", "revm", - "rustc-hash 2.0.0", + "rustc-hash", "serde", "serde_json", "thiserror", @@ -5304,9 +5304,9 @@ dependencies = [ [[package]] name = "keccak-asm" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47a3633291834c4fbebf8673acbc1b04ec9d151418ff9b8e26dcd79129928758" +checksum = "422fbc7ff2f2f5bdffeb07718e5a5324dca72b0c9293d50df4026652385e3314" dependencies = [ "digest 0.10.7", "sha3-asm", @@ -6803,16 +6803,17 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ceeeeabace7857413798eb1ffa1e9c905a9946a57d81fb69b4b71c4d8eb3ad" +checksum = "b22d8e7369034b9a7132bc2008cac12f2013c8132b45e0554e6e20e2617f2156" dependencies = [ "bytes", "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 1.1.0", + "rustc-hash", "rustls 0.23.12", + "socket2", "thiserror", "tokio", "tracing", @@ -6820,14 +6821,14 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.3" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddf517c03a109db8100448a4be38d498df8a210a99fe0e1b9eaf39e78c640efe" +checksum = "ba92fb39ec7ad06ca2582c0ca834dfeadcaf06ddfc8e635c80aa7e1c05315fdd" dependencies = [ "bytes", "rand", "ring", - "rustc-hash 1.1.0", + "rustc-hash", "rustls 0.23.12", "slab", "thiserror", @@ -6844,6 +6845,7 @@ dependencies = [ "libc", "once_cell", "socket2", + "tracing", "windows-sys 0.52.0", ] @@ -7340,12 +7342,6 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustc-hash" version = "2.0.0" @@ -7591,9 +7587,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a870e34715d5d59c8536040d4d4e7a41af44d527dc50237036ba4090db7996fc" +checksum = "8d777f59627453628a9a5be1ee8d948745b94b1dfc2d0c3099cbd9e08ab89e7c" dependencies = [ "sdd", ] @@ -7960,9 +7956,9 @@ dependencies = [ [[package]] name = "sha3-asm" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9b57fd861253bff08bb1919e995f90ba8f4889de2726091c8876f3a4e823b40" +checksum = "57d79b758b7cb2085612b11a235055e485605a5103faccdd633f35bd7aee69dd" dependencies = [ "cc", "cfg-if", From 57c621d88917116b06ddc094cc6dfb0c1c7b5462 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 00:38:25 +0000 Subject: [PATCH 049/184] chore(tests): bump forge-std version (#8610) chore: bump forge-std version used for tests Co-authored-by: DaniPopes --- testdata/forge-std-rev | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testdata/forge-std-rev b/testdata/forge-std-rev index 9b2bd83b3..239d2091e 100644 --- a/testdata/forge-std-rev +++ b/testdata/forge-std-rev @@ -1 +1 @@ -07263d193d621c4b2b0ce8b4d54af58f6957d97d \ No newline at end of file +1714bee72e286e73f76e320d110e0eaf5c4e649d \ No newline at end of file From 1ba907bbcca6eb9bfc8fe5d3471d73f2309407c5 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Tue, 6 Aug 2024 12:08:34 +0200 Subject: [PATCH 050/184] feat: add default `T-needs-triage` label to default feature / bug form (#8612) * add T-needs-triage labels * fix regression --- .github/ISSUE_TEMPLATE/BUG-FORM.yml | 2 +- .github/ISSUE_TEMPLATE/FEATURE-FORM.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/BUG-FORM.yml b/.github/ISSUE_TEMPLATE/BUG-FORM.yml index 8067de866..b2575134d 100644 --- a/.github/ISSUE_TEMPLATE/BUG-FORM.yml +++ b/.github/ISSUE_TEMPLATE/BUG-FORM.yml @@ -1,6 +1,6 @@ name: Bug report description: File a bug report -labels: ["T-bug"] +labels: ["T-bug", "T-needs-triage"] body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/FEATURE-FORM.yml b/.github/ISSUE_TEMPLATE/FEATURE-FORM.yml index d7df54030..581eb5c0d 100644 --- a/.github/ISSUE_TEMPLATE/FEATURE-FORM.yml +++ b/.github/ISSUE_TEMPLATE/FEATURE-FORM.yml @@ -1,6 +1,6 @@ name: Feature request description: Suggest a feature -labels: ["T-feature"] +labels: ["T-feature", "T-needs-triage"] body: - type: markdown attributes: From e9c8bf5f30b697cd4d7ab6e059acb353ed62fa8c Mon Sep 17 00:00:00 2001 From: Qiwei Yang Date: Tue, 6 Aug 2024 19:52:55 +0800 Subject: [PATCH 051/184] Add "%ne" format support for forge console log (#8543) * wip * fix * fix sign * fix --------- Co-authored-by: Matthias Seitz --- crates/common/fmt/src/console.rs | 131 ++++++++++++++++++++++++------- 1 file changed, 104 insertions(+), 27 deletions(-) diff --git a/crates/common/fmt/src/console.rs b/crates/common/fmt/src/console.rs index efade2e43..45f12ffbf 100644 --- a/crates/common/fmt/src/console.rs +++ b/crates/common/fmt/src/console.rs @@ -1,5 +1,6 @@ use super::UIfmt; use alloy_primitives::{Address, Bytes, FixedBytes, I256, U256}; +use std::iter::Peekable; /// A format specifier. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] @@ -13,21 +14,45 @@ pub enum FormatSpec { Integer, /// %o format spec Object, - /// %e format spec - Exponential, + /// %e format spec with an optional precision + Exponential(Option), /// %x format spec Hexadecimal, } impl FormatSpec { - fn from_char(ch: char) -> Option { - match ch { + fn from_chars(iter: &mut Peekable) -> Option + where + I: Iterator, + { + match iter.next()? { 's' => Some(Self::String), 'd' => Some(Self::Number), 'i' => Some(Self::Integer), 'o' => Some(Self::Object), - 'e' => Some(Self::Exponential), + 'e' => Some(Self::Exponential(None)), 'x' => Some(Self::Hexadecimal), + ch if ch.is_ascii_digit() => { + let mut num = ch.to_string(); + while let Some(&ch) = iter.peek() { + if ch.is_ascii_digit() { + num.push(ch); + iter.next(); + } else { + break; + } + } + if let Some(&ch) = iter.peek() { + if ch == 'e' { + iter.next(); + Some(Self::Exponential(Some(num.parse().ok()?))) + } else { + None + } + } else { + None + } + } _ => None, } } @@ -46,7 +71,7 @@ impl ConsoleFmt for String { FormatSpec::Object => format!("'{}'", self.clone()), FormatSpec::Number | FormatSpec::Integer | - FormatSpec::Exponential | + FormatSpec::Exponential(_) | FormatSpec::Hexadecimal => Self::from("NaN"), } } @@ -58,7 +83,7 @@ impl ConsoleFmt for bool { FormatSpec::String => self.pretty(), FormatSpec::Object => format!("'{}'", self.pretty()), FormatSpec::Number => (*self as i32).to_string(), - FormatSpec::Integer | FormatSpec::Exponential | FormatSpec::Hexadecimal => { + FormatSpec::Integer | FormatSpec::Exponential(_) | FormatSpec::Hexadecimal => { String::from("NaN") } } @@ -75,7 +100,7 @@ impl ConsoleFmt for U256 { let hex = format!("{self:x}"); format!("0x{}", hex.trim_start_matches('0')) } - FormatSpec::Exponential => { + FormatSpec::Exponential(None) => { let log = self.pretty().len() - 1; let exp10 = Self::from(10).pow(Self::from(log)); let amount = *self; @@ -88,6 +113,18 @@ impl ConsoleFmt for U256 { format!("{integer}e{log}") } } + FormatSpec::Exponential(Some(precision)) => { + let exp10 = Self::from(10).pow(Self::from(precision)); + let amount = *self; + let integer = amount / exp10; + let decimal = (amount % exp10).to_string(); + let decimal = format!("{decimal:0>precision$}").trim_end_matches('0').to_string(); + if !decimal.is_empty() { + format!("{integer}.{decimal}") + } else { + format!("{integer}") + } + } } } } @@ -102,7 +139,7 @@ impl ConsoleFmt for I256 { let hex = format!("{self:x}"); format!("0x{}", hex.trim_start_matches('0')) } - FormatSpec::Exponential => { + FormatSpec::Exponential(None) => { let amount = *self; let sign = if amount.is_negative() { "-" } else { "" }; let log = if amount.is_negative() { @@ -117,7 +154,20 @@ impl ConsoleFmt for I256 { if !decimal.is_empty() { format!("{sign}{integer}.{decimal}e{log}") } else { - format!("{integer}e{log}") + format!("{sign}{integer}e{log}") + } + } + FormatSpec::Exponential(Some(precision)) => { + let amount = *self; + let sign = if amount.is_negative() { "-" } else { "" }; + let exp10 = Self::exp10(precision); + let integer = (amount / exp10).twos_complement(); + let decimal = (amount % exp10).twos_complement().to_string(); + let decimal = format!("{decimal:0>precision$}").trim_end_matches('0').to_string(); + if !decimal.is_empty() { + format!("{sign}{integer}.{decimal}") + } else { + format!("{sign}{integer}") } } } @@ -129,7 +179,7 @@ impl ConsoleFmt for Address { match spec { FormatSpec::String | FormatSpec::Hexadecimal => self.pretty(), FormatSpec::Object => format!("'{}'", self.pretty()), - FormatSpec::Number | FormatSpec::Integer | FormatSpec::Exponential => { + FormatSpec::Number | FormatSpec::Integer | FormatSpec::Exponential(_) => { String::from("NaN") } } @@ -165,7 +215,7 @@ impl ConsoleFmt for [u8] { match spec { FormatSpec::String | FormatSpec::Hexadecimal => self.pretty(), FormatSpec::Object => format!("'{}'", self.pretty()), - FormatSpec::Number | FormatSpec::Integer | FormatSpec::Exponential => { + FormatSpec::Number | FormatSpec::Integer | FormatSpec::Exponential(_) => { String::from("NaN") } } @@ -233,31 +283,37 @@ fn format_spec<'a>( ) -> Option<&'a dyn ConsoleFmt> { let mut expect_fmt = false; let mut current_value = values.next(); + let mut chars = s.chars().peekable(); - for (i, ch) in s.char_indices() { - // no more values - if current_value.is_none() { - result.push_str(&s[i..].replace("%%", "%")); - break - } - + while let Some(ch) = chars.next() { if expect_fmt { expect_fmt = false; - if let Some(spec) = FormatSpec::from_char(ch) { + let mut iter = std::iter::once(ch).chain(chars.by_ref()).peekable(); + if let Some(spec) = FormatSpec::from_chars(&mut iter) { // format and write the value - let string = current_value.unwrap().fmt(spec); - result.push_str(&string); - current_value = values.next(); + if let Some(value) = current_value { + let string = value.fmt(spec); + result.push_str(&string); + current_value = values.next(); + } } else { // invalid specifier or a second `%`, in both cases we ignore + result.push('%'); result.push(ch); } - } else { - expect_fmt = ch == '%'; - // push when not a `%` or it's the last char - if !expect_fmt || i == s.len() - 1 { + } else if ch == '%' { + if let Some(&next_ch) = chars.peek() { + if next_ch == '%' { + result.push('%'); + chars.next(); + } else { + expect_fmt = true; + } + } else { result.push(ch); } + } else { + result.push(ch); } } @@ -388,10 +444,20 @@ mod tests { assert_eq!("100", fmt_1("%d", &I256::try_from(100).unwrap())); assert_eq!("100", fmt_1("%i", &I256::try_from(100).unwrap())); assert_eq!("1e2", fmt_1("%e", &I256::try_from(100).unwrap())); + assert_eq!("-1e2", fmt_1("%e", &I256::try_from(-100).unwrap())); assert_eq!("-1.0023e6", fmt_1("%e", &I256::try_from(-1002300).unwrap())); assert_eq!("-1.23e5", fmt_1("%e", &I256::try_from(-123000).unwrap())); assert_eq!("1.0023e6", fmt_1("%e", &I256::try_from(1002300).unwrap())); assert_eq!("1.23e5", fmt_1("%e", &I256::try_from(123000).unwrap())); + + // %ne + assert_eq!("10", fmt_1("%1e", &I256::try_from(100).unwrap())); + assert_eq!("-1", fmt_1("%2e", &I256::try_from(-100).unwrap())); + assert_eq!("123000", fmt_1("%0e", &I256::try_from(123000).unwrap())); + assert_eq!("12300", fmt_1("%1e", &I256::try_from(123000).unwrap())); + assert_eq!("0.0123", fmt_1("%7e", &I256::try_from(123000).unwrap())); + assert_eq!("-0.0123", fmt_1("%7e", &I256::try_from(-123000).unwrap())); + assert_eq!("0x64", fmt_1("%x", &I256::try_from(100).unwrap())); assert_eq!( "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c", @@ -430,6 +496,17 @@ mod tests { "foo 0xdEADBEeF00000000000000000000000000000000 %s true and 21 foo %", logf3!(log3) ); + + // %ne + let log4 = Log1 { p_0: String::from("%5e"), p_1: U256::from(123456789) }; + assert_eq!("1234.56789", logf1!(log4)); + + let log5 = Log1 { p_0: String::from("foo %3e bar"), p_1: U256::from(123456789) }; + assert_eq!("foo 123456.789 bar", logf1!(log5)); + + let log6 = + Log2 { p_0: String::from("%e and %12e"), p_1: false, p_2: U256::from(123456789) }; + assert_eq!("NaN and 0.000123456789", logf2!(log6)); } #[test] From fbdd40d0c296413352693f677de38864eebbf0d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Vincent?= <28714795+leovct@users.noreply.github.com> Date: Tue, 6 Aug 2024 14:49:22 +0200 Subject: [PATCH 052/184] feat(`cast interface`): allow retrieving abi from contract name (#8585) * feat(`cast interface`): allow retrieving abi from contract name * fix: cast tests * test: add test that fetches weth interface from etherscan * Revert "fix: cast tests" This reverts commit c0ec3e968e0e0d3a3775c73b129a17515118c914. * fix: cast tests on macos --------- Co-authored-by: Matthias Seitz --- crates/cast/bin/cmd/interface.rs | 156 +++++++++++++++++++------------ crates/cast/bin/cmd/logs.rs | 20 ++-- crates/cast/tests/cli/main.rs | 34 +++++++ 3 files changed, 140 insertions(+), 70 deletions(-) diff --git a/crates/cast/bin/cmd/interface.rs b/crates/cast/bin/cmd/interface.rs index a402ea8af..caf9ac5dd 100644 --- a/crates/cast/bin/cmd/interface.rs +++ b/crates/cast/bin/cmd/interface.rs @@ -1,24 +1,31 @@ -use alloy_chains::Chain; -use alloy_json_abi::ContractObject; +use alloy_json_abi::{ContractObject, JsonAbi}; use alloy_primitives::Address; use clap::Parser; use eyre::{Context, Result}; use foundry_block_explorers::Client; use foundry_cli::opts::EtherscanOpts; -use foundry_common::fs; -use foundry_config::Config; +use foundry_common::{compile::ProjectCompiler, fs}; +use foundry_compilers::{info::ContractInfo, utils::canonicalize}; +use foundry_config::{find_project_root_path, load_config_with_root, Config}; use itertools::Itertools; -use std::path::{Path, PathBuf}; +use serde_json::Value; +use std::{ + path::{Path, PathBuf}, + str::FromStr, +}; /// CLI arguments for `cast interface`. #[derive(Clone, Debug, Parser)] pub struct InterfaceArgs { - /// The contract address, or the path to an ABI file. - /// - /// If an address is specified, then the ABI is fetched from Etherscan. - path_or_address: String, + /// The target contract, which can be one of: + /// - A file path to an ABI JSON file. + /// - A contract identifier in the form `:` or just ``. + /// - An Ethereum address, for which the ABI will be fetched from Etherscan. + contract: String, /// The name to use for the generated interface. + /// + /// Only relevant when retrieving the ABI from a file. #[arg(long, short)] name: Option, @@ -47,61 +54,32 @@ pub struct InterfaceArgs { impl InterfaceArgs { pub async fn run(self) -> Result<()> { - let Self { path_or_address, name, pragma, output: output_location, etherscan, json } = self; - let source = if Path::new(&path_or_address).exists() { - AbiPath::Local { path: path_or_address, name } + let Self { contract, name, pragma, output: output_location, etherscan, json } = self; + + // Determine if the target contract is an ABI file, a local contract or an Ethereum address. + let abis = if Path::new(&contract).is_file() && + fs::read_to_string(&contract) + .ok() + .and_then(|content| serde_json::from_str::(&content).ok()) + .is_some() + { + load_abi_from_file(&contract, name)? } else { - let config = Config::from(ðerscan); - let chain = config.chain.unwrap_or_default(); - let api_key = config.get_etherscan_api_key(Some(chain)).unwrap_or_default(); - AbiPath::Etherscan { - chain, - api_key, - address: path_or_address.parse().wrap_err("invalid path or address")?, + match Address::from_str(&contract) { + Ok(address) => fetch_abi_from_etherscan(address, ðerscan).await?, + Err(_) => load_abi_from_artifact(&contract)?, } }; - let items = match source { - AbiPath::Local { path, name } => { - let file = std::fs::read_to_string(&path).wrap_err("unable to read abi file")?; - let obj: ContractObject = serde_json::from_str(&file)?; - let abi = - obj.abi.ok_or_else(|| eyre::eyre!("could not find ABI in file {path}"))?; - let name = name.unwrap_or_else(|| "Interface".to_owned()); - vec![(abi, name)] - } - AbiPath::Etherscan { address, chain, api_key } => { - let client = Client::new(chain, api_key)?; - let source = client.contract_source_code(address).await?; - source - .items - .into_iter() - .map(|item| Ok((item.abi()?, item.contract_name))) - .collect::>>()? - } - }; + // Retrieve interfaces from the array of ABIs. + let interfaces = get_interfaces(abis)?; - let interfaces = items - .into_iter() - .map(|(contract_abi, name)| { - let source = match foundry_cli::utils::abi_to_solidity(&contract_abi, &name) { - Ok(generated_source) => generated_source, - Err(e) => { - warn!("Failed to format interface for {name}: {e}"); - contract_abi.to_sol(&name, None) - } - }; - Ok(InterfaceSource { - json_abi: serde_json::to_string_pretty(&contract_abi)?, - source, - }) - }) - .collect::>>()?; - - // put it all together + // Print result or write to file. let res = if json { + // Format as JSON. interfaces.iter().map(|iface| &iface.json_abi).format("\n").to_string() } else { + // Format as Solidity. format!( "// SPDX-License-Identifier: UNLICENSED\n\ pragma solidity {pragma};\n\n\ @@ -110,7 +88,6 @@ impl InterfaceArgs { ) }; - // print or write to file if let Some(loc) = output_location { if let Some(parent) = loc.parent() { fs::create_dir_all(parent)?; @@ -120,6 +97,7 @@ impl InterfaceArgs { } else { print!("{res}"); } + Ok(()) } } @@ -129,9 +107,63 @@ struct InterfaceSource { source: String, } -// Local is a path to the directory containing the ABI files -// In case of etherscan, ABI is fetched from the address on the chain -enum AbiPath { - Local { path: String, name: Option }, - Etherscan { address: Address, chain: Chain, api_key: String }, +/// Load the ABI from a file. +fn load_abi_from_file(path: &str, name: Option) -> Result> { + let file = std::fs::read_to_string(path).wrap_err("unable to read abi file")?; + let obj: ContractObject = serde_json::from_str(&file)?; + let abi = obj.abi.ok_or_else(|| eyre::eyre!("could not find ABI in file {path}"))?; + let name = name.unwrap_or_else(|| "Interface".to_owned()); + Ok(vec![(abi, name)]) +} + +/// Load the ABI from the artifact of a locally compiled contract. +fn load_abi_from_artifact(path_or_contract: &str) -> Result> { + let root = find_project_root_path(None)?; + let config = load_config_with_root(Some(root)); + let project = config.project()?; + let compiler = ProjectCompiler::new().quiet(true); + + let contract = ContractInfo::new(path_or_contract); + let target_path = if let Some(path) = &contract.path { + canonicalize(project.root().join(path))? + } else { + project.find_contract_path(&contract.name)? + }; + let mut output = compiler.files([target_path.clone()]).compile(&project)?; + + let artifact = output.remove(&target_path, &contract.name).ok_or_else(|| { + eyre::eyre!("Could not find artifact `{contract}` in the compiled artifacts") + })?; + let abi = artifact.abi.as_ref().ok_or_else(|| eyre::eyre!("Failed to fetch lossless ABI"))?; + Ok(vec![(abi.clone(), contract.name)]) +} + +/// Fetches the ABI of a contract from Etherscan. +async fn fetch_abi_from_etherscan( + address: Address, + etherscan: &EtherscanOpts, +) -> Result> { + let config = Config::from(etherscan); + let chain = config.chain.unwrap_or_default(); + let api_key = config.get_etherscan_api_key(Some(chain)).unwrap_or_default(); + let client = Client::new(chain, api_key)?; + let source = client.contract_source_code(address).await?; + source.items.into_iter().map(|item| Ok((item.abi()?, item.contract_name))).collect() +} + +/// Converts a vector of tuples containing the ABI and contract name into a vector of +/// `InterfaceSource` objects. +fn get_interfaces(abis: Vec<(JsonAbi, String)>) -> Result> { + abis.into_iter() + .map(|(contract_abi, name)| { + let source = match foundry_cli::utils::abi_to_solidity(&contract_abi, &name) { + Ok(generated_source) => generated_source, + Err(e) => { + warn!("Failed to format interface for {name}: {e}"); + contract_abi.to_sol(&name, None) + } + }; + Ok(InterfaceSource { json_abi: serde_json::to_string_pretty(&contract_abi)?, source }) + }) + .collect() } diff --git a/crates/cast/bin/cmd/logs.rs b/crates/cast/bin/cmd/logs.rs index c51bad8cc..51b95cffc 100644 --- a/crates/cast/bin/cmd/logs.rs +++ b/crates/cast/bin/cmd/logs.rs @@ -391,9 +391,10 @@ mod tests { ) .err() .unwrap() - .to_string(); + .to_string() + .to_lowercase(); - assert_eq!(err, "parser error:\n1234\n^\nInvalid string length"); + assert_eq!(err, "parser error:\n1234\n^\ninvalid string length"); } #[test] @@ -401,9 +402,10 @@ mod tests { let err = build_filter(None, None, None, Some("asdasdasd".to_string()), vec![]) .err() .unwrap() - .to_string(); + .to_string() + .to_lowercase(); - assert_eq!(err, "Odd number of digits"); + assert_eq!(err, "odd number of digits"); } #[test] @@ -411,9 +413,10 @@ mod tests { let err = build_filter(None, None, None, Some(ADDRESS.to_string()), vec![]) .err() .unwrap() - .to_string(); + .to_string() + .to_lowercase(); - assert_eq!(err, "Invalid string length"); + assert_eq!(err, "invalid string length"); } #[test] @@ -427,8 +430,9 @@ mod tests { ) .err() .unwrap() - .to_string(); + .to_string() + .to_lowercase(); - assert_eq!(err, "Invalid string length"); + assert_eq!(err, "invalid string length"); } } diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 8349f6d3e..91d04f031 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -910,6 +910,40 @@ interface Interface { assert_eq!(output.trim(), s); }); +// tests that fetches WETH interface from etherscan +// +casttest!(fetch_weth_interface_from_etherscan, |_prj, cmd| { + let weth_address = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"; + let api_key = "ZUB97R31KSYX7NYVW6224Q6EYY6U56H591"; + cmd.args(["interface", "--etherscan-api-key", api_key, weth_address]); + let output = cmd.stdout_lossy(); + + let weth_interface = r#"// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.4; + +interface WETH9 { + event Approval(address indexed src, address indexed guy, uint256 wad); + event Deposit(address indexed dst, uint256 wad); + event Transfer(address indexed src, address indexed dst, uint256 wad); + event Withdrawal(address indexed src, uint256 wad); + + fallback() external payable; + + function allowance(address, address) external view returns (uint256); + function approve(address guy, uint256 wad) external returns (bool); + function balanceOf(address) external view returns (uint256); + function decimals() external view returns (uint8); + function deposit() external payable; + function name() external view returns (string memory); + function symbol() external view returns (string memory); + function totalSupply() external view returns (uint256); + function transfer(address dst, uint256 wad) external returns (bool); + function transferFrom(address src, address dst, uint256 wad) external returns (bool); + function withdraw(uint256 wad) external; +}"#; + assert_eq!(output.trim(), weth_interface); +}); + const ENS_NAME: &str = "emo.eth"; const ENS_NAMEHASH: B256 = b256!("0a21aaf2f6414aa664deb341d1114351fdb023cad07bf53b28e57c26db681910"); From 78f86c325fddfc084c5739a946a365fe798b26c6 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 6 Aug 2024 20:25:58 +0300 Subject: [PATCH 053/184] chore(coverage): cleanup creation / push item (#8616) --- crates/evm/coverage/src/analysis.rs | 147 ++++++++-------------------- 1 file changed, 43 insertions(+), 104 deletions(-) diff --git a/crates/evm/coverage/src/analysis.rs b/crates/evm/coverage/src/analysis.rs index 15e2bab30..c25bd79de 100644 --- a/crates/evm/coverage/src/analysis.rs +++ b/crates/evm/coverage/src/analysis.rs @@ -59,11 +59,7 @@ impl<'a> ContractVisitor<'a> { match &node.body { Some(body) => { - self.push_item(CoverageItem { - kind: CoverageItemKind::Function { name }, - loc: self.source_location_for(&node.src), - hits: 0, - }); + self.push_item_kind(CoverageItemKind::Function { name }, &node.src); self.visit_block(body) } _ => Ok(()), @@ -76,11 +72,7 @@ impl<'a> ContractVisitor<'a> { match &node.body { Some(body) => { - self.push_item(CoverageItem { - kind: CoverageItemKind::Function { name }, - loc: self.source_location_for(&node.src), - hits: 0, - }); + self.push_item_kind(CoverageItemKind::Function { name }, &node.src); self.visit_block(body) } _ => Ok(()), @@ -119,22 +111,14 @@ impl<'a> ContractVisitor<'a> { NodeType::YulContinue | NodeType::YulLeave | NodeType::YulVariableDeclaration => { - self.push_item(CoverageItem { - kind: CoverageItemKind::Statement, - loc: self.source_location_for(&node.src), - hits: 0, - }); + self.push_item_kind(CoverageItemKind::Statement, &node.src); Ok(()) } // Skip placeholder statements as they are never referenced in source maps. NodeType::PlaceholderStatement => Ok(()), // Return with eventual subcall NodeType::Return => { - self.push_item(CoverageItem { - kind: CoverageItemKind::Statement, - loc: self.source_location_for(&node.src), - hits: 0, - }); + self.push_item_kind(CoverageItemKind::Statement, &node.src); if let Some(expr) = node.attribute("expression") { self.visit_expression(&expr)?; } @@ -142,11 +126,7 @@ impl<'a> ContractVisitor<'a> { } // Variable declaration NodeType::VariableDeclarationStatement => { - self.push_item(CoverageItem { - kind: CoverageItemKind::Statement, - loc: self.source_location_for(&node.src), - hits: 0, - }); + self.push_item_kind(CoverageItemKind::Statement, &node.src); if let Some(expr) = node.attribute("initialValue") { self.visit_expression(&expr)?; } @@ -225,31 +205,29 @@ impl<'a> ContractVisitor<'a> { // If this source range is not processed like this, it is virtually // impossible to correctly map instructions back to branches that // include more complex logic like conditional logic. - self.push_item(CoverageItem { - kind: CoverageItemKind::Branch { branch_id, path_id: 0 }, - loc: self.source_location_for(&ast::LowFidelitySourceLocation { + self.push_item_kind( + CoverageItemKind::Branch { branch_id, path_id: 0 }, + &ast::LowFidelitySourceLocation { start: node.src.start, length: true_body.src.length.map(|length| { true_body.src.start - node.src.start + length }), index: node.src.index, - }), - hits: 0, - }); + }, + ); // Add the coverage item for branch 1 (false body). // The relevant source range for the false branch is the `else` // statement itself and the false body of the else statement. - self.push_item(CoverageItem { - kind: CoverageItemKind::Branch { branch_id, path_id: 1 }, - loc: self.source_location_for(&ast::LowFidelitySourceLocation { + self.push_item_kind( + CoverageItemKind::Branch { branch_id, path_id: 1 }, + &ast::LowFidelitySourceLocation { start: node.src.start, length: false_body.src.length.map(|length| { false_body.src.start - true_body.src.start + length }), index: node.src.index, - }), - hits: 0, - }); + }, + ); // Process the true body. self.visit_block_or_statement(&true_body)?; @@ -261,11 +239,10 @@ impl<'a> ContractVisitor<'a> { // Add single branch coverage only if it contains statements. if has_statements(&true_body) { // Add the coverage item for branch 0 (true body). - self.push_item(CoverageItem { - kind: CoverageItemKind::SinglePathBranch { branch_id }, - loc: self.source_location_for(&true_body.src), - hits: 0, - }); + self.push_item_kind( + CoverageItemKind::SinglePathBranch { branch_id }, + &true_body.src, + ); // Process the true body. self.visit_block_or_statement(&true_body)?; } @@ -293,11 +270,7 @@ impl<'a> ContractVisitor<'a> { // branch ID as we do self.branch_id += 1; - self.push_item(CoverageItem { - kind: CoverageItemKind::Branch { branch_id, path_id: 0 }, - loc: self.source_location_for(&node.src), - hits: 0, - }); + self.push_item_kind(CoverageItemKind::Branch { branch_id, path_id: 0 }, &node.src); self.visit_block(body)?; Ok(()) @@ -317,21 +290,13 @@ impl<'a> ContractVisitor<'a> { .ok_or_else(|| eyre::eyre!("try statement had no clause"))? { // Add coverage for clause statement. - self.push_item(CoverageItem { - kind: CoverageItemKind::Statement, - loc: self.source_location_for(&clause.src), - hits: 0, - }); + self.push_item_kind(CoverageItemKind::Statement, &clause.src); self.visit_statement(&clause)?; // Add coverage for clause body only if it is not empty. if let Some(block) = clause.attribute::("block") { if has_statements(&block) { - self.push_item(CoverageItem { - kind: CoverageItemKind::Statement, - loc: self.source_location_for(&block.src), - hits: 0, - }); + self.push_item_kind(CoverageItemKind::Statement, &block.src); self.visit_block(&block)?; } } @@ -345,19 +310,11 @@ impl<'a> ContractVisitor<'a> { .attribute::>("cases") .ok_or_else(|| eyre::eyre!("yul switch had no case"))? { - self.push_item(CoverageItem { - kind: CoverageItemKind::Statement, - loc: self.source_location_for(&case.src), - hits: 0, - }); + self.push_item_kind(CoverageItemKind::Statement, &case.src); self.visit_statement(&case)?; if let Some(body) = case.body { - self.push_item(CoverageItem { - kind: CoverageItemKind::Statement, - loc: self.source_location_for(&body.src), - hits: 0, - }); + self.push_item_kind(CoverageItemKind::Statement, &body.src); self.visit_block(&body)? } } @@ -375,11 +332,7 @@ impl<'a> ContractVisitor<'a> { } if let Some(body) = &node.body { - self.push_item(CoverageItem { - kind: CoverageItemKind::Statement, - loc: self.source_location_for(&body.src), - hits: 0, - }); + self.push_item_kind(CoverageItemKind::Statement, &body.src); self.visit_block(body)? } Ok(()) @@ -398,11 +351,7 @@ impl<'a> ContractVisitor<'a> { NodeType::UnaryOperation | NodeType::Conditional | NodeType::YulFunctionCall => { - self.push_item(CoverageItem { - kind: CoverageItemKind::Statement, - loc: self.source_location_for(&node.src), - hits: 0, - }); + self.push_item_kind(CoverageItemKind::Statement, &node.src); Ok(()) } NodeType::FunctionCall => { @@ -410,11 +359,7 @@ impl<'a> ContractVisitor<'a> { // and `structConstructorCall`). let kind: Option = node.attribute("kind"); if let Some("functionCall") = kind.as_deref() { - self.push_item(CoverageItem { - kind: CoverageItemKind::Statement, - loc: self.source_location_for(&node.src), - hits: 0, - }); + self.push_item_kind(CoverageItemKind::Statement, &node.src); let expr: Option = node.attribute("expression"); if let Some(NodeType::Identifier) = expr.as_ref().map(|expr| &expr.node_type) { @@ -423,16 +368,14 @@ impl<'a> ContractVisitor<'a> { if let Some("require" | "assert") = name.as_deref() { let branch_id = self.branch_id; self.branch_id += 1; - self.push_item(CoverageItem { - kind: CoverageItemKind::Branch { branch_id, path_id: 0 }, - loc: self.source_location_for(&node.src), - hits: 0, - }); - self.push_item(CoverageItem { - kind: CoverageItemKind::Branch { branch_id, path_id: 1 }, - loc: self.source_location_for(&node.src), - hits: 0, - }); + self.push_item_kind( + CoverageItemKind::Branch { branch_id, path_id: 0 }, + &node.src, + ); + self.push_item_kind( + CoverageItemKind::Branch { branch_id, path_id: 1 }, + &node.src, + ); } } } @@ -440,11 +383,7 @@ impl<'a> ContractVisitor<'a> { Ok(()) } NodeType::BinaryOperation => { - self.push_item(CoverageItem { - kind: CoverageItemKind::Statement, - loc: self.source_location_for(&node.src), - hits: 0, - }); + self.push_item_kind(CoverageItemKind::Statement, &node.src); // visit left and right expressions // There could possibly a function call in the left or right expression @@ -500,24 +439,24 @@ impl<'a> ContractVisitor<'a> { } } - /// Pushes a coverage item to the internal collection, and might push a line item as well. - fn push_item(&mut self, item: CoverageItem) { - let source_location = &item.loc; - + /// Creates a coverage item for a given kind and source location. Pushes item to the internal + /// collection (plus additional coverage line if item is a statement). + fn push_item_kind(&mut self, kind: CoverageItemKind, src: &ast::LowFidelitySourceLocation) { + let item = CoverageItem { kind, loc: self.source_location_for(src), hits: 0 }; // Push a line item if we haven't already if matches!( item.kind, CoverageItemKind::Statement | CoverageItemKind::Branch { .. } | CoverageItemKind::SinglePathBranch { .. } - ) && self.last_line < source_location.line + ) && self.last_line < item.loc.line { self.items.push(CoverageItem { kind: CoverageItemKind::Line, - loc: source_location.clone(), + loc: item.loc.clone(), hits: 0, }); - self.last_line = source_location.line; + self.last_line = item.loc.line; } self.items.push(item); From 4351742481c98adaa9ca3e8642e619aa986b3cee Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 6 Aug 2024 22:01:58 +0300 Subject: [PATCH 054/184] fix(coverage): use first opcode for if block with statements (#8615) * fix(coverage): use first opcode for if block anchor * Better naming --- crates/evm/coverage/src/analysis.rs | 45 +++++++------ crates/evm/coverage/src/anchors.rs | 41 +++++++----- crates/evm/coverage/src/lib.rs | 4 +- crates/forge/src/coverage.rs | 2 +- crates/forge/tests/cli/coverage.rs | 97 +++++++++++++++++++++++++++-- 5 files changed, 147 insertions(+), 42 deletions(-) diff --git a/crates/evm/coverage/src/analysis.rs b/crates/evm/coverage/src/analysis.rs index c25bd79de..cb557eeb6 100644 --- a/crates/evm/coverage/src/analysis.rs +++ b/crates/evm/coverage/src/analysis.rs @@ -197,29 +197,25 @@ impl<'a> ContractVisitor<'a> { // Add branch coverage items only if one of true/branch bodies contains // statements. if has_statements(&true_body) || has_statements(&false_body) { - // Add the coverage item for branch 0 (true body). - // The relevant source range for the true branch is the `if(...)` - // statement itself and the true body of the if statement. - // - // The false body of the statement is processed as its own thing. - // If this source range is not processed like this, it is virtually - // impossible to correctly map instructions back to branches that - // include more complex logic like conditional logic. + // The branch instruction is mapped to the first opcode within the true + // body source range. self.push_item_kind( - CoverageItemKind::Branch { branch_id, path_id: 0 }, - &ast::LowFidelitySourceLocation { - start: node.src.start, - length: true_body.src.length.map(|length| { - true_body.src.start - node.src.start + length - }), - index: node.src.index, + CoverageItemKind::Branch { + branch_id, + path_id: 0, + is_first_opcode: true, }, + &true_body.src, ); // Add the coverage item for branch 1 (false body). // The relevant source range for the false branch is the `else` // statement itself and the false body of the else statement. self.push_item_kind( - CoverageItemKind::Branch { branch_id, path_id: 1 }, + CoverageItemKind::Branch { + branch_id, + path_id: 1, + is_first_opcode: false, + }, &ast::LowFidelitySourceLocation { start: node.src.start, length: false_body.src.length.map(|length| { @@ -270,7 +266,10 @@ impl<'a> ContractVisitor<'a> { // branch ID as we do self.branch_id += 1; - self.push_item_kind(CoverageItemKind::Branch { branch_id, path_id: 0 }, &node.src); + self.push_item_kind( + CoverageItemKind::Branch { branch_id, path_id: 0, is_first_opcode: false }, + &node.src, + ); self.visit_block(body)?; Ok(()) @@ -369,11 +368,19 @@ impl<'a> ContractVisitor<'a> { let branch_id = self.branch_id; self.branch_id += 1; self.push_item_kind( - CoverageItemKind::Branch { branch_id, path_id: 0 }, + CoverageItemKind::Branch { + branch_id, + path_id: 0, + is_first_opcode: false, + }, &node.src, ); self.push_item_kind( - CoverageItemKind::Branch { branch_id, path_id: 1 }, + CoverageItemKind::Branch { + branch_id, + path_id: 1, + is_first_opcode: false, + }, &node.src, ); } diff --git a/crates/evm/coverage/src/anchors.rs b/crates/evm/coverage/src/anchors.rs index ad4ad744d..9e82ec1cb 100644 --- a/crates/evm/coverage/src/anchors.rs +++ b/crates/evm/coverage/src/anchors.rs @@ -24,27 +24,34 @@ pub fn find_anchors( } let item = &items[item_id]; + let find_anchor_by_first_opcode = |item: &CoverageItem| match find_anchor_simple( + source_map, ic_pc_map, item_id, &item.loc, + ) { + Ok(anchor) => Some(anchor), + Err(e) => { + warn!("Could not find anchor for item {item}: {e}"); + None + } + }; match item.kind { - CoverageItemKind::Branch { path_id, .. } => { - match find_anchor_branch(bytecode, source_map, item_id, &item.loc) { - Ok(anchors) => match path_id { - 0 => Some(anchors.0), - 1 => Some(anchors.1), - _ => panic!("Too many paths for branch"), - }, - Err(e) => { - warn!("Could not find anchor for item {item}: {e}"); - None + CoverageItemKind::Branch { path_id, is_first_opcode, .. } => { + if is_first_opcode { + find_anchor_by_first_opcode(item) + } else { + match find_anchor_branch(bytecode, source_map, item_id, &item.loc) { + Ok(anchors) => match path_id { + 0 => Some(anchors.0), + 1 => Some(anchors.1), + _ => panic!("Too many paths for branch"), + }, + Err(e) => { + warn!("Could not find anchor for item {item}: {e}"); + None + } } } } - _ => match find_anchor_simple(source_map, ic_pc_map, item_id, &item.loc) { - Ok(anchor) => Some(anchor), - Err(e) => { - warn!("Could not find anchor for item {item}: {e}"); - None - } - }, + _ => find_anchor_by_first_opcode(item), } }) .collect() diff --git a/crates/evm/coverage/src/lib.rs b/crates/evm/coverage/src/lib.rs index 24390aa12..b9f6913a6 100644 --- a/crates/evm/coverage/src/lib.rs +++ b/crates/evm/coverage/src/lib.rs @@ -294,6 +294,8 @@ pub enum CoverageItemKind { /// /// The first path has ID 0, the next ID 1, and so on. path_id: usize, + /// If true, then the branch anchor is the first opcode within the branch source range. + is_first_opcode: bool, }, /// A branch in the code. SinglePathBranch { @@ -326,7 +328,7 @@ impl Display for CoverageItem { CoverageItemKind::Statement => { write!(f, "Statement")?; } - CoverageItemKind::Branch { branch_id, path_id } => { + CoverageItemKind::Branch { branch_id, path_id, .. } => { write!(f, "Branch (branch: {branch_id}, path: {path_id})")?; } CoverageItemKind::SinglePathBranch { branch_id } => { diff --git a/crates/forge/src/coverage.rs b/crates/forge/src/coverage.rs index cc83e1329..df0d06d03 100644 --- a/crates/forge/src/coverage.rs +++ b/crates/forge/src/coverage.rs @@ -109,7 +109,7 @@ impl<'a> CoverageReporter for LcovReporter<'a> { CoverageItemKind::Line => { writeln!(self.destination, "DA:{line},{hits}")?; } - CoverageItemKind::Branch { branch_id, path_id } => { + CoverageItemKind::Branch { branch_id, path_id, .. } => { writeln!( self.destination, "BRDA:{line},{branch_id},{path_id},{}", diff --git a/crates/forge/tests/cli/coverage.rs b/crates/forge/tests/cli/coverage.rs index a2ee110ee..078e06d95 100644 --- a/crates/forge/tests/cli/coverage.rs +++ b/crates/forge/tests/cli/coverage.rs @@ -1094,10 +1094,10 @@ contract AContractTest is DSTest { .assert_success() .stdout_eq(str![[r#" ... -| File | % Lines | % Statements | % Branches | % Funcs | -|-------------------|---------------|--------------|--------------|---------------| -| src/AContract.sol | 100.00% (4/4) | 50.00% (2/4) | 50.00% (2/4) | 100.00% (1/1) | -| Total | 100.00% (4/4) | 50.00% (2/4) | 50.00% (2/4) | 100.00% (1/1) | +| File | % Lines | % Statements | % Branches | % Funcs | +|-------------------|--------------|--------------|--------------|---------------| +| src/AContract.sol | 50.00% (2/4) | 50.00% (2/4) | 50.00% (2/4) | 100.00% (1/1) | +| Total | 50.00% (2/4) | 50.00% (2/4) | 50.00% (2/4) | 100.00% (1/1) | "#]]); @@ -1113,3 +1113,92 @@ contract AContractTest is DSTest { "#]], ); }); + +// https://github.com/foundry-rs/foundry/issues/8604 +forgetest!(test_branch_with_calldata_reads, |prj, cmd| { + prj.insert_ds_test(); + prj.add_source( + "AContract.sol", + r#" +contract AContract { + event IsTrue(bool isTrue); + event IsFalse(bool isFalse); + + function execute(bool[] calldata isTrue) external { + for (uint256 i = 0; i < isTrue.length; i++) { + if (isTrue[i]) { + emit IsTrue(isTrue[i]); + } else { + emit IsFalse(!isTrue[i]); + } + } + } +} + "#, + ) + .unwrap(); + + prj.add_source( + "AContractTest.sol", + r#" +import "./test.sol"; +import {AContract} from "./AContract.sol"; + +contract AContractTest is DSTest { + function testTrueCoverage() external { + AContract a = new AContract(); + bool[] memory isTrue = new bool[](1); + isTrue[0] = true; + a.execute(isTrue); + } + + function testFalseCoverage() external { + AContract a = new AContract(); + bool[] memory isFalse = new bool[](1); + isFalse[0] = false; + a.execute(isFalse); + } +} + "#, + ) + .unwrap(); + + // Assert 50% coverage for true branches. + cmd.arg("coverage") + .args(["--mt".to_string(), "testTrueCoverage".to_string()]) + .assert_success() + .stdout_eq(str![[r#" +... +| File | % Lines | % Statements | % Branches | % Funcs | +|-------------------|--------------|--------------|--------------|---------------| +| src/AContract.sol | 75.00% (3/4) | 80.00% (4/5) | 50.00% (1/2) | 100.00% (1/1) | +| Total | 75.00% (3/4) | 80.00% (4/5) | 50.00% (1/2) | 100.00% (1/1) | + +"#]]); + + // Assert 50% coverage for false branches. + cmd.forge_fuse() + .arg("coverage") + .args(["--mt".to_string(), "testFalseCoverage".to_string()]) + .assert_success() + .stdout_eq(str![[r#" +... +| File | % Lines | % Statements | % Branches | % Funcs | +|-------------------|--------------|--------------|--------------|---------------| +| src/AContract.sol | 50.00% (2/4) | 80.00% (4/5) | 50.00% (1/2) | 100.00% (1/1) | +| Total | 50.00% (2/4) | 80.00% (4/5) | 50.00% (1/2) | 100.00% (1/1) | + +"#]]); + + // Assert 100% coverage (true/false branches properly covered). + cmd.forge_fuse().arg("coverage").args(["--summary".to_string()]).assert_success().stdout_eq( + str![[r#" +... +| File | % Lines | % Statements | % Branches | % Funcs | +|-------------------|---------------|---------------|---------------|---------------| +| src/AContract.sol | 100.00% (4/4) | 100.00% (5/5) | 100.00% (2/2) | 100.00% (1/1) | +| Total | 100.00% (4/4) | 100.00% (5/5) | 100.00% (2/2) | 100.00% (1/1) | + +"#]], + ); +}); From 75342b1b7f79b63914150ca6e884ee68c7c3d199 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 7 Aug 2024 17:35:56 +0300 Subject: [PATCH 055/184] chore(coverage): remove SinglePathBranch kind, use Branch kind (#8619) chore(coverage): remove SinglePathBranch kind, use Branch with first opcode --- crates/evm/coverage/src/analysis.rs | 14 +++++++------- crates/evm/coverage/src/lib.rs | 10 +--------- crates/forge/src/coverage.rs | 7 ------- 3 files changed, 8 insertions(+), 23 deletions(-) diff --git a/crates/evm/coverage/src/analysis.rs b/crates/evm/coverage/src/analysis.rs index cb557eeb6..daf676c2c 100644 --- a/crates/evm/coverage/src/analysis.rs +++ b/crates/evm/coverage/src/analysis.rs @@ -236,7 +236,11 @@ impl<'a> ContractVisitor<'a> { if has_statements(&true_body) { // Add the coverage item for branch 0 (true body). self.push_item_kind( - CoverageItemKind::SinglePathBranch { branch_id }, + CoverageItemKind::Branch { + branch_id, + path_id: 0, + is_first_opcode: true, + }, &true_body.src, ); // Process the true body. @@ -451,12 +455,8 @@ impl<'a> ContractVisitor<'a> { fn push_item_kind(&mut self, kind: CoverageItemKind, src: &ast::LowFidelitySourceLocation) { let item = CoverageItem { kind, loc: self.source_location_for(src), hits: 0 }; // Push a line item if we haven't already - if matches!( - item.kind, - CoverageItemKind::Statement | - CoverageItemKind::Branch { .. } | - CoverageItemKind::SinglePathBranch { .. } - ) && self.last_line < item.loc.line + if matches!(item.kind, CoverageItemKind::Statement | CoverageItemKind::Branch { .. }) && + self.last_line < item.loc.line { self.items.push(CoverageItem { kind: CoverageItemKind::Line, diff --git a/crates/evm/coverage/src/lib.rs b/crates/evm/coverage/src/lib.rs index b9f6913a6..4481813ac 100644 --- a/crates/evm/coverage/src/lib.rs +++ b/crates/evm/coverage/src/lib.rs @@ -297,11 +297,6 @@ pub enum CoverageItemKind { /// If true, then the branch anchor is the first opcode within the branch source range. is_first_opcode: bool, }, - /// A branch in the code. - SinglePathBranch { - /// The ID that identifies the branch. - branch_id: usize, - }, /// A function in the code. Function { /// The name of the function. @@ -331,9 +326,6 @@ impl Display for CoverageItem { CoverageItemKind::Branch { branch_id, path_id, .. } => { write!(f, "Branch (branch: {branch_id}, path: {path_id})")?; } - CoverageItemKind::SinglePathBranch { branch_id } => { - write!(f, "Branch (branch: {branch_id}, path: 0)")?; - } CoverageItemKind::Function { name } => { write!(f, r#"Function "{name}""#)?; } @@ -418,7 +410,7 @@ impl AddAssign<&CoverageItem> for CoverageSummary { self.statement_hits += 1; } } - CoverageItemKind::Branch { .. } | CoverageItemKind::SinglePathBranch { .. } => { + CoverageItemKind::Branch { .. } => { self.branch_count += 1; if item.hits > 0 { self.branch_hits += 1; diff --git a/crates/forge/src/coverage.rs b/crates/forge/src/coverage.rs index df0d06d03..135636d3c 100644 --- a/crates/forge/src/coverage.rs +++ b/crates/forge/src/coverage.rs @@ -116,13 +116,6 @@ impl<'a> CoverageReporter for LcovReporter<'a> { if hits == 0 { "-".to_string() } else { hits.to_string() } )?; } - CoverageItemKind::SinglePathBranch { branch_id } => { - writeln!( - self.destination, - "BRDA:{line},{branch_id},0,{}", - if hits == 0 { "-".to_string() } else { hits.to_string() } - )?; - } // Statements are not in the LCOV format. // We don't add them in order to avoid doubling line hits. _ => {} From 8390f2d6f6b030caf00f337ad28f73f7100967ce Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 8 Aug 2024 01:52:42 +0200 Subject: [PATCH 056/184] perf: configure provider poll interval (#8624) --- crates/common/src/provider/mod.rs | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/crates/common/src/provider/mod.rs b/crates/common/src/provider/mod.rs index 7bb943eac..49cadd95c 100644 --- a/crates/common/src/provider/mod.rs +++ b/crates/common/src/provider/mod.rs @@ -27,6 +27,13 @@ use std::{ }; use url::ParseError; +/// The assumed block time for unknown chains. +/// We assume that these are chains have a faster block time. +const DEFAULT_UNKNOWN_CHAIN_BLOCK_TIME: Duration = Duration::from_secs(3); + +/// The factor to scale the block time by to get the poll interval. +const POLL_INTERVAL_BLOCK_TIME_SCALE_FACTOR: f32 = 0.6; + /// Helper type alias for a retry provider pub type RetryProvider = RootProvider, N>; @@ -229,7 +236,7 @@ impl ProviderBuilder { pub fn build(self) -> Result { let Self { url, - chain: _, + chain, max_retry, initial_backoff, timeout, @@ -250,6 +257,15 @@ impl ProviderBuilder { .build(); let client = ClientBuilder::default().layer(retry_layer).transport(transport, is_local); + if !is_local { + client.set_poll_interval( + chain + .average_blocktime_hint() + .unwrap_or(DEFAULT_UNKNOWN_CHAIN_BLOCK_TIME) + .mul_f32(POLL_INTERVAL_BLOCK_TIME_SCALE_FACTOR), + ); + } + let provider = AlloyProviderBuilder::<_, _, AnyNetwork>::default() .on_provider(RootProvider::new(client)); @@ -260,7 +276,7 @@ impl ProviderBuilder { pub fn build_with_wallet(self, wallet: EthereumWallet) -> Result { let Self { url, - chain: _, + chain, max_retry, initial_backoff, timeout, @@ -282,6 +298,15 @@ impl ProviderBuilder { let client = ClientBuilder::default().layer(retry_layer).transport(transport, is_local); + if !is_local { + client.set_poll_interval( + chain + .average_blocktime_hint() + .unwrap_or(DEFAULT_UNKNOWN_CHAIN_BLOCK_TIME) + .mul_f32(POLL_INTERVAL_BLOCK_TIME_SCALE_FACTOR), + ); + } + let provider = AlloyProviderBuilder::<_, _, AnyNetwork>::default() .with_recommended_fillers() .wallet(wallet) From 7f0f5b4c1aa75dc4fd2eb15aca9757491d885902 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Wed, 7 Aug 2024 17:00:15 -0700 Subject: [PATCH 057/184] feat(cast): call json flag (#8618) * add call json flag * update json param to option * address comments --- crates/cast/bin/cmd/call.rs | 7 ++++++- crates/cast/src/lib.rs | 6 +++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index 65ec11d6e..4cec6f0f0 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -62,6 +62,10 @@ pub struct CallArgs { #[arg(long, short)] block: Option, + /// Print the decoded output as JSON. + #[arg(long, short, help_heading = "Display options")] + json: bool, + #[command(subcommand)] command: Option, @@ -112,6 +116,7 @@ impl CallArgs { decode_internal, labels, data, + json, } = self; if let Some(data) = data { @@ -190,7 +195,7 @@ impl CallArgs { return Ok(()); } - println!("{}", Cast::new(provider).call(&tx, func.as_ref(), block).await?); + println!("{}", Cast::new(provider).call(&tx, func.as_ref(), block, json).await?); Ok(()) } diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 46610d630..93e3c4a0f 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -118,7 +118,7 @@ where /// let tx = TransactionRequest::default().to(to).input(bytes.into()); /// let tx = WithOtherFields::new(tx); /// let cast = Cast::new(alloy_provider); - /// let data = cast.call(&tx, None, None).await?; + /// let data = cast.call(&tx, None, None, false).await?; /// println!("{}", data); /// # Ok(()) /// # } @@ -128,6 +128,7 @@ where req: &WithOtherFields, func: Option<&Function>, block: Option, + json: bool, ) -> Result { let res = self.provider.call(req).block(block.unwrap_or_default()).await?; @@ -168,6 +169,9 @@ where // handle case when return type is not specified Ok(if decoded.is_empty() { format!("{res}\n") + } else if json { + let tokens = decoded.iter().map(format_token_raw).collect::>(); + serde_json::to_string_pretty(&tokens).unwrap() } else { // seth compatible user-friendly return type conversions decoded.iter().map(format_token).collect::>().join("\n") From 5a3176966b6703c0d363c853b2caf7419e4d82ba Mon Sep 17 00:00:00 2001 From: francesco-gaglione <94604837+francesco-gaglione@users.noreply.github.com> Date: Thu, 8 Aug 2024 02:20:25 +0200 Subject: [PATCH 058/184] Forge doc --watch implementation (#8592) * First doc --watch implementation * refactored doc watch implementation * Removed static paths * fix fmt * Update crates/forge/bin/cmd/watch.rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Update crates/forge/bin/cmd/watch.rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Update crates/forge/bin/cmd/doc/mod.rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Update crates/forge/bin/cmd/watch.rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * removed unnecessary clones * chore: nits --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/forge/bin/cmd/doc/mod.rs | 30 ++++++++++++++++++++++-------- crates/forge/bin/cmd/watch.rs | 11 ++++++++++- crates/forge/bin/main.rs | 9 ++++++++- 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/crates/forge/bin/cmd/doc/mod.rs b/crates/forge/bin/cmd/doc/mod.rs index e561dcc5c..ad4375bfb 100644 --- a/crates/forge/bin/cmd/doc/mod.rs +++ b/crates/forge/bin/cmd/doc/mod.rs @@ -1,3 +1,4 @@ +use super::watch::WatchArgs; use clap::{Parser, ValueHint}; use eyre::Result; use forge_doc::{ @@ -5,7 +6,7 @@ use forge_doc::{ }; use foundry_cli::opts::GH_REPO_PREFIX_REGEX; use foundry_common::compile::ProjectCompiler; -use foundry_config::{find_project_root_path, load_config_with_root}; +use foundry_config::{find_project_root_path, load_config_with_root, Config}; use std::{path::PathBuf, process::Command}; mod server; @@ -47,6 +48,9 @@ pub struct DocArgs { #[arg(long, requires = "serve")] hostname: Option, + #[command(flatten)] + pub watch: WatchArgs, + /// Port for serving documentation. #[arg(long, short, requires = "serve")] port: Option, @@ -62,14 +66,14 @@ pub struct DocArgs { } impl DocArgs { - pub fn run(self) -> Result<()> { - let root = self.root.clone().unwrap_or(find_project_root_path(None)?); - let config = load_config_with_root(Some(root.clone())); + pub async fn run(self) -> Result<()> { + let config = self.config()?; + let root = &config.root.0; let project = config.project()?; let compiler = ProjectCompiler::new().quiet(true); let _output = compiler.compile(&project)?; - let mut doc_config = config.doc.clone(); + let mut doc_config = config.doc; if let Some(out) = self.out { doc_config.out = out; } @@ -91,7 +95,7 @@ impl DocArgs { } } - let commit = foundry_cli::utils::Git::new(&root).commit_hash(false, "HEAD").ok(); + let commit = foundry_cli::utils::Git::new(root).commit_hash(false, "HEAD").ok(); let mut builder = DocBuilder::new( root.clone(), @@ -113,14 +117,14 @@ impl DocArgs { // If deployment docgen is enabled, add the [Deployments] preprocessor if let Some(deployments) = self.deployments { - builder = builder.with_preprocessor(Deployments { root, deployments }); + builder = builder.with_preprocessor(Deployments { root: root.clone(), deployments }); } builder.build()?; if self.serve { Server::new(doc_config.out) - .with_hostname(self.hostname.unwrap_or("localhost".to_owned())) + .with_hostname(self.hostname.unwrap_or_else(|| "localhost".into())) .with_port(self.port.unwrap_or(3000)) .open(self.open) .serve()?; @@ -128,4 +132,14 @@ impl DocArgs { Ok(()) } + + /// Returns whether watch mode is enabled + pub fn is_watch(&self) -> bool { + self.watch.watch.is_some() + } + + pub fn config(&self) -> Result { + let root = self.root.clone().unwrap_or(find_project_root_path(None)?); + Ok(load_config_with_root(Some(root))) + } } diff --git a/crates/forge/bin/cmd/watch.rs b/crates/forge/bin/cmd/watch.rs index 0dfb73468..09eb8c666 100644 --- a/crates/forge/bin/cmd/watch.rs +++ b/crates/forge/bin/cmd/watch.rs @@ -1,4 +1,4 @@ -use super::{build::BuildArgs, snapshot::SnapshotArgs, test::TestArgs}; +use super::{build::BuildArgs, doc::DocArgs, snapshot::SnapshotArgs, test::TestArgs}; use clap::Parser; use eyre::Result; use foundry_cli::utils::{self, FoundryPathExt}; @@ -311,6 +311,15 @@ pub async fn watch_test(args: TestArgs) -> Result<()> { Ok(()) } +/// Executes a [`Watchexec`] that listens for changes in the project's sources directory +pub async fn watch_doc(args: DocArgs) -> Result<()> { + let src_path = args.config()?.src; + let config = args.watch.watchexec_config(|| [src_path])?; + run(config).await?; + + Ok(()) +} + /// Converts a list of arguments to a `watchexec::Command`. /// /// The first index in `args` is the path to the executable. diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs index 4484be629..dc4a18dd6 100644 --- a/crates/forge/bin/main.rs +++ b/crates/forge/bin/main.rs @@ -105,7 +105,14 @@ fn main() -> Result<()> { } Ok(()) } - ForgeSubcommand::Doc(cmd) => cmd.run(), + ForgeSubcommand::Doc(cmd) => { + if cmd.is_watch() { + utils::block_on(watch::watch_doc(cmd)) + } else { + utils::block_on(cmd.run())?; + Ok(()) + } + } ForgeSubcommand::Selectors { command } => utils::block_on(command.run()), ForgeSubcommand::Generate(cmd) => match cmd.sub { GenerateSubcommands::Test(cmd) => cmd.run(), From 960878a412c839c4d199d68c094156383ee18acf Mon Sep 17 00:00:00 2001 From: zjb0807 Date: Thu, 8 Aug 2024 19:42:18 +0800 Subject: [PATCH 059/184] fix: add Mandala + Karura + Acala as exceptions for gas calculation during deployment (#8625) * add Mandala & Karura & Acala * update alloy-chains * fix --- Cargo.lock | 4 ++-- crates/cli/src/utils/cmd.rs | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3d1e00b94..57bb59ae5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,9 +68,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-chains" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3312b2a48f29abe7c3ea7c7fbc1f8cc6ea09b85d74b6232e940df35f2f3826fd" +checksum = "01828af9e8fb20a85b1caf6570d84ee93595d6cbd9e8ea96bda3a4e27a55a4fa" dependencies = [ "num_enum", "serde", diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index 6abd11fce..1e4af95df 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -163,13 +163,18 @@ pub fn has_different_gas_calc(chain_id: u64) -> bool { if let Some(chain) = Chain::from(chain_id).named() { return matches!( chain, - NamedChain::Arbitrum | + NamedChain::Acala | + NamedChain::AcalaMandalaTestnet | + NamedChain::AcalaTestnet | + NamedChain::Arbitrum | NamedChain::ArbitrumGoerli | NamedChain::ArbitrumSepolia | NamedChain::ArbitrumTestnet | + NamedChain::Karura | + NamedChain::KaruraTestnet | NamedChain::Mantle | - NamedChain::MantleTestnet | NamedChain::MantleSepolia | + NamedChain::MantleTestnet | NamedChain::Moonbase | NamedChain::Moonbeam | NamedChain::MoonbeamDev | From 4cdebf77b2757d50768f188e886a55feaf4316fd Mon Sep 17 00:00:00 2001 From: Miguel Palhas Date: Thu, 8 Aug 2024 12:44:00 +0100 Subject: [PATCH 060/184] `eth_getBlockReceipts` should accept block hashes (#8623) * eth_getBlockReceipts using BlockID * fix block receipt * Update crates/anvil/src/eth/backend/mem/mod.rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Update crates/anvil/src/eth/api.rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * code review --------- Co-authored-by: joaocosta9 Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --- crates/anvil/core/src/eth/mod.rs | 2 +- crates/anvil/src/eth/api.rs | 5 +---- crates/anvil/src/eth/backend/mem/mod.rs | 7 +++++-- crates/anvil/tests/it/fork.rs | 12 +++++++++--- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index 185e723f3..7c86baad3 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -221,7 +221,7 @@ pub enum EthRequest { EthGetTransactionReceipt(B256), #[cfg_attr(feature = "serde", serde(rename = "eth_getBlockReceipts", with = "sequence"))] - EthGetBlockReceipts(BlockNumber), + EthGetBlockReceipts(BlockId), #[cfg_attr(feature = "serde", serde(rename = "eth_getUncleByBlockHashAndIndex"))] EthGetUncleByBlockHashAndIndex(B256, Index), diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 4f853cd0c..822bc57f1 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -1217,10 +1217,7 @@ impl EthApi { /// Returns block receipts by block number. /// /// Handler for ETH RPC call: `eth_getBlockReceipts` - pub async fn block_receipts( - &self, - number: BlockNumber, - ) -> Result>> { + pub async fn block_receipts(&self, number: BlockId) -> Result>> { node_info!("eth_getBlockReceipts"); self.backend.block_receipts(number).await } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index c3285e25f..da0a22883 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -2236,14 +2236,17 @@ impl Backend { /// Returns the blocks receipts for the given number pub async fn block_receipts( &self, - number: BlockNumber, + number: BlockId, ) -> Result>, BlockchainError> { if let Some(receipts) = self.mined_block_receipts(number) { return Ok(Some(receipts)); } if let Some(fork) = self.get_fork() { - let number = self.convert_block_number(Some(number)); + let number = match self.ensure_block_number(Some(number)).await { + Err(_) => return Ok(None), + Ok(n) => n, + }; if fork.predates_fork_inclusive(number) { let receipts = fork.block_receipts(number).await?; diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 8a4542b48..884bc0605 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -5,7 +5,7 @@ use crate::{ utils::{http_provider, http_provider_with_signer}, }; use alloy_network::{EthereumWallet, ReceiptResponse, TransactionBuilder}; -use alloy_primitives::{address, bytes, Address, Bytes, TxHash, TxKind, U256}; +use alloy_primitives::{address, b256, bytes, Address, Bytes, TxHash, TxKind, U256}; use alloy_provider::Provider; use alloy_rpc_types::{ anvil::Forking, @@ -1015,12 +1015,18 @@ async fn test_block_receipts() { let (api, _) = spawn(fork_config()).await; // Receipts from the forked block (14608400) - let receipts = api.block_receipts(BlockNumberOrTag::Number(BLOCK_NUMBER)).await.unwrap(); + let receipts = api.block_receipts(BlockNumberOrTag::Number(BLOCK_NUMBER).into()).await.unwrap(); assert!(receipts.is_some()); // Receipts from a block in the future (14608401) - let receipts = api.block_receipts(BlockNumberOrTag::Number(BLOCK_NUMBER + 1)).await.unwrap(); + let receipts = + api.block_receipts(BlockNumberOrTag::Number(BLOCK_NUMBER + 1).into()).await.unwrap(); assert!(receipts.is_none()); + + // Receipts from a block hash (14608400) + let hash = b256!("4c1c76f89cfe4eb503b09a0993346dd82865cac9d76034efc37d878c66453f0a"); + let receipts = api.block_receipts(BlockId::Hash(hash.into())).await.unwrap(); + assert!(receipts.is_some()); } #[tokio::test(flavor = "multi_thread")] From 5ee33c85bb76a08e94622f6922802376d30032bc Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 8 Aug 2024 15:43:36 +0300 Subject: [PATCH 061/184] fix(fmt): fix disable line for first / last block lines (#8602) * fix(fmt): fix disable line for first / last block lines * Fix win spacing * Reuse visit_block in visit_fn * Fix win failure --- crates/fmt/src/formatter.rs | 171 ++++++++++++------ crates/fmt/testdata/InlineDisable/fmt.sol | 3 +- crates/fmt/testdata/Repros/fmt.sol | 68 +++++++ crates/fmt/testdata/Repros/original.sol | 66 +++++++ .../common/InvariantPreserveState.t.sol | 5 +- 5 files changed, 256 insertions(+), 57 deletions(-) diff --git a/crates/fmt/src/formatter.rs b/crates/fmt/src/formatter.rs index 5a3b7f124..e7625dc11 100644 --- a/crates/fmt/src/formatter.rs +++ b/crates/fmt/src/formatter.rs @@ -246,6 +246,17 @@ impl<'a, W: Write> Formatter<'a, W> { Ok(()) } + /// Write unformatted src and comments for given location. + fn write_raw_src(&mut self, loc: Loc) -> Result<()> { + let disabled_stmts_src = String::from_utf8(self.source.as_bytes()[loc.range()].to_vec()) + .map_err(FormatterError::custom)?; + self.write_raw(disabled_stmts_src.trim_end())?; + self.write_whitespace_separator(true)?; + // Remove comments as they're already included in disabled src. + let _ = self.comments.remove_all_comments_before(loc.end()); + Ok(()) + } + /// Returns number of blank lines in source between two byte indexes fn blank_lines(&self, start: usize, end: usize) -> usize { // because of sorting import statements, start can be greater than end @@ -1192,22 +1203,114 @@ impl<'a, W: Write> Formatter<'a, W> { } } - write_chunk!(self, "{{")?; + // Determine if any of start / end of the block is disabled and block lines boundaries. + let is_start_disabled = self.inline_config.is_disabled(loc.with_end(loc.start())); + let is_end_disabled = self.inline_config.is_disabled(loc.with_start(loc.end())); + let end_of_first_line = self.find_next_line(loc.start()).unwrap_or_default(); + let end_of_last_line = self.find_next_line(loc.end()).unwrap_or_default(); - if let Some(statement) = statements.first() { - self.write_whitespace_separator(true)?; - self.write_postfix_comments_before(CodeLocation::loc(statement).start())?; + // Write first line of the block: + // - as it is until the end of line, if format disabled + // - start block if line formatted + if is_start_disabled { + self.write_raw_src(loc.with_end(end_of_first_line))?; + } else { + write_chunk!(self, "{{")?; } - self.indented(1, |fmt| { - fmt.write_lined_visitable(loc, statements.iter_mut(), |_, _| false)?; - Ok(()) - })?; + // Write comments and close block if no statement. + if statements.is_empty() { + self.indented(1, |fmt| { + fmt.write_prefix_comments_before(loc.end())?; + fmt.write_postfix_comments_before(loc.end())?; + Ok(()) + })?; + + write_chunk!(self, "}}")?; + return Ok(true) + } + + // Determine writable statements by excluding statements from disabled start / end lines. + // We check the position of last statement from first line (if disabled) and position of + // first statement from last line (if disabled) and slice accordingly. + let writable_statments = match ( + statements.iter().rposition(|stmt| { + is_start_disabled && + self.find_next_line(stmt.loc().end()).unwrap_or_default() == + end_of_first_line + }), + statements.iter().position(|stmt| { + is_end_disabled && + self.find_next_line(stmt.loc().end()).unwrap_or_default() == end_of_last_line + }), + ) { + // We have statements on both disabled start / end lines. + (Some(start), Some(end)) => { + if start == end || start + 1 == end { + None + } else { + Some(&mut statements[start + 1..end]) + } + } + // We have statements only on disabled start line. + (Some(start), None) => { + if start + 1 == statements.len() { + None + } else { + Some(&mut statements[start + 1..]) + } + } + // We have statements only on disabled end line. + (None, Some(end)) => { + if end == 0 { + None + } else { + Some(&mut statements[..end]) + } + } + // No statements on disabled start / end line. + (None, None) => Some(statements), + }; - if !statements.is_empty() { + // Write statements that are not on any disabled first / last block line. + let mut statements_loc = loc; + if let Some(writable_statements) = writable_statments { + if let Some(first_statement) = writable_statements.first() { + statements_loc = statements_loc.with_start(first_statement.loc().start()); + self.write_whitespace_separator(true)?; + self.write_postfix_comments_before(statements_loc.start())?; + } + // If last line is disabled then statements location ends where last block line starts. + if is_end_disabled { + if let Some(last_statement) = writable_statements.last() { + statements_loc = statements_loc.with_end( + self.find_next_line(last_statement.loc().end()).unwrap_or_default(), + ); + } + } + self.indented(1, |fmt| { + fmt.write_lined_visitable( + statements_loc, + writable_statements.iter_mut(), + |_, _| false, + )?; + Ok(()) + })?; self.write_whitespace_separator(true)?; } - write_chunk!(self, loc.end(), "}}")?; + + // Write last line of the block: + // - as it is from where statements location ends until the end of last line, if format + // disabled + // - close block if line formatted + if is_end_disabled { + self.write_raw_src(loc.with_start(statements_loc.end()).with_end(end_of_last_line))?; + } else { + if end_of_first_line != end_of_last_line { + self.write_whitespace_separator(true)?; + } + write_chunk!(self, loc.end(), "}}")?; + } Ok(false) } @@ -3045,51 +3148,15 @@ impl<'a, W: Write> Visitor for Formatter<'a, W> { if fmt.inline_config.is_disabled(body_loc.with_end(body_loc.start())) { match body { Statement::Block { statements, .. } if !statements.is_empty() => { - // TODO: move this logic in `visit_body` fn and reuse it. - // Retain enabled statements. - statements.retain(|stmt| { - !fmt.inline_config.is_disabled( - body_loc.with_end(CodeLocation::loc(stmt).start()), - ) - }); - - // Disabled statement stops where first enabled statement starts or - // where body ends (if no statement enabled). - let disabled_stmts_end = match statements.first() { - Some(stmt) => CodeLocation::loc(stmt).start(), - None => body_loc.end(), - }; - - // Write non formatted statements. This includes the curly bracket - // block start, comments and any other disabled statement. - let disabled_stmts_src = String::from_utf8( - fmt.source.as_bytes() - [body_loc.with_end(disabled_stmts_end).range()] - .to_vec(), - ) - .map_err(FormatterError::custom)?; fmt.write_whitespace_separator(false)?; - fmt.write_raw(disabled_stmts_src.trim_end())?; - // Remove all comments as they're already included in disabled src. - let _ = fmt.comments.remove_all_comments_before(disabled_stmts_end); - - // Write enabled statements. - fmt.indented(1, |fmt| { - fmt.write_lined_visitable( - body_loc.with_start(disabled_stmts_end), - statements.iter_mut(), - |_, _| false, - )?; - Ok(()) - })?; - - // Write curly bracket block end. - fmt.write_whitespace_separator(true)?; - write_chunk!(fmt, body_loc.end(), "}}")?; - + fmt.visit_block(body_loc, statements, false, false)?; return Ok(()) } - _ => {} + _ => { + // Attrs should be written on same line if first line is disabled + // and there's no statement. + attrs_multiline = false + } } } diff --git a/crates/fmt/testdata/InlineDisable/fmt.sol b/crates/fmt/testdata/InlineDisable/fmt.sol index 9211929e7..d7adea60b 100644 --- a/crates/fmt/testdata/InlineDisable/fmt.sol +++ b/crates/fmt/testdata/InlineDisable/fmt.sol @@ -90,8 +90,7 @@ function testFunc(uint256 num, bytes32 data , address receiver) function testAttrs(uint256 num, bytes32 data, address receiver) // forgefmt: disable-next-line - public payable attr1 Cool( "hello" ) -{} + public payable attr1 Cool( "hello" ) {} // forgefmt: disable-next-line function testParams(uint256 num, bytes32 data , address receiver) diff --git a/crates/fmt/testdata/Repros/fmt.sol b/crates/fmt/testdata/Repros/fmt.sol index b2e232c19..d4daa364c 100644 --- a/crates/fmt/testdata/Repros/fmt.sol +++ b/crates/fmt/testdata/Repros/fmt.sol @@ -38,3 +38,71 @@ contract Format { ) {} } } + +// https://github.com/foundry-rs/foundry/issues/3830 +contract TestContract { + function test(uint256 a) public { + if (a > 1) { + a = 2; + } // forgefmt: disable-line + } + + function test1() public { + assembly{ sstore( 1, 1) /* inline comment*/ // forgefmt: disable-line + sstore(2, 2) + } + } + + function test2() public { + assembly{ sstore( 1, 1) // forgefmt: disable-line + sstore(2, 2) + sstore(3, 3)// forgefmt: disable-line + sstore(4, 4) + } + } + + function test3() public { + // forgefmt: disable-next-line + assembly{ sstore( 1, 1) + sstore(2, 2) + sstore(3, 3)// forgefmt: disable-line + sstore(4, 4) + }// forgefmt: disable-line + } + + function test4() public { + // forgefmt: disable-next-line + assembly{ + sstore(1, 1) + sstore(2, 2) + sstore(3, 3)// forgefmt: disable-line + sstore(4, 4) + }// forgefmt: disable-line + if (condition) execute(); // comment7 + } + + function test5() public { + assembly { sstore(0, 0) }// forgefmt: disable-line + } + + function test6() returns (bool) { // forgefmt: disable-line + if ( true ) { // forgefmt: disable-line + } + return true ; } // forgefmt: disable-line + + function test7() returns (bool) { // forgefmt: disable-line + if (true) { // forgefmt: disable-line + uint256 a = 1; // forgefmt: disable-line + } + return true; + } + + function test8() returns (bool) { // forgefmt: disable-line + if ( true ) { // forgefmt: disable-line + uint256 a = 1; + } else { + uint256 b = 1; // forgefmt: disable-line + } + return true; + } +} diff --git a/crates/fmt/testdata/Repros/original.sol b/crates/fmt/testdata/Repros/original.sol index 23e96ac63..33c4d216f 100644 --- a/crates/fmt/testdata/Repros/original.sol +++ b/crates/fmt/testdata/Repros/original.sol @@ -38,3 +38,69 @@ contract Format { ) {} } } + +// https://github.com/foundry-rs/foundry/issues/3830 +contract TestContract { + function test(uint256 a) public { + if (a > 1) { + a = 2; + } // forgefmt: disable-line + } + + function test1() public { + assembly { sstore( 1, 1) /* inline comment*/ // forgefmt: disable-line + sstore(2, 2) + } + } + + function test2() public { + assembly { sstore( 1, 1) // forgefmt: disable-line + sstore(2, 2) + sstore(3, 3) // forgefmt: disable-line + sstore(4, 4) + } + } + + function test3() public { + // forgefmt: disable-next-line + assembly{ sstore( 1, 1) + sstore(2, 2) + sstore(3, 3) // forgefmt: disable-line + sstore(4, 4) + }// forgefmt: disable-line + } + + function test4() public { + // forgefmt: disable-next-line + assembly { + sstore( 1, 1) + sstore(2, 2) + sstore(3, 3) // forgefmt: disable-line + sstore(4, 4) + }// forgefmt: disable-line + if (condition) execute(); // comment7 + } + + function test5() public { + assembly { sstore(0, 0) }// forgefmt: disable-line + } + + function test6() returns (bool) { // forgefmt: disable-line + if ( true ) { // forgefmt: disable-line + } + return true ; } // forgefmt: disable-line + + function test7() returns (bool) { // forgefmt: disable-line + if (true) { // forgefmt: disable-line + uint256 a = 1; // forgefmt: disable-line + } + return true ; } + + function test8() returns (bool) { // forgefmt: disable-line + if ( true ) { // forgefmt: disable-line + uint256 a = 1; + } else { + uint256 b = 1; // forgefmt: disable-line + } + return true ; } +} diff --git a/testdata/default/fuzz/invariant/common/InvariantPreserveState.t.sol b/testdata/default/fuzz/invariant/common/InvariantPreserveState.t.sol index 546980136..b91cda739 100644 --- a/testdata/default/fuzz/invariant/common/InvariantPreserveState.t.sol +++ b/testdata/default/fuzz/invariant/common/InvariantPreserveState.t.sol @@ -15,9 +15,8 @@ contract Handler is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); function thisFunctionReverts() external { - if (block.number < 10) {} else { - revert(); - } + if (block.number < 10) {} + else revert(); } function advanceTime(uint256 blocks) external { From b574cdfff47942f154c5b9782dd2231d8cf9da30 Mon Sep 17 00:00:00 2001 From: cui <523516579@qq.com> Date: Thu, 8 Aug 2024 20:45:25 +0800 Subject: [PATCH 062/184] fix(cast run): print custom error when revert (#8620) fix(cast run): print custom revert Co-authored-by: Matthias Seitz --- crates/cheatcodes/src/test/expect.rs | 2 +- crates/evm/core/src/decode.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs index 4c5bfe928..b68ae700a 100644 --- a/crates/cheatcodes/src/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -585,7 +585,7 @@ pub(crate) fn handle_expect_revert( Ok(success_return()) } else { let stringify = |data: &[u8]| { - if let Ok(s) = String::abi_decode(data, false) { + if let Ok(s) = String::abi_decode(data, true) { return s; } if data.is_ascii() { diff --git a/crates/evm/core/src/decode.rs b/crates/evm/core/src/decode.rs index 0c52ea3c7..29f448bce 100644 --- a/crates/evm/core/src/decode.rs +++ b/crates/evm/core/src/decode.rs @@ -167,7 +167,7 @@ impl RevertDecoder { } // ABI-encoded `string`. - if let Ok(s) = String::abi_decode(err, false) { + if let Ok(s) = String::abi_decode(err, true) { return Some(s); } From 1f33c6f834a9edec8d12c69830686a3dc8d5e6cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Vincent?= <28714795+leovct@users.noreply.github.com> Date: Thu, 8 Aug 2024 14:50:22 +0200 Subject: [PATCH 063/184] feat(cheatcodes): add deterministic random value generation with seed (#8622) * feat(cheatcodes): add ability to set seed for `vm.randomUint()` * chore: move `vm.randomAddress` test to its own contract * feat(cheatcodes): add ability to set seed for `vm.randomAddress()` * feat: use global seed instead of introducing new cheatcodes * chore: clean up * chore: clean up tests * feat: add `fuzz.seed` as inline parameter in tests * chore: trim 0x prefix * chore: nit * test: update random tests * fix: inline parsing on fuzz seed * test: set seed and update random tests * chore: remove inline config for seed * chore: clean up * chore: clean up tests * test: remove deterministic tests from testdata * test: implement forgetest to test that forge test with a seed produces deterministic random values * test: fix tests * chore: clean up * test: remove seed * fix: clippy and forge-fmt * chore: clean up * chore: rename test contract * fix: lint * chore: move rng to state instead of creating a new one when calling `vm.random*` cheats * chore: nit * test: update tests * fix: clippy * chore: nit * chore: clean up * Update crates/cheatcodes/src/inspector.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * test: only check outputs are the same or different * chore: clean up * chore: nits --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/cheatcodes/src/config.rs | 6 +- crates/cheatcodes/src/fs.rs | 2 +- crates/cheatcodes/src/inspector.rs | 12 ++++ crates/cheatcodes/src/utils.rs | 14 ++-- crates/forge/tests/cli/test_cmd.rs | 78 ++++++++++++++++++++- testdata/default/cheats/RandomAddress.t.sol | 13 ++++ testdata/default/cheats/RandomUint.t.sol | 4 -- 7 files changed, 114 insertions(+), 15 deletions(-) create mode 100644 testdata/default/cheats/RandomAddress.t.sol diff --git a/crates/cheatcodes/src/config.rs b/crates/cheatcodes/src/config.rs index 9a9e59cce..715e26dc5 100644 --- a/crates/cheatcodes/src/config.rs +++ b/crates/cheatcodes/src/config.rs @@ -1,6 +1,6 @@ use super::Result; use crate::{script::ScriptWallets, Vm::Rpc}; -use alloy_primitives::Address; +use alloy_primitives::{Address, U256}; use foundry_common::{fs::normalize_path, ContractsByArtifact}; use foundry_compilers::{utils::canonicalize, ProjectPathsConfig}; use foundry_config::{ @@ -54,6 +54,8 @@ pub struct CheatsConfig { pub running_version: Option, /// Whether to enable legacy (non-reverting) assertions. pub assertions_revert: bool, + /// Optional seed for the RNG algorithm. + pub seed: Option, } impl CheatsConfig { @@ -93,6 +95,7 @@ impl CheatsConfig { available_artifacts, running_version, assertions_revert: config.assertions_revert, + seed: config.fuzz.seed, } } @@ -221,6 +224,7 @@ impl Default for CheatsConfig { available_artifacts: Default::default(), running_version: Default::default(), assertions_revert: true, + seed: None, } } } diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 5467cfcf6..7f07674d0 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -618,7 +618,7 @@ mod tests { root: PathBuf::from(&env!("CARGO_MANIFEST_DIR")), ..Default::default() }; - Cheatcodes { config: Arc::new(config), ..Default::default() } + Cheatcodes::new(Arc::new(config)) } #[test] diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 0d377a382..f1af752a4 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -29,6 +29,7 @@ use foundry_evm_core::{ InspectorExt, }; use itertools::Itertools; +use rand::{rngs::StdRng, Rng, SeedableRng}; use revm::{ interpreter::{ opcode, CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, EOFCreateInputs, @@ -315,6 +316,9 @@ pub struct Cheatcodes { /// Breakpoints supplied by the `breakpoint` cheatcode. /// `char -> (address, pc)` pub breakpoints: Breakpoints, + + /// Optional RNG algorithm. + rng: Option, } // This is not derived because calling this in `fn new` with `..Default::default()` creates a second @@ -356,6 +360,7 @@ impl Cheatcodes { mapping_slots: Default::default(), pc: Default::default(), breakpoints: Default::default(), + rng: Default::default(), } } @@ -926,6 +931,13 @@ impl Cheatcodes { None } + + pub fn rng(&mut self) -> &mut impl Rng { + self.rng.get_or_insert_with(|| match self.config.seed { + Some(seed) => StdRng::from_seed(seed.to_be_bytes::<32>()), + None => StdRng::from_entropy(), + }) + } } impl Inspector for Cheatcodes { diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index 6edd700ec..5fa55ba8f 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -55,22 +55,21 @@ impl Cheatcode for ensNamehashCall { } impl Cheatcode for randomUint_0Call { - fn apply(&self, _state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; - // Use thread_rng to get a random number - let mut rng = rand::thread_rng(); + let rng = state.rng(); let random_number: U256 = rng.gen(); Ok(random_number.abi_encode()) } } impl Cheatcode for randomUint_1Call { - fn apply(&self, _state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { min, max } = *self; ensure!(min <= max, "min must be less than or equal to max"); // Generate random between range min..=max - let mut rng = rand::thread_rng(); let exclusive_modulo = max - min; + let rng = state.rng(); let mut random_number = rng.gen::(); if exclusive_modulo != U256::MAX { let inclusive_modulo = exclusive_modulo + U256::from(1); @@ -82,9 +81,10 @@ impl Cheatcode for randomUint_1Call { } impl Cheatcode for randomAddressCall { - fn apply(&self, _state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; - let addr = Address::random(); + let rng = state.rng(); + let addr = Address::random_with(rng); Ok(addr.abi_encode()) } } diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 1f2e22a15..97e4c97cf 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -6,6 +6,7 @@ use foundry_test_utils::{ rpc, str, util::{OutputExt, OTHER_SOLC_VERSION, SOLC_VERSION}, }; +use similar_asserts::assert_eq; use std::{path::PathBuf, str::FromStr}; // tests that test filters are handled correctly @@ -597,7 +598,7 @@ contract GasWaster { contract GasLimitTest is Test { function test() public { vm.createSelectFork(""); - + GasWaster waster = new GasWaster(); waster.waste(); } @@ -613,7 +614,7 @@ contract GasLimitTest is Test { forgetest!(test_match_path, |prj, cmd| { prj.add_source( "dummy", - r" + r" contract Dummy { function testDummy() public {} } @@ -1048,3 +1049,76 @@ Traces: "# ]]); }); + +// tests that `forge test` with a seed produces deterministic random values for uint and addresses. +forgetest_init!(deterministic_randomness_with_seed, |prj, cmd| { + prj.wipe_contracts(); + prj.add_test( + "DeterministicRandomnessTest.t.sol", + r#" +import {Test, console} from "forge-std/Test.sol"; + +contract DeterministicRandomnessTest is Test { + + function testDeterministicRandomUint() public { + console.log(vm.randomUint()); + console.log(vm.randomUint()); + console.log(vm.randomUint()); + } + + function testDeterministicRandomUintRange() public { + uint256 min = 0; + uint256 max = 1000000000; + console.log(vm.randomUint(min, max)); + console.log(vm.randomUint(min, max)); + console.log(vm.randomUint(min, max)); + } + + function testDeterministicRandomAddress() public { + console.log(vm.randomAddress()); + console.log(vm.randomAddress()); + console.log(vm.randomAddress()); + } +} +"#, + ) + .unwrap(); + + // Extracts the test result section from the DeterministicRandomnessTest contract output. + fn extract_test_result(out: &str) -> &str { + let start = out + .find("for test/DeterministicRandomnessTest.t.sol:DeterministicRandomnessTest") + .unwrap(); + let end = out.find("Suite result: ok.").unwrap(); + &out[start..end] + } + + // Run the test twice with the same seed and verify the outputs are the same. + let seed1 = "0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"; + cmd.args(["test", "--fuzz-seed", seed1, "-vv"]).assert_success(); + let out1 = cmd.stdout_lossy(); + let res1 = extract_test_result(&out1); + + cmd.forge_fuse(); + cmd.args(["test", "--fuzz-seed", seed1, "-vv"]).assert_success(); + let out2 = cmd.stdout_lossy(); + let res2 = extract_test_result(&out2); + + assert_eq!(res1, res2); + + // Run the test with another seed and verify the output differs. + let seed2 = "0xb1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"; + cmd.forge_fuse(); + cmd.args(["test", "--fuzz-seed", seed2, "-vv"]).assert_success(); + let out3 = cmd.stdout_lossy(); + let res3 = extract_test_result(&out3); + assert_ne!(res3, res1); + + // Run the test without a seed and verify the outputs differs once again. + cmd.forge_fuse(); + cmd.args(["test", "-vv"]).assert_success(); + let out4 = cmd.stdout_lossy(); + let res4 = extract_test_result(&out4); + assert_ne!(res4, res1); + assert_ne!(res4, res3); +}); diff --git a/testdata/default/cheats/RandomAddress.t.sol b/testdata/default/cheats/RandomAddress.t.sol new file mode 100644 index 000000000..74c5e2040 --- /dev/null +++ b/testdata/default/cheats/RandomAddress.t.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract RandomAddress is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function testRandomAddress() public { + vm.randomAddress(); + } +} diff --git a/testdata/default/cheats/RandomUint.t.sol b/testdata/default/cheats/RandomUint.t.sol index e679f9bfd..68a65dc0f 100644 --- a/testdata/default/cheats/RandomUint.t.sol +++ b/testdata/default/cheats/RandomUint.t.sol @@ -26,8 +26,4 @@ contract RandomUint is DSTest { assertTrue(rand >= min, "rand >= min"); assertTrue(rand <= max, "rand <= max"); } - - function testRandomAddress() public { - vm.randomAddress(); - } } From 14e50ed89293806c02b1cb5bce3c91c5e9e40b1b Mon Sep 17 00:00:00 2001 From: francesco-gaglione <94604837+francesco-gaglione@users.noreply.github.com> Date: Thu, 8 Aug 2024 14:53:53 +0200 Subject: [PATCH 064/184] extended JsonResult structure (#8565) * extended JsonResult structure * removed breakpoints in JsonResult * chore: nits --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/script/src/execute.rs | 16 +++++++--------- crates/script/src/lib.rs | 14 +++++++++----- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index 1eff7e0de..caa3fb2c2 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -1,10 +1,9 @@ +use super::{runner::ScriptRunner, JsonResult, NestedValue, ScriptResult}; use crate::{ build::{CompiledState, LinkedBuildData}, simulate::PreSimulationState, ScriptArgs, ScriptConfig, }; - -use super::{runner::ScriptRunner, JsonResult, NestedValue, ScriptResult}; use alloy_dyn_abi::FunctionExt; use alloy_json_abi::{Function, InternalType, JsonAbi}; use alloy_primitives::{Address, Bytes}; @@ -382,14 +381,13 @@ impl PreSimulationState { pub fn show_json(&self) -> Result<()> { let result = &self.execution_result; - let console_logs = decode_console_logs(&result.logs); - let output = JsonResult { - logs: console_logs, - gas_used: result.gas_used, - returns: self.execution_artifacts.returns.clone(), + let json_result = JsonResult { + logs: decode_console_logs(&result.logs), + returns: &self.execution_artifacts.returns, + result, }; - let j = serde_json::to_string(&output)?; - shell::println(j)?; + let json = serde_json::to_string(&json_result)?; + shell::println(json)?; if !self.execution_result.success { return Err(eyre::eyre!( diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 6ca1d4610..e2f86ce3b 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -457,16 +457,19 @@ impl Provider for ScriptArgs { } } -#[derive(Default)] +#[derive(Default, Serialize)] pub struct ScriptResult { pub success: bool, + #[serde(rename = "raw_logs")] pub logs: Vec, pub traces: Traces, pub gas_used: u64, pub labeled_addresses: HashMap, + #[serde(skip)] pub transactions: Option, pub returned: Bytes, pub address: Option
, + #[serde(skip)] pub breakpoints: Breakpoints, } @@ -490,11 +493,12 @@ impl ScriptResult { } } -#[derive(Serialize, Deserialize)] -struct JsonResult { +#[derive(Serialize)] +struct JsonResult<'a> { logs: Vec, - gas_used: u64, - returns: HashMap, + returns: &'a HashMap, + #[serde(flatten)] + result: &'a ScriptResult, } #[derive(Clone, Serialize, Deserialize)] From 56cd9a94434a5de63ef0fd655b0d6d856f1e45c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ca=C3=ADque=20Porfirio?= <56317416+caiquejjx@users.noreply.github.com> Date: Thu, 8 Aug 2024 11:09:52 -0300 Subject: [PATCH 065/184] feat: add `auth` field to `RPCEndpointConfig` (#8570) * add auth parsing in RPC config * add comment explaining auth param * add missing field in test * fix formatting * fix formatting * fix failing test * fix failing test * undo wrong formatting * remove reminiscent ; * auth option as enum to be able to resolve env vars * add test for auth resolving and new field to resolved endpoint --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Matthias Seitz --- crates/config/src/endpoints.rs | 96 +++++++++++++++++++++++++++++++--- crates/config/src/lib.rs | 72 +++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 6 deletions(-) diff --git a/crates/config/src/endpoints.rs b/crates/config/src/endpoints.rs index eabc5acb1..78cda4f73 100644 --- a/crates/config/src/endpoints.rs +++ b/crates/config/src/endpoints.rs @@ -41,7 +41,20 @@ impl RpcEndpoints { /// Returns all (alias -> url) pairs pub fn resolved(self) -> ResolvedRpcEndpoints { ResolvedRpcEndpoints { - endpoints: self.endpoints.into_iter().map(|(name, e)| (name, e.resolve())).collect(), + endpoints: self + .endpoints + .clone() + .into_iter() + .map(|(name, e)| (name, e.resolve())) + .collect(), + auths: self + .endpoints + .into_iter() + .map(|(name, e)| match e.auth { + Some(auth) => (name, auth.resolve().map(Some)), + None => (name, Ok(None)), + }) + .collect(), } } } @@ -210,6 +223,58 @@ impl From for RpcEndpointConfig { } } +/// The auth token to be used for RPC endpoints +/// It works in the same way as the `RpcEndpoint` type, where it can be a raw string or a reference +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum RpcAuth { + Raw(String), + Env(String), +} + +impl RpcAuth { + /// Returns the auth token this type holds + /// + /// # Error + /// + /// Returns an error if the type holds a reference to an env var and the env var is not set + pub fn resolve(self) -> Result { + match self { + Self::Raw(raw_auth) => Ok(raw_auth), + Self::Env(var) => interpolate(&var), + } + } +} + +impl fmt::Display for RpcAuth { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Raw(url) => url.fmt(f), + Self::Env(var) => var.fmt(f), + } + } +} + +impl Serialize for RpcAuth { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for RpcAuth { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let val = String::deserialize(deserializer)?; + let auth = if RE_PLACEHOLDER.is_match(&val) { Self::Env(val) } else { Self::Raw(val) }; + + Ok(auth) + } +} + /// Rpc endpoint configuration variant #[derive(Debug, Clone, PartialEq, Eq)] pub struct RpcEndpointConfig { @@ -226,6 +291,9 @@ pub struct RpcEndpointConfig { /// /// See also pub compute_units_per_second: Option, + + /// Token to be used as authentication + pub auth: Option, } impl RpcEndpointConfig { @@ -237,7 +305,7 @@ impl RpcEndpointConfig { impl fmt::Display for RpcEndpointConfig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { endpoint, retries, retry_backoff, compute_units_per_second } = self; + let Self { endpoint, retries, retry_backoff, compute_units_per_second, auth } = self; write!(f, "{endpoint}")?; @@ -253,6 +321,10 @@ impl fmt::Display for RpcEndpointConfig { write!(f, ", compute_units_per_second={compute_units_per_second}")?; } + if let Some(auth) = auth { + write!(f, ", auth={auth}")?; + } + Ok(()) } } @@ -274,6 +346,7 @@ impl Serialize for RpcEndpointConfig { map.serialize_entry("retries", &self.retries)?; map.serialize_entry("retry_backoff", &self.retry_backoff)?; map.serialize_entry("compute_units_per_second", &self.compute_units_per_second)?; + map.serialize_entry("auth", &self.auth)?; map.end() } } @@ -299,12 +372,18 @@ impl<'de> Deserialize<'de> for RpcEndpointConfig { retries: Option, retry_backoff: Option, compute_units_per_second: Option, + auth: Option, } - let RpcEndpointConfigInner { endpoint, retries, retry_backoff, compute_units_per_second } = - serde_json::from_value(value).map_err(serde::de::Error::custom)?; + let RpcEndpointConfigInner { + endpoint, + retries, + retry_backoff, + compute_units_per_second, + auth, + } = serde_json::from_value(value).map_err(serde::de::Error::custom)?; - Ok(Self { endpoint, retries, retry_backoff, compute_units_per_second }) + Ok(Self { endpoint, retries, retry_backoff, compute_units_per_second, auth }) } } @@ -321,6 +400,7 @@ impl Default for RpcEndpointConfig { retries: None, retry_backoff: None, compute_units_per_second: None, + auth: None, } } } @@ -331,6 +411,7 @@ pub struct ResolvedRpcEndpoints { /// contains all named endpoints and their URL or an error if we failed to resolve the env var /// alias endpoints: BTreeMap>, + auths: BTreeMap, UnresolvedEnvVarError>>, } impl ResolvedRpcEndpoints { @@ -364,7 +445,8 @@ mod tests { "endpoint": "http://localhost:8545", "retries": 5, "retry_backoff": 250, - "compute_units_per_second": 100 + "compute_units_per_second": 100, + "auth": "Bearer 123" }"#; let config: RpcEndpointConfig = serde_json::from_str(s).unwrap(); assert_eq!( @@ -374,6 +456,7 @@ mod tests { retries: Some(5), retry_backoff: Some(250), compute_units_per_second: Some(100), + auth: Some(RpcAuth::Raw("Bearer 123".to_string())), } ); @@ -386,6 +469,7 @@ mod tests { retries: None, retry_backoff: None, compute_units_per_second: None, + auth: None, } ); } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index d9c762581..7033cc31a 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -2799,6 +2799,7 @@ mod tests { endpoints::{RpcEndpointConfig, RpcEndpointType}, etherscan::ResolvedEtherscanConfigs, }; + use endpoints::RpcAuth; use figment::error::Kind::InvalidType; use foundry_compilers::artifacts::{ vyper::VyperOptimizationMode, ModelCheckerEngine, YulDetails, @@ -3449,6 +3450,7 @@ mod tests { retries: Some(3), retry_backoff: Some(1000), compute_units_per_second: Some(1000), + auth: None, }) ), ]), @@ -3471,6 +3473,76 @@ mod tests { }) } + #[test] + fn test_resolve_auth() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [profile.default] + eth_rpc_url = "optimism" + [rpc_endpoints] + optimism = "https://example.com/" + mainnet = { endpoint = "${_CONFIG_MAINNET}", retries = 3, retry_backoff = 1000, compute_units_per_second = 1000, auth = "Bearer ${_CONFIG_AUTH}" } + "#, + )?; + + let config = Config::load(); + + jail.set_env("_CONFIG_AUTH", "123456"); + jail.set_env("_CONFIG_MAINNET", "https://eth-mainnet.alchemyapi.io/v2/123455"); + + assert_eq!( + RpcEndpoints::new([ + ( + "optimism", + RpcEndpointType::String(RpcEndpoint::Url( + "https://example.com/".to_string() + )) + ), + ( + "mainnet", + RpcEndpointType::Config(RpcEndpointConfig { + endpoint: RpcEndpoint::Env("${_CONFIG_MAINNET}".to_string()), + retries: Some(3), + retry_backoff: Some(1000), + compute_units_per_second: Some(1000), + auth: Some(RpcAuth::Env("Bearer ${_CONFIG_AUTH}".to_string())), + }) + ), + ]), + config.rpc_endpoints + ); + let resolved = config.rpc_endpoints.resolved(); + assert_eq!( + RpcEndpoints::new([ + ( + "optimism", + RpcEndpointType::String(RpcEndpoint::Url( + "https://example.com/".to_string() + )) + ), + ( + "mainnet", + RpcEndpointType::Config(RpcEndpointConfig { + endpoint: RpcEndpoint::Url( + "https://eth-mainnet.alchemyapi.io/v2/123455".to_string() + ), + retries: Some(3), + retry_backoff: Some(1000), + compute_units_per_second: Some(1000), + auth: Some(RpcAuth::Raw("Bearer 123456".to_string())), + }) + ), + ]) + .resolved(), + resolved + ); + + Ok(()) + }); + } + #[test] fn test_resolve_endpoints() { figment::Jail::expect_with(|jail| { From 1197fbea0b0f9dde45579a61a5ff956fc0aee426 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 8 Aug 2024 23:01:54 +0200 Subject: [PATCH 066/184] chore: bump revm 13 (#8628) --- Cargo.lock | 24 ++++++++++++------------ Cargo.toml | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 57bb59ae5..5b38c834b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4035,9 +4035,9 @@ dependencies = [ [[package]] name = "foundry-fork-db" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "734f01574b6804ed6985d042684235c6c1007228eff8b2b488c260e3caf742d5" +checksum = "48e1217b5063138a87feb51bd9ac71857d370f06f1aa3d8c22b73aae0e49f4c3" dependencies = [ "alloy-primitives", "alloy-provider", @@ -7135,9 +7135,9 @@ dependencies = [ [[package]] name = "revm" -version = "12.1.0" +version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cfb48bce8ca2113e157bdbddbd5eeb09daac1c903d79ec17085897c38c7c91" +checksum = "6b2f635bbbf4002b1b5c0219f841ec1a317723883ed7662c0d138617539a6087" dependencies = [ "auto_impl", "cfg-if", @@ -7150,9 +7150,9 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a785dafff303a335980e317669c4e9800cdd5dd2830c6880c3247022761e88" +checksum = "43cbb1576a147317c6990cf69d6cd5ef402df99f638616ba911006e9ec45866b" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -7167,9 +7167,9 @@ dependencies = [ [[package]] name = "revm-interpreter" -version = "8.1.0" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b0daddea06fc6da5346acc39b32a357bbe3579e9e3d94117d9ae125cd596fc" +checksum = "f2ad04c7d87dc3421a5ccca76e56dbd0b29a358c03bb41fe9e80976e9d3f397d" dependencies = [ "revm-primitives", "serde", @@ -7177,9 +7177,9 @@ dependencies = [ [[package]] name = "revm-precompile" -version = "9.2.0" +version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef55228211251d7b6c7707c3ee13bb70dea4d2fd81ec4034521e4fe31010b2ea" +checksum = "3526a4ba5ec400e7bbe71affbc10fe2e67c1cd1fb782bab988873d09a102e271" dependencies = [ "aurora-engine-modexp", "blst", @@ -7197,9 +7197,9 @@ dependencies = [ [[package]] name = "revm-primitives" -version = "7.1.0" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fc4311037ee093ec50ec734e1424fcb3e12d535c6cef683b75d1c064639630c" +checksum = "4093d98a26601f0a793871c5bc7928410592f76b1f03fc89fde77180c554643c" dependencies = [ "alloy-eips", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index d1646a070..e7ff9087f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -164,8 +164,8 @@ solang-parser = "=0.3.3" ## revm # no default features to avoid c-kzg -revm = { version = "12.1.0", default-features = false } -revm-primitives = { version = "7.1.0", default-features = false } +revm = { version = "13.0.0", default-features = false } +revm-primitives = { version = "8.0.0", default-features = false } revm-inspectors = { version = "0.5", features = ["serde"] } ## ethers From f6c6b3585ff103d592257d82171104012d3465d0 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 9 Aug 2024 13:12:56 +0300 Subject: [PATCH 067/184] chore: simplify assert/require shrink test (#8634) --- crates/forge/tests/it/invariant.rs | 2 + .../common/InvariantShrinkWithAssert.t.sol | 67 +------------------ 2 files changed, 4 insertions(+), 65 deletions(-) diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index e4bf14ecd..bd9286713 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -301,6 +301,8 @@ async fn check_shrink_sequence(test_pattern: &str, expected_len: usize) { Filter::new(test_pattern, ".*", ".*fuzz/invariant/common/InvariantShrinkWithAssert.t.sol"); let mut runner = TEST_DATA_DEFAULT.runner(); runner.test_options.fuzz.seed = Some(U256::from(100u32)); + runner.test_options.invariant.runs = 1; + runner.test_options.invariant.depth = 15; match get_counterexample!(runner, &filter) { CounterExample::Single(_) => panic!("CounterExample should be a sequence."), diff --git a/testdata/default/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol b/testdata/default/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol index 34d11ccb3..fa4a6e945 100644 --- a/testdata/default/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol +++ b/testdata/default/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol @@ -2,20 +2,10 @@ pragma solidity ^0.8.13; import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -struct FuzzSelector { - address addr; - bytes4[] selectors; -} contract Counter { uint256 public number; - function setNumber(uint256 newNumber) public { - number = newNumber; - } - function increment() public { number++; } @@ -23,73 +13,20 @@ contract Counter { function decrement() public { number--; } - - function double() public { - number *= 2; - } - - function half() public { - number /= 2; - } - - function triple() public { - number *= 3; - } - - function third() public { - number /= 3; - } - - function quadruple() public { - number *= 4; - } - - function quarter() public { - number /= 4; - } -} - -contract Handler is DSTest { - Counter public counter; - - constructor(Counter _counter) { - counter = _counter; - counter.setNumber(0); - } - - function increment() public { - counter.increment(); - } - - function setNumber(uint256 x) public { - counter.setNumber(x); - } } contract InvariantShrinkWithAssert is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); Counter public counter; - Handler handler; function setUp() public { counter = new Counter(); - handler = new Handler(counter); - } - - function targetSelectors() public returns (FuzzSelector[] memory) { - FuzzSelector[] memory targets = new FuzzSelector[](1); - bytes4[] memory selectors = new bytes4[](2); - selectors[0] = handler.increment.selector; - selectors[1] = handler.setNumber.selector; - targets[0] = FuzzSelector(address(handler), selectors); - return targets; } function invariant_with_assert() public { - assertTrue(counter.number() != 3, "wrong counter"); + assertTrue(counter.number() < 2, "wrong counter"); } function invariant_with_require() public { - require(counter.number() != 3, "wrong counter"); + require(counter.number() < 2, "wrong counter"); } } From a52011523e2d40205a64d705d50f7716b725d941 Mon Sep 17 00:00:00 2001 From: Joseph Zhao <65984904+programskillforverification@users.noreply.github.com> Date: Fri, 9 Aug 2024 18:43:07 +0800 Subject: [PATCH 068/184] feat(forge script): set both tx input fields (#8532) * initial fix * fix error * make script tests pass * Update crates/script/src/runner.rs Co-authored-by: Matthias Seitz * Update crates/script/src/runner.rs Co-authored-by: Matthias Seitz * Update crates/script/src/runner.rs Co-authored-by: Matthias Seitz * change style --------- Co-authored-by: Matthias Seitz --- crates/script/src/runner.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/script/src/runner.rs b/crates/script/src/runner.rs index 7b7a2375b..584180256 100644 --- a/crates/script/src/runner.rs +++ b/crates/script/src/runner.rs @@ -1,7 +1,7 @@ use super::ScriptResult; use crate::build::ScriptPredeployLibraries; use alloy_primitives::{Address, Bytes, TxKind, U256}; -use alloy_rpc_types::TransactionRequest; +use alloy_rpc_types::{TransactionInput, TransactionRequest}; use eyre::Result; use foundry_cheatcodes::BroadcastableTransaction; use foundry_config::Config; @@ -75,7 +75,7 @@ impl ScriptRunner { rpc: self.evm_opts.fork_url.clone(), transaction: TransactionRequest { from: Some(self.evm_opts.sender), - input: Some(code.clone()).into(), + input: TransactionInput::both(code.clone()), nonce: Some(sender_nonce + library_transactions.len() as u64), ..Default::default() } @@ -109,7 +109,7 @@ impl ScriptRunner { rpc: self.evm_opts.fork_url.clone(), transaction: TransactionRequest { from: Some(self.evm_opts.sender), - input: Some(calldata.into()).into(), + input: TransactionInput::both(calldata.clone().into()), nonce: Some(sender_nonce + library_transactions.len() as u64), to: Some(TxKind::Call(DEFAULT_CREATE2_DEPLOYER)), ..Default::default() From 21fae5ed4e3a21809946ae1d5642d653eb8b63f4 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 9 Aug 2024 13:44:17 +0300 Subject: [PATCH 069/184] fix(fmt): preserve format of disabled item (#8633) --- crates/fmt/src/formatter.rs | 21 ++++++++++++++++++++- crates/fmt/testdata/Repros/fmt.sol | 25 +++++++++++++++++++++++++ crates/fmt/testdata/Repros/original.sol | 25 +++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/crates/fmt/src/formatter.rs b/crates/fmt/src/formatter.rs index e7625dc11..e36a403e5 100644 --- a/crates/fmt/src/formatter.rs +++ b/crates/fmt/src/formatter.rs @@ -405,7 +405,26 @@ impl<'a, W: Write> Formatter<'a, W> { while let Some((loc, item)) = items.next() { let chunk_next_byte_offset = items.peek().map(|(loc, _)| loc.start()).or(next_byte_offset); - out.push(self.visit_to_chunk(loc.start(), chunk_next_byte_offset, item)?); + + let chunk = if self.inline_config.is_disabled(loc) { + // If item format is disabled, we determine last disabled line from item and create + // chunk with raw src. + let mut disabled_loc = loc; + self.chunked(disabled_loc.start(), chunk_next_byte_offset, |fmt| { + while fmt.inline_config.is_disabled(disabled_loc) { + if let Some(next_line) = fmt.find_next_line(disabled_loc.end()) { + disabled_loc = disabled_loc.with_end(next_line); + } else { + break; + } + } + fmt.write_raw_src(disabled_loc)?; + Ok(()) + })? + } else { + self.visit_to_chunk(loc.start(), chunk_next_byte_offset, item)? + }; + out.push(chunk); } Ok(out) } diff --git a/crates/fmt/testdata/Repros/fmt.sol b/crates/fmt/testdata/Repros/fmt.sol index d4daa364c..4c305b3e5 100644 --- a/crates/fmt/testdata/Repros/fmt.sol +++ b/crates/fmt/testdata/Repros/fmt.sol @@ -106,3 +106,28 @@ contract TestContract { return true; } } + +// https://github.com/foundry-rs/foundry/issues/5825 +library MyLib { + bytes32 private constant TYPE_HASH = keccak256( + // forgefmt: disable-start + "MyStruct(" + "uint8 myEnum," + "address myAddress" + ")" + // forgefmt: disable-end + ); + + bytes32 private constant TYPE_HASH_1 = keccak256( + "MyStruct(" "uint8 myEnum," "address myAddress" ")" // forgefmt: disable-line + ); + + // forgefmt: disable-start + bytes32 private constant TYPE_HASH_2 = keccak256( + "MyStruct(" + "uint8 myEnum," + "address myAddress" + ")" + ); + // forgefmt: disable-end +} diff --git a/crates/fmt/testdata/Repros/original.sol b/crates/fmt/testdata/Repros/original.sol index 33c4d216f..3c09cd920 100644 --- a/crates/fmt/testdata/Repros/original.sol +++ b/crates/fmt/testdata/Repros/original.sol @@ -104,3 +104,28 @@ contract TestContract { } return true ; } } + +// https://github.com/foundry-rs/foundry/issues/5825 +library MyLib { + bytes32 private constant TYPE_HASH = keccak256( + // forgefmt: disable-start + "MyStruct(" + "uint8 myEnum," + "address myAddress" + ")" + // forgefmt: disable-end + ); + + bytes32 private constant TYPE_HASH_1 = keccak256( + "MyStruct(" "uint8 myEnum," "address myAddress" ")" // forgefmt: disable-line + ); + + // forgefmt: disable-start + bytes32 private constant TYPE_HASH_2 = keccak256( + "MyStruct(" + "uint8 myEnum," + "address myAddress" + ")" + ); + // forgefmt: disable-end +} From 1902f754950c57286155d39e3e0eba1e23f64ff4 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 9 Aug 2024 15:57:59 +0200 Subject: [PATCH 070/184] fix: bad unwrap for pretty fmt (#8636) --- crates/common/fmt/src/ui.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/common/fmt/src/ui.rs b/crates/common/fmt/src/ui.rs index bd969fecd..f13e0f021 100644 --- a/crates/common/fmt/src/ui.rs +++ b/crates/common/fmt/src/ui.rs @@ -339,7 +339,7 @@ transactionIndex {} type {} value {} yParity {}{}", - self.access_list.as_deref().unwrap().pretty(), + self.access_list.as_deref().map(Vec::as_slice).unwrap_or(&[]).pretty(), self.block_hash.pretty(), self.block_number.pretty(), self.chain_id.pretty(), @@ -378,7 +378,7 @@ transactionIndex {} type {} value {} yParity {}{}", - self.access_list.as_deref().unwrap().pretty(), + self.access_list.as_deref().map(Vec::as_slice).unwrap_or(&[]).pretty(), self.block_hash.pretty(), self.block_number.pretty(), self.chain_id.pretty(), @@ -420,8 +420,8 @@ transactionIndex {} type {} value {} yParity {}{}", - self.access_list.as_deref().unwrap().pretty(), - self.blob_versioned_hashes.as_deref().unwrap().pretty(), + self.access_list.as_deref().map(Vec::as_slice).unwrap_or(&[]).pretty(), + self.blob_versioned_hashes.as_deref().unwrap_or(&[]).pretty(), self.block_hash.pretty(), self.block_number.pretty(), self.chain_id.pretty(), @@ -463,7 +463,7 @@ transactionIndex {} type {} value {} yParity {}{}", - self.access_list.as_deref().unwrap().pretty(), + self.access_list.as_deref().map(Vec::as_slice).unwrap_or(&[]).pretty(), self.authorization_list .as_ref() .map(|l| serde_json::to_string(&l).unwrap()) From 77158ccee1cc0be8b647021b868a14314676a81a Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 9 Aug 2024 18:35:39 +0300 Subject: [PATCH 071/184] fix(fmt): write single param on multiline if `params_first` (#8637) fix(fmt): write single param on multiline --- crates/fmt/src/formatter.rs | 4 ++++ crates/fmt/testdata/FunctionDefinition/all.fmt.sol | 4 +++- crates/fmt/testdata/FunctionDefinition/params-first.fmt.sol | 4 +++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/fmt/src/formatter.rs b/crates/fmt/src/formatter.rs index e36a403e5..6cd054d56 100644 --- a/crates/fmt/src/formatter.rs +++ b/crates/fmt/src/formatter.rs @@ -1626,6 +1626,10 @@ impl<'a, W: Write> Formatter<'a, W> { ¶ms, ",", )?; + // Write new line if we have only one parameter and params on multiline set. + if params.len() == 1 && params_multiline { + writeln!(fmt.buf())?; + } fmt.write_chunks_separated(¶ms, ",", params_multiline)?; Ok(()) }, diff --git a/crates/fmt/testdata/FunctionDefinition/all.fmt.sol b/crates/fmt/testdata/FunctionDefinition/all.fmt.sol index 6d9088067..86577d44b 100644 --- a/crates/fmt/testdata/FunctionDefinition/all.fmt.sol +++ b/crates/fmt/testdata/FunctionDefinition/all.fmt.sol @@ -717,7 +717,9 @@ contract FunctionOverrides is a = 1; } - function oneParam(uint256 x) + function oneParam( + uint256 x + ) override( FunctionInterfaces, FunctionDefinitions, diff --git a/crates/fmt/testdata/FunctionDefinition/params-first.fmt.sol b/crates/fmt/testdata/FunctionDefinition/params-first.fmt.sol index 50f539c32..8208c15d2 100644 --- a/crates/fmt/testdata/FunctionDefinition/params-first.fmt.sol +++ b/crates/fmt/testdata/FunctionDefinition/params-first.fmt.sol @@ -697,7 +697,9 @@ contract FunctionOverrides is a = 1; } - function oneParam(uint256 x) + function oneParam( + uint256 x + ) override( FunctionInterfaces, FunctionDefinitions, From 03c20289eff8d90e891f9605fd5729ae677bf303 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sat, 10 Aug 2024 19:19:12 +0800 Subject: [PATCH 072/184] fix(anvil): correctly print logs (#8642) --- crates/anvil/src/eth/backend/mem/inspector.rs | 2 +- crates/anvil/src/eth/backend/mem/mod.rs | 22 ++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/crates/anvil/src/eth/backend/mem/inspector.rs b/crates/anvil/src/eth/backend/mem/inspector.rs index 68336935f..2095e9d50 100644 --- a/crates/anvil/src/eth/backend/mem/inspector.rs +++ b/crates/anvil/src/eth/backend/mem/inspector.rs @@ -41,7 +41,7 @@ impl Inspector { self } - pub fn with_config(mut self, config: TracingInspectorConfig) -> Self { + pub fn with_tracing_config(mut self, config: TracingInspectorConfig) -> Self { self.tracer = Some(TracingInspector::new(config)); self } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index da0a22883..180554e83 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -851,7 +851,7 @@ impl Backend { } let db = self.db.read().await; - let mut inspector = Inspector::default(); + let mut inspector = self.build_inspector(); let mut evm = self.new_evm_with_inspector_ref(&**db, env, &mut inspector); let ResultAndState { result, state } = evm.transact()?; let (exit_reason, gas_used, out, logs) = match result { @@ -1200,6 +1200,17 @@ impl Backend { env } + /// Builds [`Inspector`] with the configured options + fn build_inspector(&self) -> Inspector { + let mut inspector = Inspector::default(); + + if self.print_logs { + inspector = inspector.with_log_collector(); + } + + inspector + } + pub fn call_with_state( &self, state: D, @@ -1210,7 +1221,7 @@ impl Backend { where D: DatabaseRef, { - let mut inspector = Inspector::default(); + let mut inspector = self.build_inspector(); let env = self.build_call_env(request, fee_details, block_env); let mut evm = self.new_evm_with_inspector_ref(state, env, &mut inspector); @@ -1251,7 +1262,7 @@ impl Backend { .into_call_config() .map_err(|e| (RpcError::invalid_params(e.to_string())))?; - let mut inspector = Inspector::default().with_config( + let mut inspector = self.build_inspector().with_tracing_config( TracingInspectorConfig::from_geth_call_config(&call_config), ); @@ -1283,8 +1294,9 @@ impl Backend { } // defaults to StructLog tracer used since no tracer is specified - let mut inspector = - Inspector::default().with_config(TracingInspectorConfig::from_geth_config(&config)); + let mut inspector = self + .build_inspector() + .with_tracing_config(TracingInspectorConfig::from_geth_config(&config)); let env = self.build_call_env(request, fee_details, block); let mut evm = self.new_evm_with_inspector_ref(state, env, &mut inspector); From a0a002020be4c40946fe122fe6ff752b21cb2885 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 11 Aug 2024 20:53:14 +0000 Subject: [PATCH 073/184] chore(deps): weekly `cargo update` (#8647) --- Cargo.lock | 114 +++++++++++++++++++++++++---------------------------- 1 file changed, 53 insertions(+), 61 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b38c834b..b97f66881 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,9 +68,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-chains" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01828af9e8fb20a85b1caf6570d84ee93595d6cbd9e8ea96bda3a4e27a55a4fa" +checksum = "5b515e82c8468ddb6ff8db21c78a5997442f113fd8471fd5b2261b2602dd0c67" dependencies = [ "num_enum", "serde", @@ -307,9 +307,9 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a43b18702501396fa9bcdeecd533bc85fac75150d308fc0f6800a01e6234a003" +checksum = "26154390b1d205a4a7ac7352aa2eb4f81f391399d4e2f546fb81a2f8bb383f62" dependencies = [ "alloy-rlp-derive", "arrayvec", @@ -318,9 +318,9 @@ dependencies = [ [[package]] name = "alloy-rlp-derive" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83524c1f6162fcb5b0decf775498a125066c86dda6066ed609531b0e912f85a" +checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" dependencies = [ "proc-macro2", "quote", @@ -1211,9 +1211,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "aws-config" -version = "1.5.4" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf6cfe2881cb1fcbba9ae946fb9a6480d3b7a714ca84c74925014a89ef3387a" +checksum = "4e95816a168520d72c0e7680c405a5a8c1fb6a035b4bc4b9d7b0de8e1a941697" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1231,7 +1231,6 @@ dependencies = [ "fastrand", "hex", "http 0.2.12", - "hyper 0.14.30", "ring", "time", "tokio", @@ -1254,9 +1253,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87c5f920ffd1e0526ec9e70e50bf444db50b204395a0fa7016bbf9e31ea1698f" +checksum = "f42c2d4218de4dcd890a109461e2f799a1a2ba3bcd2cde9af88360f5df9266c6" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -1269,6 +1268,7 @@ dependencies = [ "fastrand", "http 0.2.12", "http-body 0.4.6", + "once_cell", "percent-encoding", "pin-project-lite", "tracing", @@ -1277,9 +1277,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91f43512620f4b0d9e67ccf7d768fab5ed310ac2229ebb9422177abe99c36ba" +checksum = "9d073fcc95d01301c115011f8f23bc436d66f01b8265d149e994a2d8318c903c" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1299,9 +1299,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.36.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6acca681c53374bf1d9af0e317a41d12a44902ca0f2d1e10e5cb5bb98ed74f35" +checksum = "1074e818fbe4f9169242d78448b15be8916a79daa38ea1231f2e2e10d993fcd2" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1321,9 +1321,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b79c6bdfe612503a526059c05c9ccccbf6bd9530b003673cb863e547fd7c0c9a" +checksum = "29755c51e33fa3f678598f64324a169cf4b7d3c4865d2709d4308f53366a92a4" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1343,9 +1343,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.36.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e6ecdb2bd756f3b2383e6f0588dc10a4e65f5d551e70a56e0bfe0c884673ce" +checksum = "6e52dc3fd7dfa6c01a69cf3903e00aa467261639138a05b06cd92314d2c8fb07" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1466,9 +1466,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30819352ed0a04ecf6a2f3477e344d2d1ba33d43e0f09ad9047c12e0d923616f" +checksum = "e086682a53d3aa241192aa110fa8dfce98f2f5ac2ead0de84d41582c7e8fdb96" dependencies = [ "aws-smithy-async", "aws-smithy-types", @@ -1987,9 +1987,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" +checksum = "504bdec147f2cc13c8b57ed9401fd8a147cc66b67ad5cb241394244f2c947549" dependencies = [ "jobserver", "libc", @@ -2098,9 +2098,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.13" +version = "4.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc" +checksum = "11d8838454fda655dafd3accb2b6e2bea645b9e4078abe84a22ceb947235c5cc" dependencies = [ "clap_builder", "clap_derive", @@ -2108,9 +2108,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.13" +version = "4.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99" +checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" dependencies = [ "anstream", "anstyle", @@ -2123,9 +2123,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.12" +version = "4.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8670053e87c316345e384ca1f3eba3006fc6355ed8b8a1140d104e109e3df34" +checksum = "1d11bff0290e9a266fc9b4ce6fa96c2bf2ca3f9724c41c10202ac1daf7a087f8" dependencies = [ "clap", ] @@ -3255,14 +3255,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", - "windows-sys 0.52.0", + "libredox", + "windows-sys 0.59.0", ] [[package]] @@ -4942,9 +4942,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" +checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" dependencies = [ "bytes", "futures-channel", @@ -5416,6 +5416,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.6.0", "libc", + "redox_syscall", ] [[package]] @@ -6002,14 +6003,14 @@ dependencies = [ [[package]] name = "opener" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8df34be653210fbe9ffaff41d3b92721c56ce82dfee58ee684f9afb5e3a90c0" +checksum = "d0812e5e4df08da354c851a3376fead46db31c2214f849d3de356d774d057681" dependencies = [ "bstr", "dbus", "normpath", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -6151,7 +6152,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.3", + "redox_syscall", "smallvec", "windows-targets 0.52.6", ] @@ -6959,15 +6960,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.5.3" @@ -7471,9 +7463,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" [[package]] name = "rustls-webpki" @@ -7587,9 +7579,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.1.8" +version = "2.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d777f59627453628a9a5be1ee8d948745b94b1dfc2d0c3099cbd9e08ab89e7c" +checksum = "c76e6f627d67cd14a317d7909585f4d06609acafd7891432ea45ce519211a8e9" dependencies = [ "sdd", ] @@ -7663,9 +7655,9 @@ dependencies = [ [[package]] name = "sdd" -version = "2.1.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177258b64c0faaa9ffd3c65cd3262c2bc7e2588dbbd9c1641d0346145c1bbda8" +checksum = "f081bcf2e6c4d1d88d2b8d1c9d8a308993eafbdabb851050be4b2ff14d2c5649" [[package]] name = "sec1" @@ -7771,18 +7763,18 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.204" +version = "1.0.205" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.205" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1" dependencies = [ "proc-macro2", "quote", @@ -8390,15 +8382,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.11.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fcd239983515c23a32fb82099f97d0b11b8c72f654ed659363a95c3dad7a53" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] From 515a4cc8aba1627a717a1057ff4f09c8cd3bf51f Mon Sep 17 00:00:00 2001 From: Kris Kaczor Date: Mon, 12 Aug 2024 12:14:27 +0400 Subject: [PATCH 074/184] Add --disable-code-size-limit flag for anvil (#8646) Add --disable-code-size-limit flag --- crates/anvil/src/cmd.rs | 29 +++++++++++++++++++++++++++-- crates/anvil/src/config.rs | 8 ++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 2897cebdd..a2802e059 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -246,6 +246,7 @@ impl NodeArgs { .with_auto_impersonate(self.evm_opts.auto_impersonate) .with_ipc(self.ipc) .with_code_size_limit(self.evm_opts.code_size_limit) + .disable_code_size_limit(self.evm_opts.disable_code_size_limit) .set_pruned_history(self.prune_history) .with_init_state(self.load_state.or_else(|| self.state.and_then(|s| s.state))) .with_transaction_block_keeper(self.transaction_block_keeper) @@ -495,11 +496,20 @@ pub struct AnvilEvmArgs { )] pub disable_block_gas_limit: bool, - /// EIP-170: Contract code size limit in bytes. Useful to increase this because of tests. By - /// default, it is 0x6000 (~25kb). + /// EIP-170: Contract code size limit in bytes. Useful to increase this because of tests. To + /// disable entirely, use `--disable-code-size-limit`. By default, it is 0x6000 (~25kb). #[arg(long, value_name = "CODE_SIZE", help_heading = "Environment config")] pub code_size_limit: Option, + /// Disable EIP-170: Contract code size limit. + #[arg( + long, + value_name = "DISABLE_CODE_SIZE_LIMIT", + conflicts_with = "code_size_limit", + help_heading = "Environment config" + )] + pub disable_code_size_limit: bool, + /// The gas price. #[arg(long, help_heading = "Environment config")] pub gas_price: Option, @@ -805,6 +815,21 @@ mod tests { assert!(args.is_err()); } + #[test] + fn can_parse_disable_code_size_limit() { + let args: NodeArgs = NodeArgs::parse_from(["anvil", "--disable-code-size-limit"]); + assert!(args.evm_opts.disable_code_size_limit); + + let args = NodeArgs::try_parse_from([ + "anvil", + "--disable-code-size-limit", + "--code-size-limit", + "100", + ]); + // can't be used together + assert!(args.is_err()); + } + #[test] fn can_parse_host() { let args = NodeArgs::parse_from(["anvil"]); diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 28080f628..5e8ef0f9a 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -482,6 +482,14 @@ impl NodeConfig { self.code_size_limit = code_size_limit; self } + /// Disables code size limit + #[must_use] + pub fn disable_code_size_limit(mut self, disable_code_size_limit: bool) -> Self { + if disable_code_size_limit { + self.code_size_limit = Some(usize::MAX); + } + self + } /// Sets the init state if any #[must_use] From e36bc81b1dad51d6601ea2ba214b6c86ea200197 Mon Sep 17 00:00:00 2001 From: cui <523516579@qq.com> Date: Tue, 13 Aug 2024 22:10:45 +0800 Subject: [PATCH 075/184] fix: allow result large err (#8656) --- crates/config/src/fix.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/config/src/fix.rs b/crates/config/src/fix.rs index 73d52d979..f3ec1271f 100644 --- a/crates/config/src/fix.rs +++ b/crates/config/src/fix.rs @@ -69,6 +69,7 @@ impl std::error::Error for InsertProfileError {} impl TomlFile { /// Insert a name as `[profile.name]`. Creating the `[profile]` table where necessary and /// throwing an error if there exists a conflict + #[allow(clippy::result_large_err)] fn insert_profile( &mut self, profile_str: &str, From 1c71ab19f28e98e05db393aa6fd827730c9bf4ac Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 13 Aug 2024 08:40:33 -0700 Subject: [PATCH 076/184] fix(coverage): relax deployed bytecode accepted score (#8657) --- crates/common/src/contracts.rs | 8 ++-- crates/forge/tests/cli/coverage.rs | 69 ++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index 7e07f9db6..38f73a713 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -115,24 +115,26 @@ impl ContractsByArtifact { /// Finds a contract which has a similar bytecode as `code`. pub fn find_by_creation_code(&self, code: &[u8]) -> Option> { - self.find_by_code(code, ContractData::bytecode) + self.find_by_code(code, 0.1, ContractData::bytecode) } /// Finds a contract which has a similar deployed bytecode as `code`. pub fn find_by_deployed_code(&self, code: &[u8]) -> Option> { - self.find_by_code(code, ContractData::deployed_bytecode) + self.find_by_code(code, 0.15, ContractData::deployed_bytecode) } + /// Finds a contract based on provided bytecode and accepted match score. fn find_by_code( &self, code: &[u8], + accepted_score: f64, get: impl Fn(&ContractData) -> Option<&Bytes>, ) -> Option> { self.iter() .filter_map(|(id, contract)| { if let Some(deployed_bytecode) = get(contract) { let score = bytecode_diff_score(deployed_bytecode.as_ref(), code); - (score <= 0.1).then_some((score, (id, contract))) + (score <= accepted_score).then_some((score, (id, contract))) } else { None } diff --git a/crates/forge/tests/cli/coverage.rs b/crates/forge/tests/cli/coverage.rs index 078e06d95..1b668afac 100644 --- a/crates/forge/tests/cli/coverage.rs +++ b/crates/forge/tests/cli/coverage.rs @@ -1202,3 +1202,72 @@ contract AContractTest is DSTest { "#]], ); }); + +forgetest!(test_identical_bytecodes, |prj, cmd| { + prj.insert_ds_test(); + prj.add_source( + "AContract.sol", + r#" +contract AContract { + uint256 public number; + address public immutable usdc1; + address public immutable usdc2; + address public immutable usdc3; + address public immutable usdc4; + address public immutable usdc5; + address public immutable usdc6; + + constructor() { + address a = 0x176211869cA2b568f2A7D4EE941E073a821EE1ff; + usdc1 = a; + usdc2 = a; + usdc3 = a; + usdc4 = a; + usdc5 = a; + usdc6 = a; + } + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} + "#, + ) + .unwrap(); + + prj.add_source( + "AContractTest.sol", + r#" +import "./test.sol"; +import {AContract} from "./AContract.sol"; + +contract AContractTest is DSTest { + AContract public counter; + + function setUp() public { + counter = new AContract(); + counter.setNumber(0); + } + + function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); + } +} + "#, + ) + .unwrap(); + + cmd.arg("coverage").args(["--summary".to_string()]).assert_success().stdout_eq(str![[r#" +... +| File | % Lines | % Statements | % Branches | % Funcs | +|-------------------|---------------|---------------|---------------|---------------| +| src/AContract.sol | 100.00% (9/9) | 100.00% (9/9) | 100.00% (0/0) | 100.00% (3/3) | +| Total | 100.00% (9/9) | 100.00% (9/9) | 100.00% (0/0) | 100.00% (3/3) | + +"#]]); +}); From 224fe9cbf76084c176dabf7d3b2edab5df1ab818 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 13 Aug 2024 14:26:02 -0700 Subject: [PATCH 077/184] fix(fmt): apply multi statement block config to else block (#8661) fix(fmt): apply multi statement block to else clauses --- crates/fmt/src/formatter.rs | 2 +- crates/fmt/testdata/Repros/fmt.sol | 14 ++++++++++++++ crates/fmt/testdata/Repros/original.sol | 14 ++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/crates/fmt/src/formatter.rs b/crates/fmt/src/formatter.rs index 6cd054d56..396348201 100644 --- a/crates/fmt/src/formatter.rs +++ b/crates/fmt/src/formatter.rs @@ -1782,7 +1782,7 @@ impl<'a, W: Write> Formatter<'a, W> { self.visit_if(*loc, cond, if_branch, else_branch, false)?; } else { let else_branch_is_single_line = - self.visit_stmt_as_block(else_branch, if_branch_is_single_line)?; + self.visit_stmt_as_block(else_branch, attempt_single_line)?; if single_line_stmt_wide && !else_branch_is_single_line { bail!(FormatterError::fmt()) } diff --git a/crates/fmt/testdata/Repros/fmt.sol b/crates/fmt/testdata/Repros/fmt.sol index 4c305b3e5..0842c7956 100644 --- a/crates/fmt/testdata/Repros/fmt.sol +++ b/crates/fmt/testdata/Repros/fmt.sol @@ -131,3 +131,17 @@ library MyLib { ); // forgefmt: disable-end } + +contract IfElseTest { + function setNumber(uint256 newNumber) public { + number = newNumber; + if (newNumber = 1) { + number = 1; + } else if (newNumber = 2) { + // number = 2; + } + else { + newNumber = 3; + } + } +} diff --git a/crates/fmt/testdata/Repros/original.sol b/crates/fmt/testdata/Repros/original.sol index 3c09cd920..ef75931e2 100644 --- a/crates/fmt/testdata/Repros/original.sol +++ b/crates/fmt/testdata/Repros/original.sol @@ -129,3 +129,17 @@ library MyLib { ); // forgefmt: disable-end } + +contract IfElseTest { + function setNumber(uint256 newNumber) public { + number = newNumber; + if (newNumber = 1) { + number = 1; + } else if (newNumber = 2) { + // number = 2; + } + else { + newNumber = 3; + } + } +} From 3e3b30c61c6b24c0d3e336503b67358f612a6f0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Vincent?= <28714795+leovct@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:40:31 +0200 Subject: [PATCH 078/184] fix: `cast --to-ascii` does not work if input has trailing whitespaces (#8670) * fix: `cast --to-ascii` removes trailing whitespaces on stdin * fix: remove extra break line returned by `cast call` * Update crates/cast/bin/main.rs Co-authored-by: Matthias Seitz * Update crates/cast/src/lib.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * Update crates/cast/bin/main.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: Matthias Seitz Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/cast/bin/main.rs | 2 +- crates/cast/src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 044cc25ff..8bc0b9866 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -66,7 +66,7 @@ async fn main() -> Result<()> { } CastSubcommand::ToAscii { hexdata } => { let value = stdin::unwrap(hexdata, false)?; - println!("{}", SimpleCast::to_ascii(&value)?); + println!("{}", SimpleCast::to_ascii(value.trim())?); } CastSubcommand::ToUtf8 { hexdata } => { let value = stdin::unwrap(hexdata, false)?; diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 93e3c4a0f..7c2d970a7 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -168,7 +168,7 @@ where // handle case when return type is not specified Ok(if decoded.is_empty() { - format!("{res}\n") + res.to_string() } else if json { let tokens = decoded.iter().map(format_token_raw).collect::>(); serde_json::to_string_pretty(&tokens).unwrap() @@ -1503,7 +1503,7 @@ impl SimpleCast { /// decoded[1].as_address().unwrap().to_string().to_lowercase(), /// decoded[2].as_uint().unwrap().0.to_string(), /// decoded[3].as_uint().unwrap().0.to_string(), - /// hex::encode(decoded[4].as_bytes().unwrap()) + /// hex::encode(decoded[4].as_bytes().unwrap()) /// ] /// .into_iter() /// .collect::>(); From b34b0f720c8b2b281b3dccdf5cab6e9479ebce2b Mon Sep 17 00:00:00 2001 From: bernard-wagner Date: Thu, 15 Aug 2024 16:21:55 +0200 Subject: [PATCH 079/184] fix(anvil): get_transaction_count forking (#8675) --- crates/anvil/src/eth/api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 822bc57f1..fbd37a3f2 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -2651,7 +2651,7 @@ impl EthApi { if let BlockRequest::Number(number) = block_request { if let Some(fork) = self.get_fork() { - if fork.predates_fork_inclusive(number) { + if fork.predates_fork(number) { return Ok(fork.get_nonce(address, number).await?) } } From 6928687de81b5eb840ad825acb0af01a984d677e Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 15 Aug 2024 12:15:36 -0700 Subject: [PATCH 080/184] alloy update (#8660) * Take 1 * cargo * Latest alloy * Fix revm --- Cargo.lock | 751 ++++++++----------- Cargo.toml | 28 +- crates/anvil/core/src/eth/transaction/mod.rs | 22 + crates/anvil/src/eth/backend/fork.rs | 2 +- crates/anvil/src/pubsub.rs | 4 +- crates/anvil/tests/it/eip7702.rs | 7 +- crates/anvil/tests/it/traces.rs | 11 +- crates/cheatcodes/src/evm.rs | 4 +- crates/cheatcodes/src/evm/mock.rs | 2 +- crates/cheatcodes/src/inspector.rs | 22 +- crates/evm/core/src/backend/mod.rs | 2 +- crates/evm/core/src/utils.rs | 2 +- crates/evm/evm/src/inspectors/stack.rs | 1 - crates/forge/bin/cmd/create.rs | 8 +- crates/wallets/Cargo.toml | 2 +- 15 files changed, 380 insertions(+), 488 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b97f66881..6fd1aa9cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,8 +80,7 @@ dependencies = [ [[package]] name = "alloy-consensus" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04c309895995eaa4bfcc345f5515a39c7df9447798645cc8bf462b6c5bf1dc96" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-eips", "alloy-primitives", @@ -94,8 +93,7 @@ dependencies = [ [[package]] name = "alloy-contract" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f4e0ef72b0876ae3068b2ed7dfae9ae1779ce13cfaec2ee1f08f5bd0348dc57" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -125,7 +123,7 @@ dependencies = [ "arbitrary", "const-hex", "derive_arbitrary", - "derive_more", + "derive_more 0.99.18", "itoa", "proptest", "serde", @@ -136,15 +134,14 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9431c99a3b3fe606ede4b3d4043bdfbcb780c45b8d8d226c3804e2b75cfbe68" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-primitives", "alloy-rlp", "alloy-serde", "arbitrary", "c-kzg", - "derive_more", + "derive_more 1.0.0", "k256", "once_cell", "rand", @@ -155,8 +152,7 @@ dependencies = [ [[package]] name = "alloy-genesis" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79614dfe86144328da11098edcc7bc1a3f25ad8d3134a9eb9e857e06f0d9840d" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-primitives", "alloy-serde", @@ -178,8 +174,7 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57e2865c4c3bb4cdad3f0d9ec1ab5c0c657ba69a375651bd35e32fb6c180ccc2" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -192,8 +187,7 @@ dependencies = [ [[package]] name = "alloy-network" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e701fc87ef9a3139154b0b4ccb935b565d27ffd9de020fe541bf2dec5ae4ede" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-consensus", "alloy-eips", @@ -213,8 +207,7 @@ dependencies = [ [[package]] name = "alloy-network-primitives" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec9d5a0f9170b10988b6774498a022845e13eda94318440d17709d50687f67f9" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-primitives", "alloy-serde", @@ -233,7 +226,7 @@ dependencies = [ "cfg-if", "const-hex", "derive_arbitrary", - "derive_more", + "derive_more 0.99.18", "ethereum_ssz", "getrandom", "hex-literal", @@ -251,8 +244,7 @@ dependencies = [ [[package]] name = "alloy-provider" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9c0ab10b93de601a6396fc7ff2ea10d3b28c46f079338fa562107ebf9857c8" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-chains", "alloy-consensus", @@ -278,9 +270,10 @@ dependencies = [ "futures-utils-wasm", "lru", "pin-project 1.1.5", - "reqwest 0.12.5", + "reqwest", "serde", "serde_json", + "thiserror", "tokio", "tracing", "url", @@ -289,8 +282,7 @@ dependencies = [ [[package]] name = "alloy-pubsub" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f5da2c55cbaf229bad3c5f8b00b5ab66c74ef093e5f3a753d874cfecf7d2281" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -324,14 +316,13 @@ checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] name = "alloy-rpc-client" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b38e3ffdb285df5d9f60cb988d336d9b8e3505acb78750c3bc60336a7af41d3" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -342,7 +333,7 @@ dependencies = [ "alloy-transport-ws", "futures", "pin-project 1.1.5", - "reqwest 0.12.5", + "reqwest", "serde", "serde_json", "tokio", @@ -355,8 +346,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c31a3750b8f5a350d17354e46a52b0f2f19ec5f2006d816935af599dedc521" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-rpc-types-anvil", "alloy-rpc-types-engine", @@ -370,8 +360,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ab6509cd38b2e8c8da726e0f61c1e314a81df06a38d37ddec8bced3f8d25ed" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-primitives", "alloy-serde", @@ -381,8 +370,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff63f51b2fb2f547df5218527fd0653afb1947bf7fead5b3ce58c75d170b30f7" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-consensus", "alloy-eips", @@ -399,8 +387,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81e18424d962d7700a882fe423714bd5b9dde74c7a7589d4255ea64068773aef" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-consensus", "alloy-eips", @@ -418,8 +405,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a86eeb49ea0cc79f249faa1d35c20541bb1c317a59b5962cb07b1890355b0064" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -432,8 +418,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2342fed8175642b15a37a51f8729b05b2469281fbeb816f0ccbb0087e2dd74a" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -444,8 +429,7 @@ dependencies = [ [[package]] name = "alloy-serde" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33feda6a53e6079895aed1d08dcb98a1377b000d80d16370fbbdb8155d547ef" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-primitives", "arbitrary", @@ -456,8 +440,7 @@ dependencies = [ [[package]] name = "alloy-signer" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740a25b92e849ed7b0fa013951fe2f64be9af1ad5abe805037b44fb7770c5c47" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -472,8 +455,7 @@ dependencies = [ [[package]] name = "alloy-signer-aws" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1a47bd8487fb2d715f8a203c3bfe7de0b7443eeacb00bd96d8d4eb0d67e184" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-consensus", "alloy-network", @@ -490,8 +472,7 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0850956080821ee646461fde2ff17640a2babcd206046a867eda94d9a266fad9" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-consensus", "alloy-network", @@ -508,8 +489,7 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4f7e76cb4f63dbb56857a74665510517a013fe18da82082f7c66c6d321531e" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -528,8 +508,7 @@ dependencies = [ [[package]] name = "alloy-signer-local" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b0707d4f63e4356a110b30ef3add8732ab6d181dd7be4607bf79b8777105cee" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-consensus", "alloy-network", @@ -548,8 +527,7 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81147fb1a384f878653524b9473af71ef2820846bd64a473f26e49fca8e5f8f9" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-consensus", "alloy-network", @@ -573,7 +551,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -586,11 +564,11 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.3.0", + "indexmap 2.4.0", "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", "syn-solidity", "tiny-keccak", ] @@ -608,7 +586,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.72", + "syn 2.0.74", "syn-solidity", ] @@ -638,8 +616,7 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0590afbdacf2f8cca49d025a2466f3b6584a016a8b28f532f29f8da1007bae" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -657,12 +634,11 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2437d145d80ea1aecde8574d2058cceb8b3c9cba05f6aea8e67907c660d46698" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-json-rpc", "alloy-transport", - "reqwest 0.12.5", + "reqwest", "serde_json", "tower", "tracing", @@ -672,8 +648,7 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804494366e20468776db4e18f9eb5db7db0fe14f1271eb6dbf155d867233405c" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -693,8 +668,7 @@ dependencies = [ [[package]] name = "alloy-transport-ws" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af855163e7df008799941aa6dd324a43ef2bf264b08ba4b22d44aad6ced65300" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -716,7 +690,7 @@ checksum = "03704f265cbbb943b117ecb5055fd46e8f41e7dc8a58b1aed20bcd40ace38c15" dependencies = [ "alloy-primitives", "alloy-rlp", - "derive_more", + "derive_more 0.99.18", "hashbrown 0.14.5", "nybbles", "serde", @@ -839,7 +813,7 @@ dependencies = [ "anvil-server", "async-trait", "auto_impl", - "axum 0.7.5", + "axum", "bytes", "chrono", "clap", @@ -911,7 +885,7 @@ version = "0.2.0" dependencies = [ "anvil-rpc", "async-trait", - "axum 0.7.5", + "axum", "bytes", "clap", "futures", @@ -1120,7 +1094,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -1142,7 +1116,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -1153,7 +1127,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -1182,6 +1156,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8ab6b55fe97976e46f91ddbed8d147d966475dc29b2032757ba47e02376fbc3" +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "aurora-engine-modexp" version = "1.1.0" @@ -1200,7 +1180,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -1449,7 +1429,7 @@ dependencies = [ "aws-smithy-types", "bytes", "fastrand", - "h2", + "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", "http-body 1.0.1", @@ -1527,34 +1507,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "axum" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" -dependencies = [ - "async-trait", - "axum-core 0.3.4", - "bitflags 1.3.2", - "bytes", - "futures-util", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.30", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "sync_wrapper 0.1.2", - "tower", - "tower-layer", - "tower-service", -] - [[package]] name = "axum" version = "0.7.5" @@ -1562,7 +1514,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" dependencies = [ "async-trait", - "axum-core 0.4.3", + "axum-core", "base64 0.21.7", "bytes", "futures-util", @@ -1592,23 +1544,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "axum-core" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http 0.2.12", - "http-body 0.4.6", - "mime", - "rustversion", - "tower-layer", - "tower-service", -] - [[package]] name = "axum-core" version = "0.4.3" @@ -1987,12 +1922,13 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.8" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504bdec147f2cc13c8b57ed9401fd8a147cc66b67ad5cb241394244f2c947549" +checksum = "5fb8dd288a69fc53a1996d7ecfbf4a20d59065bff137ce7e56bbd620de191189" dependencies = [ "jobserver", "libc", + "shlex", ] [[package]] @@ -2007,6 +1943,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chisel" version = "0.2.0" @@ -2028,7 +1970,7 @@ dependencies = [ "foundry-evm", "once_cell", "regex", - "reqwest 0.12.5", + "reqwest", "revm", "rustyline", "semver 1.0.23", @@ -2123,9 +2065,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.14" +version = "4.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d11bff0290e9a266fc9b4ce6fa96c2bf2ca3f9724c41c10202ac1daf7a087f8" +checksum = "9c677cd0126f3026d8b093fa29eae5d812fde5c05bc66dbb29d0374eea95113a" dependencies = [ "clap", ] @@ -2149,7 +2091,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -2184,9 +2126,9 @@ dependencies = [ [[package]] name = "coins-bip32" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66c43ff7fd9ff522219058808a259e61423335767b1071d5b346de60d9219657" +checksum = "2073678591747aed4000dd468b97b14d7007f7936851d3f2f01846899f5ebf08" dependencies = [ "bs58", "coins-core", @@ -2200,9 +2142,9 @@ dependencies = [ [[package]] name = "coins-bip39" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4587c0b4064da887ed39a6522f577267d57e58bdd583178cd877d721b56a2e" +checksum = "74b169b26623ff17e9db37a539fe4f15342080df39f129ef7631df7683d6d9d4" dependencies = [ "bitvec", "coins-bip32", @@ -2216,9 +2158,9 @@ dependencies = [ [[package]] name = "coins-core" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b3aeeec621f4daec552e9d28befd58020a78cfc364827d06a753e8bc13c6c4b" +checksum = "62b962ad8545e43a28e14e87377812ba9ae748dd4fd963f4c10e9fcc6d13475b" dependencies = [ "base64 0.21.7", "bech32", @@ -2235,9 +2177,9 @@ dependencies = [ [[package]] name = "coins-ledger" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "166ef757aa936b45f3e5d39c344047f65ef7d25a50067246a498021a816d074b" +checksum = "ab9bc0994d0aa0f4ade5f3a9baf4a8d936f250278c85a1124b401860454246ab" dependencies = [ "async-trait", "byteorder", @@ -2389,15 +2331,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" dependencies = [ "libc", ] @@ -2547,12 +2489,12 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.4" +version = "3.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345" +checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3" dependencies = [ - "nix 0.28.0", - "windows-sys 0.52.0", + "nix 0.29.0", + "windows-sys 0.59.0", ] [[package]] @@ -2576,7 +2518,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -2587,16 +2529,17 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] name = "dashmap" -version = "5.5.3" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +checksum = "804c8821570c3f8b70230c2ba75ffa5c0f9a4189b9a432b6656c536712acae28" dependencies = [ "cfg-if", + "crossbeam-utils", "hashbrown 0.14.5", "lock_api", "once_cell", @@ -2659,7 +2602,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -2680,7 +2623,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -2690,7 +2633,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -2703,7 +2646,27 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.0", - "syn 2.0.72", + "syn 2.0.74", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", ] [[package]] @@ -2810,7 +2773,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -2923,15 +2886,6 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" -[[package]] -name = "encoding_rs" -version = "0.8.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" -dependencies = [ - "cfg-if", -] - [[package]] name = "endian-type" version = "0.1.2" @@ -2946,7 +2900,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -3096,7 +3050,7 @@ dependencies = [ "regex", "serde", "serde_json", - "syn 2.0.72", + "syn 2.0.74", "toml 0.8.19", "walkdir", ] @@ -3124,7 +3078,7 @@ dependencies = [ "serde", "serde_json", "strum", - "syn 2.0.72", + "syn 2.0.74", "tempfile", "thiserror", "tiny-keccak", @@ -3335,7 +3289,7 @@ dependencies = [ "alloy-transport", "anvil", "async-trait", - "axum 0.7.5", + "axum", "clap", "clap_complete", "clap_complete_fig", @@ -3377,7 +3331,7 @@ dependencies = [ "proptest", "rayon", "regex", - "reqwest 0.12.5", + "reqwest", "revm-inspectors", "rustc-hash", "semver 1.0.23", @@ -3411,7 +3365,7 @@ version = "0.2.0" dependencies = [ "alloy-primitives", "auto_impl", - "derive_more", + "derive_more 0.99.18", "eyre", "forge-fmt", "foundry-compilers", @@ -3502,7 +3456,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -3529,7 +3483,7 @@ dependencies = [ "itertools 0.13.0", "once_cell", "regex", - "reqwest 0.12.5", + "reqwest", "revm-primitives", "semver 1.0.23", "serde", @@ -3559,7 +3513,7 @@ dependencies = [ "alloy-json-abi", "alloy-primitives", "foundry-compilers", - "reqwest 0.12.5", + "reqwest", "semver 1.0.23", "serde", "serde_json", @@ -3690,7 +3644,7 @@ dependencies = [ "foundry-macros", "num-format", "once_cell", - "reqwest 0.12.5", + "reqwest", "rustc-hash", "semver 1.0.23", "serde", @@ -3854,7 +3808,7 @@ dependencies = [ "once_cell", "path-slash", "regex", - "reqwest 0.12.5", + "reqwest", "revm-primitives", "semver 1.0.23", "serde", @@ -3919,7 +3873,7 @@ version = "0.2.0" dependencies = [ "alloy-primitives", "alloy-sol-types", - "derive_more", + "derive_more 0.99.18", "foundry-common-fmt", "foundry-macros", "foundry-test-utils", @@ -3993,7 +3947,7 @@ dependencies = [ "foundry-evm-core", "foundry-evm-coverage", "foundry-evm-traces", - "indexmap 2.3.0", + "indexmap 2.4.0", "itertools 0.13.0", "parking_lot", "proptest", @@ -4074,7 +4028,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -4234,7 +4188,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -4273,37 +4227,22 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" -[[package]] -name = "gcemeta" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d460327b24cc34c86d53d60a90e9e6044817f7906ebd9baa5c3d0ee13e1ecf" -dependencies = [ - "bytes", - "hyper 0.14.30", - "serde", - "serde_json", - "thiserror", - "tokio", - "tracing", -] - [[package]] name = "gcloud-sdk" -version = "0.24.8" +version = "0.25.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1afe2a62202f8f8eb624638f7e5b8f0988a540dd8dbb69e098daeb277273b2ab" +checksum = "77045256cd0d2075e09d62c4c9f27c2b664e2cc806d7ddf3a4293bb0c20b4728" dependencies = [ "async-trait", + "bytes", "chrono", "futures", - "gcemeta", - "hyper 0.14.30", + "hyper 1.4.1", "jsonwebtoken", "once_cell", - "prost 0.12.6", - "prost-types 0.12.6", - "reqwest 0.11.27", + "prost", + "prost-types", + "reqwest", "secret-vault-value", "serde", "serde_json", @@ -4624,7 +4563,26 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.3.0", + "indexmap 2.4.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap 2.4.0", "slab", "tokio", "tokio-util", @@ -4684,6 +4642,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "hex" version = "0.4.3" @@ -4740,7 +4704,7 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -4843,7 +4807,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", "httparse", @@ -4866,6 +4830,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", + "h2 0.4.5", "http 1.1.0", "http-body 1.0.1", "httparse", @@ -4914,14 +4879,15 @@ dependencies = [ [[package]] name = "hyper-timeout" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" dependencies = [ - "hyper 0.14.30", + "hyper 1.4.1", + "hyper-util", "pin-project-lite", "tokio", - "tokio-io-timeout", + "tower-service", ] [[package]] @@ -5090,9 +5056,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" dependencies = [ "equivalent", "hashbrown 0.14.5", @@ -5178,11 +5144,11 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" dependencies = [ - "hermit-abi", + "hermit-abi 0.4.0", "libc", "windows-sys 0.52.0", ] @@ -5246,9 +5212,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] @@ -5604,7 +5570,7 @@ checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -5652,11 +5618,11 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", "wasi", "windows-sys 0.52.0", @@ -5686,7 +5652,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -5742,7 +5708,19 @@ checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ "bitflags 2.6.0", "cfg-if", - "cfg_aliases", + "cfg_aliases 0.1.1", + "libc", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "cfg_aliases 0.2.1", "libc", ] @@ -5902,7 +5880,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", ] @@ -5924,7 +5902,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -6036,7 +6014,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -6222,7 +6200,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -6281,7 +6259,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -6302,7 +6280,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.3.0", + "indexmap 2.4.0", ] [[package]] @@ -6365,7 +6343,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -6423,7 +6401,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -6542,7 +6520,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -6618,7 +6596,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", "version_check", "yansi", ] @@ -6630,7 +6608,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38ee68ae331824036479c84060534b18254c864fa73366c58d86db3b7b811619" dependencies = [ "futures", - "indexmap 2.3.0", + "indexmap 2.4.0", "nix 0.28.0", "tokio", "tracing", @@ -6685,16 +6663,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "prost" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" -dependencies = [ - "bytes", - "prost-derive 0.12.6", -] - [[package]] name = "prost" version = "0.13.1" @@ -6702,20 +6670,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13db3d3fde688c61e2446b4d843bc27a7e8af269a69440c0308021dc92333cc" dependencies = [ "bytes", - "prost-derive 0.13.1", -] - -[[package]] -name = "prost-derive" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" -dependencies = [ - "anyhow", - "itertools 0.12.1", - "proc-macro2", - "quote", - "syn 2.0.72", + "prost-derive", ] [[package]] @@ -6728,16 +6683,7 @@ dependencies = [ "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.72", -] - -[[package]] -name = "prost-types" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" -dependencies = [ - "prost 0.12.6", + "syn 2.0.74", ] [[package]] @@ -6746,7 +6692,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cee5168b05f49d4b0ca581206eb14a7b22fafd963efe729ac48eb03266e25cc2" dependencies = [ - "prost 0.13.1", + "prost", ] [[package]] @@ -7030,57 +6976,13 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" -[[package]] -name = "reqwest" -version = "0.11.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" -dependencies = [ - "async-compression", - "base64 0.21.7", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.30", - "hyper-rustls 0.24.2", - "ipnet", - "js-sys", - "log", - "mime", - "mime_guess", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls 0.21.12", - "rustls-native-certs 0.6.3", - "rustls-pemfile 1.0.4", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration", - "tokio", - "tokio-rustls 0.24.1", - "tokio-util", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "winreg 0.50.0", -] - [[package]] name = "reqwest" version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" dependencies = [ + "async-compression", "base64 0.22.1", "bytes", "futures-channel", @@ -7122,14 +7024,13 @@ dependencies = [ "wasm-streams", "web-sys", "webpki-roots", - "winreg 0.52.0", + "winreg", ] [[package]] name = "revm" version = "13.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b2f635bbbf4002b1b5c0219f841ec1a317723883ed7662c0d138617539a6087" +source = "git+https://github.com/bluealloy/revm?rev=228b034#228b0343f372c69924023721c00f2d0e01e72a86" dependencies = [ "auto_impl", "cfg-if", @@ -7160,8 +7061,7 @@ dependencies = [ [[package]] name = "revm-interpreter" version = "9.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ad04c7d87dc3421a5ccca76e56dbd0b29a358c03bb41fe9e80976e9d3f397d" +source = "git+https://github.com/bluealloy/revm?rev=228b034#228b0343f372c69924023721c00f2d0e01e72a86" dependencies = [ "revm-primitives", "serde", @@ -7170,8 +7070,7 @@ dependencies = [ [[package]] name = "revm-precompile" version = "10.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526a4ba5ec400e7bbe71affbc10fe2e67c1cd1fb782bab988873d09a102e271" +source = "git+https://github.com/bluealloy/revm?rev=228b034#228b0343f372c69924023721c00f2d0e01e72a86" dependencies = [ "aurora-engine-modexp", "blst", @@ -7190,8 +7089,7 @@ dependencies = [ [[package]] name = "revm-primitives" version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4093d98a26601f0a793871c5bc7928410592f76b1f03fc89fde77180c554643c" +source = "git+https://github.com/bluealloy/revm?rev=228b034#228b0343f372c69924023721c00f2d0e01e72a86" dependencies = [ "alloy-eips", "alloy-primitives", @@ -7200,7 +7098,7 @@ dependencies = [ "bitvec", "c-kzg", "cfg-if", - "derive_more", + "derive_more 0.99.18", "dyn-clone", "enumn", "hashbrown 0.14.5", @@ -7389,26 +7287,13 @@ dependencies = [ "sct", ] -[[package]] -name = "rustls" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" -dependencies = [ - "log", - "ring", - "rustls-pki-types", - "rustls-webpki 0.102.6", - "subtle", - "zeroize", -] - [[package]] name = "rustls" version = "0.23.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" dependencies = [ + "log", "once_cell", "ring", "rustls-pki-types", @@ -7560,7 +7445,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eca070c12893629e2cc820a9761bedf6ce1dcddc9852984d1dc734b8bd9bd024" dependencies = [ "cfg-if", - "derive_more", + "derive_more 0.99.18", "parity-scale-codec", "scale-info-derive", ] @@ -7579,9 +7464,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.1.12" +version = "2.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c76e6f627d67cd14a317d7909585f4d06609acafd7891432ea45ce519211a8e9" +checksum = "79da19444d9da7a9a82b80ecf059eceba6d3129d84a8610fd25ff2364f255466" dependencies = [ "sdd", ] @@ -7616,7 +7501,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -7655,9 +7540,9 @@ dependencies = [ [[package]] name = "sdd" -version = "3.0.1" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f081bcf2e6c4d1d88d2b8d1c9d8a308993eafbdabb851050be4b2ff14d2c5649" +checksum = "0495e4577c672de8254beb68d01a9b62d0e8a13c099edecdbedccce3223cd29f" [[package]] name = "sec1" @@ -7698,8 +7583,8 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc32a777b53b3433b974c9c26b6d502a50037f8da94e46cb8ce2ced2cfdfaea0" dependencies = [ - "prost 0.13.1", - "prost-types 0.13.1", + "prost", + "prost-types", "serde", "serde_json", "zeroize", @@ -7763,22 +7648,22 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.205" +version = "1.0.207" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150" +checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.205" +version = "1.0.207" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1" +checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -7789,16 +7674,16 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] name = "serde_json" -version = "1.0.122" +version = "1.0.124" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" +checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d" dependencies = [ - "indexmap 2.3.0", + "indexmap 2.4.0", "itoa", "memchr", "ryu", @@ -7833,7 +7718,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -7863,7 +7748,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.3.0", + "indexmap 2.4.0", "itoa", "ryu", "serde", @@ -7892,7 +7777,7 @@ checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -8145,7 +8030,7 @@ dependencies = [ "futures", "once_cell", "regex", - "reqwest 0.12.5", + "reqwest", "rpassword", "serde", "serde_derive", @@ -8186,7 +8071,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac" dependencies = [ "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -8252,7 +8137,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -8284,7 +8169,7 @@ dependencies = [ "dirs 5.0.1", "fs4", "once_cell", - "reqwest 0.12.5", + "reqwest", "semver 1.0.23", "serde", "serde_json", @@ -8320,9 +8205,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.72" +version = "2.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" dependencies = [ "proc-macro2", "quote", @@ -8338,7 +8223,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -8353,27 +8238,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "tap" version = "1.0.1" @@ -8461,7 +8325,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -8579,7 +8443,7 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio 1.0.1", + "mio 1.0.2", "parking_lot", "pin-project-lite", "signal-hook-registry", @@ -8588,16 +8452,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "tokio-io-timeout" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" -dependencies = [ - "pin-project-lite", - "tokio", -] - [[package]] name = "tokio-macros" version = "2.4.0" @@ -8606,7 +8460,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -8629,17 +8483,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-rustls" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" -dependencies = [ - "rustls 0.22.4", - "rustls-pki-types", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.26.0" @@ -8719,7 +8562,7 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ - "indexmap 2.3.0", + "indexmap 2.4.0", "serde", "serde_spanned", "toml_datetime", @@ -8741,7 +8584,7 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.3.0", + "indexmap 2.4.0", "toml_datetime", "winnow 0.5.40", ] @@ -8752,7 +8595,7 @@ version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ - "indexmap 2.3.0", + "indexmap 2.4.0", "serde", "serde_spanned", "toml_datetime", @@ -8761,28 +8604,30 @@ dependencies = [ [[package]] name = "tonic" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" +checksum = "38659f4a91aba8598d27821589f5db7dddd94601e7a01b1e485a50e5484c7401" dependencies = [ "async-stream", "async-trait", - "axum 0.6.20", - "base64 0.21.7", + "axum", + "base64 0.22.1", "bytes", - "h2", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.30", + "h2 0.4.5", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", "hyper-timeout", + "hyper-util", "percent-encoding", "pin-project 1.1.5", - "prost 0.12.6", + "prost", "rustls-native-certs 0.7.1", "rustls-pemfile 2.1.3", - "rustls-pki-types", + "socket2", "tokio", - "tokio-rustls 0.25.0", + "tokio-rustls 0.26.0", "tokio-stream", "tower", "tower-layer", @@ -8843,15 +8688,15 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tower-util" @@ -8885,7 +8730,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -9265,34 +9110,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" dependencies = [ "cfg-if", "js-sys", @@ -9302,9 +9148,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -9312,22 +9158,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "wasm-streams" @@ -9407,9 +9253,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", @@ -9535,7 +9381,7 @@ checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -9546,7 +9392,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -9557,7 +9403,7 @@ checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -9568,7 +9414,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -9765,16 +9611,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "winreg" version = "0.52.0" @@ -9863,7 +9699,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -9883,7 +9719,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.74", ] [[package]] @@ -9916,7 +9752,7 @@ dependencies = [ "crossbeam-utils", "displaydoc", "flate2", - "indexmap 2.3.0", + "indexmap 2.4.0", "memchr", "thiserror", "zopfli", @@ -9975,3 +9811,8 @@ dependencies = [ "cc", "pkg-config", ] + +[[patch.unused]] +name = "alloy-node-bindings" +version = "0.2.1" +source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" diff --git a/Cargo.toml b/Cargo.toml index e7ff9087f..9ca9d49b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -182,7 +182,7 @@ alloy-node-bindings = { version = "0.2.1", default-features = false } alloy-provider = { version = "0.2.1", default-features = false } alloy-pubsub = { version = "0.2.1", default-features = false } alloy-rpc-client = { version = "0.2.1", default-features = false } -alloy-rpc-types = { version = "0.2.1", default-features = false } +alloy-rpc-types = { version = "0.2.1", default-features = true } alloy-serde = { version = "0.2.1", default-features = false } alloy-signer = { version = "0.2.1", default-features = false } alloy-signer-aws = { version = "0.2.1", default-features = false } @@ -264,3 +264,29 @@ soldeer = "0.2.19" proptest = "1" comfy-table = "7" + +[patch.crates-io] +alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } +alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } +alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } +alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } +alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } +alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } +alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } +alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } +alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } +alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } +alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } +alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } +alloy-signer-gcp = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } +alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } +alloy-signer-local = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } +alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } +alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } +alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } +alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } +alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } +revm = { git = "https://github.com/bluealloy/revm", rev = "228b034" } +revm-primitives = { git = "https://github.com/bluealloy/revm", rev = "228b034" } \ No newline at end of file diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 878f4bf0f..4320b423a 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -1071,6 +1071,28 @@ impl TryFrom for TypedTransaction { tx.hash, ))) } + TxType::Eip7702 => { + let eip7702 = TxEip7702 { + chain_id: tx.chain_id.ok_or(ConversionError::MissingChainId)?, + nonce: tx.nonce, + gas_limit: tx.gas, + max_fee_per_gas: tx.gas_price.ok_or(ConversionError::MissingGasPrice)?, + max_priority_fee_per_gas: tx + .max_priority_fee_per_gas + .ok_or(ConversionError::MissingMaxPriorityFeePerGas)?, + to: tx.to.map_or(TxKind::Create, TxKind::Call), + value: tx.value, + access_list: tx.access_list.ok_or(ConversionError::MissingAccessList)?, + input: tx.input, + authorization_list: tx.authorization_list.unwrap_or_default(), + }; + let signature = tx + .signature + .ok_or(ConversionError::MissingSignature)? + .try_into() + .map_err(ConversionError::SignatureError)?; + Ok(Self::EIP7702(Signed::new_unchecked(eip7702, signature, tx.hash))) + } } } } diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index 7f8146daa..0a63ad453 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -417,7 +417,7 @@ impl ClientFork { // Since alloy doesn't indicate in the result whether the block exists, // this is being temporarily implemented in anvil. if self.predates_fork_inclusive(number) { - let receipts = self.provider().get_block_receipts(BlockNumber::Number(number)).await?; + let receipts = self.provider().get_block_receipts(BlockId::from(number)).await?; let receipts = receipts .map(|r| { r.into_iter() diff --git a/crates/anvil/src/pubsub.rs b/crates/anvil/src/pubsub.rs index 21a5c631c..b28cdba8a 100644 --- a/crates/anvil/src/pubsub.rs +++ b/crates/anvil/src/pubsub.rs @@ -3,7 +3,7 @@ use crate::{ StorageInfo, }; use alloy_primitives::{TxHash, B256}; -use alloy_rpc_types::{pubsub::SubscriptionResult, FilteredParams, Log}; +use alloy_rpc_types::{pubsub::SubscriptionResult, FilteredParams, Log, Transaction}; use anvil_core::eth::{block::Block, subscription::SubscriptionId, transaction::TypedReceipt}; use anvil_rpc::{request::Version, response::ResponseResult}; use futures::{channel::mpsc::Receiver, ready, Stream, StreamExt}; @@ -112,7 +112,7 @@ impl EthSubscription { } Self::PendingTransactions(tx, id) => { let res = ready!(tx.poll_next_unpin(cx)) - .map(SubscriptionResult::TransactionHash) + .map(SubscriptionResult::::TransactionHash) .map(to_rpc_result) .map(|result| { let params = EthSubscriptionParams { subscription: id.clone(), result }; diff --git a/crates/anvil/tests/it/eip7702.rs b/crates/anvil/tests/it/eip7702.rs index 2e7439e5d..a23feceb2 100644 --- a/crates/anvil/tests/it/eip7702.rs +++ b/crates/anvil/tests/it/eip7702.rs @@ -1,8 +1,7 @@ use crate::utils::http_provider; use alloy_consensus::{transaction::TxEip7702, SignableTransaction}; -use alloy_eips::eip7702::OptionalNonce; use alloy_network::{ReceiptResponse, TransactionBuilder, TxSignerSync}; -use alloy_primitives::{bytes, TxKind}; +use alloy_primitives::{bytes, TxKind, U256}; use alloy_provider::Provider; use alloy_rpc_types::{Authorization, TransactionRequest}; use alloy_serde::WithOtherFields; @@ -45,9 +44,9 @@ async fn can_send_eip7702_tx() { let contract = receipt.contract_address.unwrap(); let authorization = Authorization { - chain_id: 31337, + chain_id: U256::from(31337u64), address: contract, - nonce: OptionalNonce::new(Some(provider.get_transaction_count(from).await.unwrap())), + nonce: provider.get_transaction_count(from).await.unwrap(), }; let signature = wallets[0].sign_hash_sync(&authorization.signature_hash()).unwrap(); let authorization = authorization.into_signed(signature); diff --git a/crates/anvil/tests/it/traces.rs b/crates/anvil/tests/it/traces.rs index ac6f90fe3..9e09f8fcf 100644 --- a/crates/anvil/tests/it/traces.rs +++ b/crates/anvil/tests/it/traces.rs @@ -3,6 +3,7 @@ use crate::{ fork::fork_config, utils::http_provider_with_signer, }; +use alloy_eips::BlockId; use alloy_network::{EthereumWallet, TransactionBuilder}; use alloy_primitives::{hex, Address, Bytes, U256}; use alloy_provider::{ @@ -18,7 +19,7 @@ use alloy_rpc_types::{ }, parity::{Action, LocalizedTransactionTrace}, }, - BlockNumberOrTag, TransactionRequest, + TransactionRequest, }; use alloy_serde::WithOtherFields; use alloy_sol_types::sol; @@ -132,7 +133,7 @@ async fn test_transfer_debug_trace_call() { let traces = handle .http_provider() - .debug_trace_call(tx, BlockNumberOrTag::Latest, GethDebugTracingCallOptions::default()) + .debug_trace_call(tx, BlockId::latest(), GethDebugTracingCallOptions::default()) .await .unwrap(); @@ -178,7 +179,7 @@ async fn test_call_tracer_debug_trace_call() { .http_provider() .debug_trace_call( internal_call_tx.clone(), - BlockNumberOrTag::Latest, + BlockId::latest(), GethDebugTracingCallOptions::default().with_tracing_options( GethDebugTracingOptions::default() .with_tracer(GethDebugTracerType::from(GethDebugBuiltInTracerType::CallTracer)) @@ -206,7 +207,7 @@ async fn test_call_tracer_debug_trace_call() { .http_provider() .debug_trace_call( internal_call_tx.clone(), - BlockNumberOrTag::Latest, + BlockId::latest(), GethDebugTracingCallOptions::default().with_tracing_options( GethDebugTracingOptions::default() .with_tracer(GethDebugTracerType::from(GethDebugBuiltInTracerType::CallTracer)) @@ -235,7 +236,7 @@ async fn test_call_tracer_debug_trace_call() { .http_provider() .debug_trace_call( direct_call_tx, - BlockNumberOrTag::Latest, + BlockId::latest(), GethDebugTracingCallOptions::default().with_tracing_options( GethDebugTracingOptions::default() .with_tracer(GethDebugTracerType::from(GethDebugBuiltInTracerType::CallTracer)) diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 1c93afd0f..5cb05ec2b 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -89,7 +89,7 @@ impl Cheatcode for loadCall { let Self { target, slot } = *self; ensure_not_precompile!(&target, ccx); ccx.ecx.load_account(target)?; - let (val, _) = ccx.ecx.sload(target, slot.into())?; + let val = ccx.ecx.sload(target, slot.into())?; Ok(val.abi_encode()) } } @@ -590,7 +590,7 @@ impl Cheatcode for setBlockhashCall { } pub(super) fn get_nonce(ccx: &mut CheatsCtxt, address: &Address) -> Result { - let (account, _) = ccx.ecx.journaled_state.load_account(*address, &mut ccx.ecx.db)?; + let account = ccx.ecx.journaled_state.load_account(*address, &mut ccx.ecx.db)?; Ok(account.info.nonce.abi_encode()) } diff --git a/crates/cheatcodes/src/evm/mock.rs b/crates/cheatcodes/src/evm/mock.rs index 0949cbf4f..1a6ffb46a 100644 --- a/crates/cheatcodes/src/evm/mock.rs +++ b/crates/cheatcodes/src/evm/mock.rs @@ -49,7 +49,7 @@ impl Cheatcode for clearMockedCallsCall { impl Cheatcode for mockCall_0Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { callee, data, returnData } = self; - let (acc, _) = ccx.ecx.load_account(*callee)?; + let acc = ccx.ecx.load_account(*callee)?; // Etches a single byte onto the account if it is empty to circumvent the `extcodesize` // check Solidity might perform. diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index f1af752a4..02c97ac27 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -92,7 +92,6 @@ pub trait CheatcodesExecutor { db: &mut ccx.ecx.db as &mut dyn DatabaseExt, error, l1_block_info, - valid_authorizations: std::mem::take(&mut ccx.ecx.valid_authorizations), }; let mut evm = new_evm_with_existing_context(inner, &mut inspector as _); @@ -103,7 +102,6 @@ pub trait CheatcodesExecutor { ccx.ecx.env = evm.context.evm.inner.env; ccx.ecx.l1_block_info = evm.context.evm.inner.l1_block_info; ccx.ecx.error = evm.context.evm.inner.error; - ccx.ecx.valid_authorizations = evm.context.evm.inner.valid_authorizations; Ok(res) } @@ -647,7 +645,7 @@ impl Cheatcodes { crate::Vm::AccountAccessKind::Create as u8 ); if let Some(address) = outcome.address { - if let Ok((created_acc, _)) = + if let Ok(created_acc) = ecx.journaled_state.load_account(address, &mut ecx.db) { create_access.newBalance = created_acc.info.balance; @@ -888,7 +886,7 @@ impl Cheatcodes { // nonce, a non-zero KECCAK_EMPTY codehash, or non-empty code let initialized; let old_balance; - if let Ok((acc, _)) = ecx.load_account(call.target_address) { + if let Ok(acc) = ecx.load_account(call.target_address) { initialized = acc.info.exists(); old_balance = acc.info.balance; } else { @@ -1130,7 +1128,7 @@ impl Inspector for Cheatcodes { // Depending on the depth the cheat was called at, there may not be any pending // calls to update if execution has percolated up to a higher depth. if call_access.depth == ecx.journaled_state.depth() { - if let Ok((acc, _)) = ecx.load_account(call.target_address) { + if let Ok(acc) = ecx.load_account(call.target_address) { debug_assert!(access_is_call(call_access.kind)); call_access.newBalance = acc.info.balance; } @@ -1438,13 +1436,13 @@ impl Cheatcodes { let target = Address::from_word(B256::from(target)); let (initialized, old_balance) = ecx .load_account(target) - .map(|(account, _)| (account.info.exists(), account.info.balance)) + .map(|account| (account.info.exists(), account.info.balance)) .unwrap_or_default(); // load balance of this account let value = ecx .balance(interpreter.contract().target_address) - .map(|(b, _)| b) + .map(|b| b.data) .unwrap_or(U256::ZERO); // register access for the target account @@ -1479,8 +1477,8 @@ impl Cheatcodes { let mut present_value = U256::ZERO; // Try to load the account and the slot's present value if ecx.load_account(address).is_ok() { - if let Ok((previous, _)) = ecx.sload(address, key) { - present_value = previous; + if let Ok(previous) = ecx.sload(address, key) { + present_value = previous.data; } } let access = crate::Vm::StorageAccess { @@ -1503,8 +1501,8 @@ impl Cheatcodes { // not set (zero value) let mut previous_value = U256::ZERO; if ecx.load_account(address).is_ok() { - if let Ok((previous, _)) = ecx.sload(address, key) { - previous_value = previous; + if let Ok(previous) = ecx.sload(address, key) { + previous_value = previous.data; } } @@ -1532,7 +1530,7 @@ impl Cheatcodes { Address::from_word(B256::from(try_or_return!(interpreter.stack().peek(0)))); let initialized; let balance; - if let Ok((acc, _)) = ecx.load_account(address) { + if let Ok(acc) = ecx.load_account(address) { initialized = acc.info.exists(); balance = acc.info.balance; } else { diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 7c2ed11c4..f33b772af 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -1381,7 +1381,7 @@ impl DatabaseExt for Backend { for (addr, acc) in allocs.iter() { // Fetch the account from the journaled state. Will create a new account if it does // not already exist. - let (state_acc, _) = journaled_state.load_account(*addr, self)?; + let mut state_acc = journaled_state.load_account(*addr, self)?; // Set the account's bytecode and code hash, if the `bytecode` field is present. if let Some(bytecode) = acc.code.as_ref() { diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index 76a738c52..8860b1683 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -142,7 +142,7 @@ pub fn create2_handler_register>( .push((ctx.evm.journaled_state.depth(), call_inputs.clone())); // Sanity check that CREATE2 deployer exists. - let code_hash = ctx.evm.load_account(DEFAULT_CREATE2_DEPLOYER)?.0.info.code_hash; + let code_hash = ctx.evm.load_account(DEFAULT_CREATE2_DEPLOYER)?.info.code_hash; if code_hash == KECCAK_EMPTY { return Ok(FrameOrResult::Result(FrameResult::Call(CallOutcome { result: InterpreterResult { diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index c61fab4e8..a6f2f3a1f 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -504,7 +504,6 @@ impl<'a> InspectorStackRefMut<'a> { .journaled_state .load_account(caller, &mut ecx.db) .expect("failed to load caller") - .0 .info .nonce; diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 62740cce9..eb30edd29 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -3,7 +3,7 @@ use alloy_dyn_abi::{DynSolValue, JsonAbiExt, Specifier}; use alloy_json_abi::{Constructor, JsonAbi}; use alloy_network::{AnyNetwork, EthereumWallet, TransactionBuilder}; use alloy_primitives::{hex, Address, Bytes}; -use alloy_provider::{Provider, ProviderBuilder}; +use alloy_provider::{PendingTransactionError, Provider, ProviderBuilder}; use alloy_rpc_types::{AnyTransactionReceipt, TransactionRequest}; use alloy_serde::WithOtherFields; use alloy_signer::Signer; @@ -586,6 +586,12 @@ pub enum ContractDeploymentError { RpcError(#[from] TransportError), } +impl From for ContractDeploymentError { + fn from(_err: PendingTransactionError) -> Self { + Self::ContractNotDeployed + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/wallets/Cargo.toml b/crates/wallets/Cargo.toml index 89c1ed2e9..8f6b606b4 100644 --- a/crates/wallets/Cargo.toml +++ b/crates/wallets/Cargo.toml @@ -32,7 +32,7 @@ aws-sdk-kms = { version = "1", default-features = false, optional = true } # gcp-kms alloy-signer-gcp = { workspace = true, features = ["eip712"], optional = true } -gcloud-sdk = { version = "0.24", features = [ +gcloud-sdk = { version = "0.25", features = [ "google-cloud-kms-v1", "google-longrunning", ], optional = true } From 62def0d68897aae7c2172eabed8085f631625003 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Fri, 16 Aug 2024 03:48:14 +0800 Subject: [PATCH 081/184] feat: `vm.publicKeyP256` (#8679) * publicKeyP256 * clippy * fix error * update doc * update error message * add coauthor Co-authored-by: protocolwhisper --------- Co-authored-by: protocolwhisper --- Cargo.lock | 1 + crates/cheatcodes/Cargo.toml | 1 + crates/cheatcodes/assets/cheatcodes.json | 20 ++++++++++ crates/cheatcodes/spec/src/vm.rs | 4 ++ crates/cheatcodes/src/crypto.rs | 49 ++++++++++++++++-------- testdata/cheats/Vm.sol | 1 + 6 files changed, 59 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6fd1aa9cd..13450d26c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3539,6 +3539,7 @@ dependencies = [ "base64 0.22.1", "chrono", "dialoguer", + "ecdsa", "eyre", "foundry-cheatcodes-spec", "foundry-common", diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index c061a6381..8af927f34 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -54,6 +54,7 @@ jsonpath_lib.workspace = true k256.workspace = true memchr = "2.7" p256 = "0.13.2" +ecdsa = "0.16" rand = "0.8" revm.workspace = true rustc-hash.workspace = true diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 6549b31af..80c8516f6 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -6631,6 +6631,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "publicKeyP256", + "description": "Derives secp256r1 public key from the provided `privateKey`.", + "declaration": "function publicKeyP256(uint256 privateKey) external pure returns (uint256 publicKeyX, uint256 publicKeyY);", + "visibility": "external", + "mutability": "pure", + "signature": "publicKeyP256(uint256)", + "selector": "0xc453949e", + "selectorBytes": [ + 196, + 83, + 148, + 158 + ] + }, + "group": "crypto", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "randomAddress", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index a4dc63690..8e0b51ec3 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -2195,6 +2195,10 @@ interface Vm { #[cheatcode(group = Crypto)] function signP256(uint256 privateKey, bytes32 digest) external pure returns (bytes32 r, bytes32 s); + /// Derives secp256r1 public key from the provided `privateKey`. + #[cheatcode(group = Crypto)] + function publicKeyP256(uint256 privateKey) external pure returns (uint256 publicKeyX, uint256 publicKeyY); + /// Derive a private key from a provided mnenomic string (or mnenomic file path) /// at the derivation path `m/44'/60'/0'/0/{index}`. #[cheatcode(group = Crypto)] diff --git a/crates/cheatcodes/src/crypto.rs b/crates/cheatcodes/src/crypto.rs index aea346279..f080938ac 100644 --- a/crates/cheatcodes/src/crypto.rs +++ b/crates/cheatcodes/src/crypto.rs @@ -13,8 +13,7 @@ use alloy_signer_local::{ use alloy_sol_types::SolValue; use k256::{ ecdsa::SigningKey, - elliptic_curve::{sec1::ToEncodedPoint, Curve}, - Secp256k1, + elliptic_curve::{bigint::ArrayEncoding, sec1::ToEncodedPoint}, }; use p256::ecdsa::{signature::hazmat::PrehashSigner, Signature, SigningKey as P256SigningKey}; @@ -153,6 +152,18 @@ impl Cheatcode for signP256Call { } } +impl Cheatcode for publicKeyP256Call { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { privateKey } = self; + let pub_key = + parse_private_key_p256(privateKey)?.verifying_key().as_affine().to_encoded_point(false); + let pub_key_x = U256::from_be_bytes((*pub_key.x().unwrap()).into()); + let pub_key_y = U256::from_be_bytes((*pub_key.y().unwrap()).into()); + + Ok((pub_key_x, pub_key_y).abi_encode()) + } +} + /// Using a given private key, return its public ETH address, its public key affine x and y /// coordinates, and its private key (see the 'Wallet' struct) /// @@ -230,14 +241,7 @@ fn sign_with_wallet( } fn sign_p256(private_key: &U256, digest: &B256) -> Result { - ensure!(*private_key != U256::ZERO, "private key cannot be 0"); - let n = U256::from_limbs(*p256::NistP256::ORDER.as_words()); - ensure!( - *private_key < n, - format!("private key must be less than the secp256r1 curve order ({})", n), - ); - let bytes = private_key.to_be_bytes(); - let signing_key = P256SigningKey::from_bytes((&bytes).into())?; + let signing_key = parse_private_key_p256(private_key)?; let signature: Signature = signing_key.sign_prehash(digest.as_slice())?; let r_bytes: [u8; 32] = signature.r().to_bytes().into(); let s_bytes: [u8; 32] = signature.s().to_bytes().into(); @@ -245,15 +249,26 @@ fn sign_p256(private_key: &U256, digest: &B256) -> Result { Ok((r_bytes, s_bytes).abi_encode()) } -fn parse_private_key(private_key: &U256) -> Result { +fn validate_private_key(private_key: &U256) -> Result<()> { ensure!(*private_key != U256::ZERO, "private key cannot be 0"); + let order = U256::from_be_slice(&C::ORDER.to_be_byte_array()); ensure!( - *private_key < U256::from_limbs(*Secp256k1::ORDER.as_words()), - "private key must be less than the secp256k1 curve order \ - (115792089237316195423570985008687907852837564279074904382605163141518161494337)", + *private_key < U256::from_be_slice(&C::ORDER.to_be_byte_array()), + "private key must be less than the {curve:?} curve order ({order})", + curve = C::default(), ); - let bytes = private_key.to_be_bytes(); - SigningKey::from_bytes((&bytes).into()).map_err(Into::into) + + Ok(()) +} + +fn parse_private_key(private_key: &U256) -> Result { + validate_private_key::(private_key)?; + Ok(SigningKey::from_bytes((&private_key.to_be_bytes()).into())?) +} + +fn parse_private_key_p256(private_key: &U256) -> Result { + validate_private_key::(private_key)?; + Ok(P256SigningKey::from_bytes((&private_key.to_be_bytes()).into())?) } pub(super) fn parse_wallet(private_key: &U256) -> Result { @@ -328,7 +343,7 @@ mod tests { ) .unwrap(); let result = sign_p256(&pk, &digest); - assert_eq!(result.err().unwrap().to_string(), "private key must be less than the secp256r1 curve order (115792089210356248762697446949407573529996955224135760342422259061068512044369)"); + assert_eq!(result.err().unwrap().to_string(), "private key must be less than the NistP256 curve order (115792089210356248762697446949407573529996955224135760342422259061068512044369)"); } #[test] diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 0e731d88e..8004efb1d 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -327,6 +327,7 @@ interface Vm { function promptSecret(string calldata promptText) external returns (string memory input); function promptSecretUint(string calldata promptText) external returns (uint256); function promptUint(string calldata promptText) external returns (uint256); + function publicKeyP256(uint256 privateKey) external pure returns (uint256 publicKeyX, uint256 publicKeyY); function randomAddress() external returns (address); function randomUint() external returns (uint256); function randomUint(uint256 min, uint256 max) external returns (uint256); From 80fd75b657d474d66351f60e891e2b5432145e77 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sat, 17 Aug 2024 02:35:39 +0800 Subject: [PATCH 082/184] feat(cast): `cast wallet sign-auth` + `cast send --auth` (#8683) * Take 1 * update match * cast send --authorize * Add test * clippy * Fix test * fix test --------- Co-authored-by: grandizzy --- Cargo.lock | 9 +- Cargo.toml | 4 +- crates/anvil/src/eth/backend/mem/mod.rs | 3 +- crates/cast/Cargo.toml | 1 + crates/cast/bin/cmd/access_list.rs | 4 +- crates/cast/bin/cmd/call.rs | 9 +- crates/cast/bin/cmd/estimate.rs | 4 +- crates/cast/bin/cmd/mktx.rs | 2 +- crates/cast/bin/cmd/send.rs | 4 +- crates/cast/bin/cmd/wallet/mod.rs | 41 ++++++++ crates/cast/bin/tx.rs | 132 +++++++++++++++++++++--- crates/cast/tests/cli/main.rs | 43 ++++++++ crates/cli/src/opts/transaction.rs | 6 ++ crates/test-utils/src/macros.rs | 12 ++- crates/wallets/src/wallet.rs | 17 +-- 15 files changed, 242 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 13450d26c..b81c6670f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1867,6 +1867,7 @@ dependencies = [ "alloy-signer-local", "alloy-sol-types", "alloy-transport", + "anvil", "async-trait", "aws-sdk-kms", "chrono", @@ -7031,7 +7032,7 @@ dependencies = [ [[package]] name = "revm" version = "13.0.0" -source = "git+https://github.com/bluealloy/revm?rev=228b034#228b0343f372c69924023721c00f2d0e01e72a86" +source = "git+https://github.com/bluealloy/revm?rev=caadc71#caadc71fd28929cd751cc997b4b454d8813c434b" dependencies = [ "auto_impl", "cfg-if", @@ -7062,7 +7063,7 @@ dependencies = [ [[package]] name = "revm-interpreter" version = "9.0.0" -source = "git+https://github.com/bluealloy/revm?rev=228b034#228b0343f372c69924023721c00f2d0e01e72a86" +source = "git+https://github.com/bluealloy/revm?rev=caadc71#caadc71fd28929cd751cc997b4b454d8813c434b" dependencies = [ "revm-primitives", "serde", @@ -7071,7 +7072,7 @@ dependencies = [ [[package]] name = "revm-precompile" version = "10.0.0" -source = "git+https://github.com/bluealloy/revm?rev=228b034#228b0343f372c69924023721c00f2d0e01e72a86" +source = "git+https://github.com/bluealloy/revm?rev=caadc71#caadc71fd28929cd751cc997b4b454d8813c434b" dependencies = [ "aurora-engine-modexp", "blst", @@ -7090,7 +7091,7 @@ dependencies = [ [[package]] name = "revm-primitives" version = "8.0.0" -source = "git+https://github.com/bluealloy/revm?rev=228b034#228b0343f372c69924023721c00f2d0e01e72a86" +source = "git+https://github.com/bluealloy/revm?rev=caadc71#caadc71fd28929cd751cc997b4b454d8813c434b" dependencies = [ "alloy-eips", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 9ca9d49b7..e78d00492 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -288,5 +288,5 @@ alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -revm = { git = "https://github.com/bluealloy/revm", rev = "228b034" } -revm-primitives = { git = "https://github.com/bluealloy/revm", rev = "228b034" } \ No newline at end of file +revm = { git = "https://github.com/bluealloy/revm", rev = "caadc71" } +revm-primitives = { git = "https://github.com/bluealloy/revm", rev = "caadc71" } \ No newline at end of file diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 180554e83..9d21b9bf2 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -1137,6 +1137,7 @@ impl Backend { nonce, access_list, blob_versioned_hashes, + authorization_list, sidecar: _, chain_id: _, transaction_type: _, @@ -1188,7 +1189,7 @@ impl Backend { access_list: access_list.unwrap_or_default().into(), blob_hashes: blob_versioned_hashes.unwrap_or_default(), optimism: OptimismFields { enveloped_tx: Some(Bytes::new()), ..Default::default() }, - authorization_list: None, + authorization_list: authorization_list.map(Into::into), }; if env.block.basefee.is_zero() { diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 1d899bbba..edc3fbe8d 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -85,6 +85,7 @@ evmole = "0.3.1" tikv-jemallocator = { workspace = true, optional = true } [dev-dependencies] +anvil.workspace = true foundry-test-utils.workspace = true async-trait.workspace = true criterion = "0.5" diff --git a/crates/cast/bin/cmd/access_list.rs b/crates/cast/bin/cmd/access_list.rs index 2a672bffd..fcaf848ac 100644 --- a/crates/cast/bin/cmd/access_list.rs +++ b/crates/cast/bin/cmd/access_list.rs @@ -1,4 +1,4 @@ -use crate::tx::CastTxBuilder; +use crate::tx::{CastTxBuilder, SenderKind}; use alloy_primitives::TxKind; use alloy_rpc_types::BlockId; use cast::Cast; @@ -53,7 +53,7 @@ impl AccessListArgs { let config = Config::from(ð); let provider = utils::get_provider(&config)?; - let sender = eth.wallet.sender().await; + let sender = SenderKind::from_wallet_opts(eth.wallet).await?; let tx_kind = if let Some(to) = to { TxKind::Call(to.resolve(&provider).await?) diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index 4cec6f0f0..e9db0084e 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -1,4 +1,4 @@ -use crate::tx::CastTxBuilder; +use crate::tx::{CastTxBuilder, SenderKind}; use alloy_primitives::{TxKind, U256}; use alloy_rpc_types::{BlockId, BlockNumberOrTag}; use cast::{traces::TraceKind, Cast}; @@ -125,7 +125,8 @@ impl CallArgs { let mut config = Config::from(ð); let provider = utils::get_provider(&config)?; - let sender = eth.wallet.sender().await; + let sender = SenderKind::from_wallet_opts(eth.wallet).await?; + let from = sender.address(); let tx_kind = if let Some(to) = to { TxKind::Call(to.resolve(&provider).await?) @@ -181,11 +182,11 @@ impl CallArgs { let trace = match tx_kind { TxKind::Create => { - let deploy_result = executor.deploy(sender, input, value, None); + let deploy_result = executor.deploy(from, input, value, None); TraceResult::try_from(deploy_result)? } TxKind::Call(to) => TraceResult::from_raw( - executor.transact_raw(sender, to, input, value)?, + executor.transact_raw(from, to, input, value)?, TraceKind::Execution, ), }; diff --git a/crates/cast/bin/cmd/estimate.rs b/crates/cast/bin/cmd/estimate.rs index d16cfac0f..84812c8cc 100644 --- a/crates/cast/bin/cmd/estimate.rs +++ b/crates/cast/bin/cmd/estimate.rs @@ -1,4 +1,4 @@ -use crate::tx::CastTxBuilder; +use crate::tx::{CastTxBuilder, SenderKind}; use alloy_primitives::{TxKind, U256}; use alloy_provider::Provider; use alloy_rpc_types::BlockId; @@ -71,7 +71,7 @@ impl EstimateArgs { let config = Config::from(ð); let provider = utils::get_provider(&config)?; - let sender = eth.wallet.sender().await; + let sender = SenderKind::from_wallet_opts(eth.wallet).await?; let tx_kind = if let Some(to) = to { TxKind::Call(to.resolve(&provider).await?) diff --git a/crates/cast/bin/cmd/mktx.rs b/crates/cast/bin/cmd/mktx.rs index 4a343af6d..4c7a55cb1 100644 --- a/crates/cast/bin/cmd/mktx.rs +++ b/crates/cast/bin/cmd/mktx.rs @@ -101,7 +101,7 @@ impl MakeTxArgs { .with_code_sig_and_args(code, sig, args) .await? .with_blob_data(blob_data)? - .build(from) + .build(&signer) .await?; let tx = tx.build(&EthereumWallet::new(signer)).await?; diff --git a/crates/cast/bin/cmd/send.rs b/crates/cast/bin/cmd/send.rs index cdafbc8a3..82c7f15d9 100644 --- a/crates/cast/bin/cmd/send.rs +++ b/crates/cast/bin/cmd/send.rs @@ -164,13 +164,13 @@ impl SendTxArgs { tx::validate_from_address(eth.wallet.from, from)?; + let (tx, _) = builder.build(&signer).await?; + let wallet = EthereumWallet::from(signer); let provider = ProviderBuilder::<_, _, AnyNetwork>::default() .wallet(wallet) .on_provider(&provider); - let (tx, _) = builder.build(from).await?; - cast_send(provider, tx, cast_async, confirmations, to_json).await } } diff --git a/crates/cast/bin/cmd/wallet/mod.rs b/crates/cast/bin/cmd/wallet/mod.rs index 64f048b2c..893a0f0ec 100644 --- a/crates/cast/bin/cmd/wallet/mod.rs +++ b/crates/cast/bin/cmd/wallet/mod.rs @@ -1,12 +1,16 @@ +use alloy_chains::Chain; use alloy_dyn_abi::TypedData; use alloy_primitives::{hex, Address, Signature, B256}; +use alloy_provider::Provider; use alloy_signer::Signer; use alloy_signer_local::{ coins_bip39::{English, Entropy, Mnemonic}, MnemonicBuilder, PrivateKeySigner, }; +use cast::revm::primitives::{Authorization, U256}; use clap::Parser; use eyre::{Context, Result}; +use foundry_cli::{opts::RpcOpts, utils}; use foundry_common::fs; use foundry_config::Config; use foundry_wallets::{RawWalletOpts, WalletOpts, WalletSigner}; @@ -116,6 +120,25 @@ pub enum WalletSubcommands { wallet: WalletOpts, }, + /// EIP-7702 sign authorization. + #[command(visible_alias = "sa")] + SignAuth { + /// Address to sign authorization for. + address: Address, + + #[command(flatten)] + rpc: RpcOpts, + + #[arg(long)] + nonce: Option, + + #[arg(long)] + chain: Option, + + #[command(flatten)] + wallet: WalletOpts, + }, + /// Verify the signature of a message. #[command(visible_alias = "v")] Verify { @@ -327,6 +350,24 @@ impl WalletSubcommands { }; println!("0x{}", hex::encode(sig.as_bytes())); } + Self::SignAuth { rpc, nonce, chain, wallet, address } => { + let wallet = wallet.signer().await?; + let provider = utils::get_provider(&Config::from(&rpc))?; + let nonce = if let Some(nonce) = nonce { + nonce + } else { + provider.get_transaction_count(wallet.address()).await? + }; + let chain_id = if let Some(chain) = chain { + chain.id() + } else { + provider.get_chain_id().await? + }; + let auth = Authorization { chain_id: U256::from(chain_id), address, nonce }; + let signature = wallet.sign_hash(&auth.signature_hash()).await?; + let auth = auth.into_signed(signature); + println!("{}", hex::encode_prefixed(alloy_rlp::encode(&auth))); + } Self::Verify { message, signature, address } => { let recovered_address = Self::recover_address_from_message(&message, &signature)?; if address == recovered_address { diff --git a/crates/cast/bin/tx.rs b/crates/cast/bin/tx.rs index 9a731187a..f6a9cbd53 100644 --- a/crates/cast/bin/tx.rs +++ b/crates/cast/bin/tx.rs @@ -1,11 +1,14 @@ use alloy_consensus::{SidecarBuilder, SimpleCoder}; use alloy_json_abi::Function; use alloy_network::{AnyNetwork, TransactionBuilder}; -use alloy_primitives::{hex, Address, Bytes, TxKind}; +use alloy_primitives::{hex, Address, Bytes, TxKind, U256}; use alloy_provider::Provider; -use alloy_rpc_types::{TransactionInput, TransactionRequest}; +use alloy_rlp::Decodable; +use alloy_rpc_types::{Authorization, TransactionInput, TransactionRequest}; use alloy_serde::WithOtherFields; +use alloy_signer::Signer; use alloy_transport::Transport; +use cast::revm::primitives::SignedAuthorization; use eyre::Result; use foundry_cli::{ opts::TransactionOpts, @@ -13,6 +16,73 @@ use foundry_cli::{ }; use foundry_common::ens::NameOrAddress; use foundry_config::{Chain, Config}; +use foundry_wallets::{WalletOpts, WalletSigner}; + +/// Different sender kinds used by [`CastTxBuilder`]. +pub enum SenderKind<'a> { + /// An address without signer. Used for read-only calls and transactions sent through unlocked + /// accounts. + Address(Address), + /// A refersnce to a signer. + Signer(&'a WalletSigner), + /// An owned signer. + OwnedSigner(WalletSigner), +} + +impl SenderKind<'_> { + /// Resolves the name to an Ethereum Address. + pub fn address(&self) -> Address { + match self { + Self::Address(addr) => *addr, + Self::Signer(signer) => signer.address(), + Self::OwnedSigner(signer) => signer.address(), + } + } + + /// Resolves the sender from the wallet options. + /// + /// This function prefers the `from` field and may return a different address from the + /// configured signer + /// If from is specified, returns it + /// If from is not specified, but there is a signer configured, returns the signer's address + /// If from is not specified and there is no signer configured, returns zero address + pub async fn from_wallet_opts(opts: WalletOpts) -> Result { + if let Some(from) = opts.from { + Ok(from.into()) + } else if let Ok(signer) = opts.signer().await { + Ok(Self::OwnedSigner(signer)) + } else { + Ok(Address::ZERO.into()) + } + } + + /// Returns the signer if available. + pub fn as_signer(&self) -> Option<&WalletSigner> { + match self { + Self::Signer(signer) => Some(signer), + Self::OwnedSigner(signer) => Some(signer), + _ => None, + } + } +} + +impl From
for SenderKind<'_> { + fn from(addr: Address) -> Self { + Self::Address(addr) + } +} + +impl<'a> From<&'a WalletSigner> for SenderKind<'a> { + fn from(signer: &'a WalletSigner) -> Self { + Self::Signer(signer) + } +} + +impl From for SenderKind<'_> { + fn from(signer: WalletSigner) -> Self { + Self::OwnedSigner(signer) + } +} /// Prevents a misconfigured hwlib from sending a transaction that defies user-specified --from pub fn validate_from_address( @@ -76,6 +146,7 @@ pub struct CastTxBuilder { tx: WithOtherFields, legacy: bool, blob: bool, + auth: Option, chain: Chain, etherscan_api_key: Option, state: S, @@ -133,6 +204,7 @@ where blob: tx_opts.blob, chain, etherscan_api_key, + auth: tx_opts.auth, state: InitState, _t: std::marker::PhantomData, }) @@ -147,6 +219,7 @@ where blob: self.blob, chain: self.chain, etherscan_api_key: self.etherscan_api_key, + auth: self.auth, state: TxKindState { kind }, _t: self._t, } @@ -196,6 +269,7 @@ where blob: self.blob, chain: self.chain, etherscan_api_key: self.etherscan_api_key, + auth: self.auth, state: InputState { kind: self.state.kind, input, func }, _t: self._t, }) @@ -211,31 +285,32 @@ where /// to be broadcasted. pub async fn build( self, - from: impl Into, + sender: impl Into>, ) -> Result<(WithOtherFields, Option)> { - self._build(from, true).await + self._build(sender, true).await } /// Builds [TransactionRequest] without filling missing fields. Used for read-only calls such as /// eth_call, eth_estimateGas, etc pub async fn build_raw( self, - from: impl Into, + sender: impl Into>, ) -> Result<(WithOtherFields, Option)> { - self._build(from, false).await + self._build(sender, false).await } async fn _build( mut self, - from: impl Into, + sender: impl Into>, fill: bool, ) -> Result<(WithOtherFields, Option)> { - let from = from.into().resolve(&self.provider).await?; + let sender = sender.into(); + let from = sender.address(); self.tx.set_kind(self.state.kind); // we set both fields to the same value because some nodes only accept the legacy `data` field: - let input = Bytes::from(self.state.input); + let input = Bytes::copy_from_slice(&self.state.input); self.tx.input = TransactionInput { input: Some(input.clone()), data: Some(input) }; self.tx.set_from(from); @@ -269,16 +344,47 @@ where } } + let nonce = if let Some(nonce) = self.tx.nonce { + nonce + } else { + let nonce = self.provider.get_transaction_count(from).await?; + self.tx.nonce = Some(nonce); + nonce + }; + + self.resolve_auth(sender, nonce).await?; + if self.tx.gas.is_none() { self.tx.gas = Some(self.provider.estimate_gas(&self.tx).await?); } - if self.tx.nonce.is_none() { - self.tx.nonce = Some(self.provider.get_transaction_count(from).await?); - } - Ok((self.tx, self.state.func)) } + + /// Parses the passed --auth value and sets the authorization list on the transaction. + async fn resolve_auth(&mut self, sender: SenderKind<'_>, nonce: u64) -> Result<()> { + let Some(auth) = &self.auth else { return Ok(()) }; + + let auth = hex::decode(auth)?; + let auth = if let Ok(address) = Address::try_from(auth.as_slice()) { + let auth = + Authorization { chain_id: U256::from(self.chain.id()), nonce: nonce + 1, address }; + + let Some(signer) = sender.as_signer() else { + eyre::bail!("No signer available to sign authorization"); + }; + let signature = signer.sign_hash(&auth.signature_hash()).await?; + + auth.into_signed(signature) + } else if let Ok(auth) = SignedAuthorization::decode(&mut auth.as_ref()) { + auth + } else { + eyre::bail!("Failed to decode authorization"); + }; + self.tx.set_authorization_list(vec![auth]); + + Ok(()) + } } impl CastTxBuilder diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 91d04f031..d0ff5a881 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1,6 +1,7 @@ //! Contains various tests for checking cast commands use alloy_primitives::{address, b256, Address, B256}; +use anvil::{Hardfork, NodeConfig}; use foundry_test_utils::{ casttest, rpc::{next_http_rpc_endpoint, next_ws_rpc_endpoint}, @@ -146,6 +147,23 @@ casttest!(wallet_sign_typed_data_file, |_prj, cmd| { "#]]); }); +// tests that `cast wallet sign-auth message` outputs the expected signature +casttest!(wallet_sign_auth, |_prj, cmd| { + cmd.args([ + "wallet", + "sign-auth", + "--private-key", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "--nonce", + "100", + "--chain", + "1", + "0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf"]).assert_success().stdout_eq(str![[r#" +0xf85a01947e5f4552091a69125d5dfcb7b8c2659029395bdf6401a0ad489ee0314497c3f06567f3080a46a63908edc1c7cdf2ac2d609ca911212086a065a6ba951c8748dd8634740fe498efb61770097d99ff5fdcb9a863b62ea899f6 + +"#]]); +}); + // tests that `cast wallet list` outputs the local accounts casttest!(wallet_list_local_accounts, |prj, cmd| { let keystore_path = prj.root().join("keystore"); @@ -1014,3 +1032,28 @@ casttest!(block_number_hash, |_prj, cmd| { .stdout_lossy(); assert_eq!(s.trim().parse::().unwrap(), 1, "{s}") }); + +casttest!(send_eip7702, async |_prj, cmd| { + let (_api, handle) = + anvil::spawn(NodeConfig::test().with_hardfork(Some(Hardfork::PragueEOF))).await; + let endpoint = handle.http_endpoint(); + cmd.args([ + "send", + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "--auth", + "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", + "--private-key", + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + "--rpc-url", + &endpoint, + ]) + .assert_success(); + + cmd.cast_fuse() + .args(["code", "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", "--rpc-url", &endpoint]) + .assert_success() + .stdout_eq(str![[r#" +0xef010070997970c51812dc3a010c7d01b50e0d17dc79c8 + +"#]]); +}); diff --git a/crates/cli/src/opts/transaction.rs b/crates/cli/src/opts/transaction.rs index 5cf126685..c443146b5 100644 --- a/crates/cli/src/opts/transaction.rs +++ b/crates/cli/src/opts/transaction.rs @@ -56,6 +56,12 @@ pub struct TransactionOpts { /// Gas price for EIP-4844 blob transaction. #[arg(long, conflicts_with = "legacy", value_parser = parse_ether_value, env = "ETH_BLOB_GAS_PRICE", value_name = "BLOB_PRICE")] pub blob_gas_price: Option, + + /// EIP-7702 authorization list. + /// + /// Can be either a hex-encoded signed authorization or an address. + #[arg(long, conflicts_with_all = &["legacy", "blob"])] + pub auth: Option, } #[cfg(test)] diff --git a/crates/test-utils/src/macros.rs b/crates/test-utils/src/macros.rs index 1f19adffe..064a94ef2 100644 --- a/crates/test-utils/src/macros.rs +++ b/crates/test-utils/src/macros.rs @@ -63,8 +63,8 @@ macro_rules! forgetest_async { #[macro_export] macro_rules! casttest { - ($(#[$attr:meta])* $test:ident, |$prj:ident, $cmd:ident| $e:expr) => { - $crate::casttest!($(#[$attr])* $test, $crate::foundry_compilers::PathStyle::Dapptools, |$prj, $cmd| $e); + ($(#[$attr:meta])* $test:ident, $($async:ident)? |$prj:ident, $cmd:ident| $e:expr) => { + $crate::casttest!($(#[$attr])* $test, $crate::foundry_compilers::PathStyle::Dapptools, $($async)? |$prj, $cmd| $e); }; ($(#[$attr:meta])* $test:ident, $style:expr, |$prj:ident, $cmd:ident| $e:expr) => { #[test] @@ -74,6 +74,14 @@ macro_rules! casttest { $e } }; + ($(#[$attr:meta])* $test:ident, $style:expr, async |$prj:ident, $cmd:ident| $e:expr) => { + #[tokio::test(flavor = "multi_thread")] + $(#[$attr])* + async fn $test() { + let (mut $prj, mut $cmd) = $crate::util::setup_cast(stringify!($test), $style); + $e + } + }; } /// Same as `forgetest` but returns an already initialized project workspace (`forge init`) diff --git a/crates/wallets/src/wallet.rs b/crates/wallets/src/wallet.rs index ce049fe40..14a9f7422 100644 --- a/crates/wallets/src/wallet.rs +++ b/crates/wallets/src/wallet.rs @@ -1,6 +1,5 @@ use crate::{raw_wallet::RawWalletOpts, utils, wallet_signer::WalletSigner}; use alloy_primitives::Address; -use alloy_signer::Signer; use clap::Parser; use eyre::Result; use serde::Serialize; @@ -140,21 +139,6 @@ of the unlocked account you want to use, or provide the --from flag with the add Ok(signer) } - - /// This function prefers the `from` field and may return a different address from the - /// configured signer - /// If from is specified, returns it - /// If from is not specified, but there is a signer configured, returns the signer's address - /// If from is not specified and there is no signer configured, returns zero address - pub async fn sender(&self) -> Address { - if let Some(from) = self.from { - from - } else if let Ok(signer) = self.signer().await { - signer.address() - } else { - Address::ZERO - } - } } impl From for WalletOpts { @@ -165,6 +149,7 @@ impl From for WalletOpts { #[cfg(test)] mod tests { + use alloy_signer::Signer; use std::{path::Path, str::FromStr}; use super::*; From f8aa4afec04cc0b7d364a5d78f0cde9e64fd14bf Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sat, 17 Aug 2024 02:41:29 +0800 Subject: [PATCH 083/184] feat: `--alphanet` flag and config option (#8680) * --alphanet * fix tests * fix doc * anvil support * better cast and anvil * fix doc --- crates/anvil/src/cmd.rs | 5 ++ crates/anvil/src/config.rs | 16 +++++- crates/anvil/src/eth/backend/executor.rs | 3 +- crates/anvil/src/eth/backend/mem/inspector.rs | 14 +++++- crates/anvil/src/eth/backend/mem/mod.rs | 7 ++- crates/cast/bin/cmd/call.rs | 47 +++++++++++++++--- crates/cast/bin/cmd/run.rs | 49 ++++++++++++++++--- crates/common/src/evm.rs | 9 ++++ crates/config/src/lib.rs | 6 ++- crates/config/src/utils.rs | 5 +- crates/evm/core/src/lib.rs | 4 ++ crates/evm/core/src/opts.rs | 3 ++ crates/evm/core/src/precompiles.rs | 30 +++++++++++- crates/evm/core/src/utils.rs | 32 ++++++++++-- crates/evm/evm/src/executors/trace.rs | 9 ++-- crates/evm/evm/src/inspectors/stack.rs | 27 ++++++++++ crates/forge/bin/cmd/test/mod.rs | 1 + crates/forge/src/multi_runner.rs | 12 +++++ crates/forge/tests/cli/config.rs | 1 + crates/script/src/lib.rs | 4 +- crates/verify/src/bytecode.rs | 12 +++-- 21 files changed, 264 insertions(+), 32 deletions(-) diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index a2802e059..e21f12d36 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -252,6 +252,7 @@ impl NodeArgs { .with_transaction_block_keeper(self.transaction_block_keeper) .with_max_persisted_states(self.max_persisted_states) .with_optimism(self.evm_opts.optimism) + .with_alphanet(self.evm_opts.alphanet) .with_disable_default_create2_deployer(self.evm_opts.disable_default_create2_deployer) .with_slots_in_an_epoch(self.slots_in_an_epoch) .with_memory_limit(self.evm_opts.memory_limit) @@ -550,6 +551,10 @@ pub struct AnvilEvmArgs { /// The memory limit per EVM execution in bytes. #[arg(long)] pub memory_limit: Option, + + /// Enable Alphanet features + #[arg(long, visible_alias = "alphanet")] + pub alphanet: bool, } /// Resolves an alias passed as fork-url to the matching url defined in the rpc_endpoints section diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 5e8ef0f9a..a94d5b827 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -183,6 +183,8 @@ pub struct NodeConfig { pub memory_limit: Option, /// Factory used by `anvil` to extend the EVM's precompiles. pub precompile_factory: Option>, + /// Enable Alphanet features. + pub alphanet: bool, } impl NodeConfig { @@ -437,6 +439,7 @@ impl Default for NodeConfig { slots_in_an_epoch: 32, memory_limit: None, precompile_factory: None, + alphanet: false, } } } @@ -471,8 +474,11 @@ impl NodeConfig { } } - /// Returns the base fee to use + /// Returns the hardfork to use pub fn get_hardfork(&self) -> Hardfork { + if self.alphanet { + return Hardfork::PragueEOF; + } self.hardfork.unwrap_or_default() } @@ -918,6 +924,13 @@ impl NodeConfig { self } + /// Sets whether to enable Alphanet support + #[must_use] + pub fn with_alphanet(mut self, alphanet: bool) -> Self { + self.alphanet = alphanet; + self + } + /// Configures everything related to env, backend and database and returns the /// [Backend](mem::Backend) /// @@ -994,6 +1007,7 @@ impl NodeConfig { Arc::new(RwLock::new(fork)), self.enable_steps_tracing, self.print_logs, + self.alphanet, self.prune_history, self.max_persisted_states, self.transaction_block_keeper, diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index d56db9069..b46f29c38 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -105,6 +105,7 @@ pub struct TransactionExecutor<'a, Db: ?Sized, Validator: TransactionValidator> /// Cumulative blob gas used by all executed transactions pub blob_gas_used: u128, pub enable_steps_tracing: bool, + pub alphanet: bool, pub print_logs: bool, /// Precompiles to inject to the EVM. pub precompile_factory: Option>, @@ -302,7 +303,7 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator let nonce = account.nonce; // records all call and step traces - let mut inspector = Inspector::default().with_tracing(); + let mut inspector = Inspector::default().with_tracing().with_alphanet(self.alphanet); if self.enable_steps_tracing { inspector = inspector.with_steps_tracing(); } diff --git a/crates/anvil/src/eth/backend/mem/inspector.rs b/crates/anvil/src/eth/backend/mem/inspector.rs index 2095e9d50..56e53acc3 100644 --- a/crates/anvil/src/eth/backend/mem/inspector.rs +++ b/crates/anvil/src/eth/backend/mem/inspector.rs @@ -23,6 +23,8 @@ pub struct Inspector { pub tracer: Option, /// collects all `console.sol` logs pub log_collector: Option, + /// Whether to enable Alphanet support + pub alphanet: bool, } impl Inspector { @@ -57,6 +59,12 @@ impl Inspector { self.log_collector = Some(Default::default()); self } + + /// Enables Alphanet features + pub fn with_alphanet(mut self, yes: bool) -> Self { + self.alphanet = yes; + self + } } impl revm::Inspector for Inspector { @@ -168,7 +176,11 @@ impl revm::Inspector for Inspector { } } -impl InspectorExt for Inspector {} +impl InspectorExt for Inspector { + fn is_alphanet(&self) -> bool { + self.alphanet + } +} /// Prints all the logs pub fn print_logs(logs: &[Log]) { diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 9d21b9bf2..5180e4a04 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -174,6 +174,7 @@ pub struct Backend { active_snapshots: Arc>>, enable_steps_tracing: bool, print_logs: bool, + alphanet: bool, /// How to keep history state prune_state_history_config: PruneStateHistoryConfig, /// max number of blocks with transactions in memory @@ -198,6 +199,7 @@ impl Backend { fork: Arc>>, enable_steps_tracing: bool, print_logs: bool, + alphanet: bool, prune_state_history_config: PruneStateHistoryConfig, max_persisted_states: Option, transaction_block_keeper: Option, @@ -256,6 +258,7 @@ impl Backend { active_snapshots: Arc::new(Mutex::new(Default::default())), enable_steps_tracing, print_logs, + alphanet, prune_state_history_config, transaction_block_keeper, node_config, @@ -908,6 +911,7 @@ impl Backend { enable_steps_tracing: self.enable_steps_tracing, print_logs: self.print_logs, precompile_factory: self.precompile_factory.clone(), + alphanet: self.alphanet, }; // create a new pending block @@ -980,6 +984,7 @@ impl Backend { blob_gas_used: 0, enable_steps_tracing: self.enable_steps_tracing, print_logs: self.print_logs, + alphanet: self.alphanet, precompile_factory: self.precompile_factory.clone(), }; let executed_tx = executor.execute(); @@ -1203,7 +1208,7 @@ impl Backend { /// Builds [`Inspector`] with the configured options fn build_inspector(&self) -> Inspector { - let mut inspector = Inspector::default(); + let mut inspector = Inspector::default().with_alphanet(self.alphanet); if self.print_logs { inspector = inspector.with_log_collector(); diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index e9db0084e..9a553e385 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -10,7 +10,14 @@ use foundry_cli::{ }; use foundry_common::ens::NameOrAddress; use foundry_compilers::artifacts::EvmVersion; -use foundry_config::{find_project_root_path, Config}; +use foundry_config::{ + figment::{ + self, + value::{Dict, Map}, + Figment, Metadata, Profile, + }, + Config, +}; use foundry_evm::{executors::TracingExecutor, opts::EvmOpts}; use std::str::FromStr; @@ -66,6 +73,10 @@ pub struct CallArgs { #[arg(long, short, help_heading = "Display options")] json: bool, + /// Enable Alphanet features. + #[arg(long)] + pub alphanet: bool, + #[command(subcommand)] command: Option, @@ -102,6 +113,10 @@ pub enum CallSubcommands { impl CallArgs { pub async fn run(self) -> Result<()> { + let figment = Into::::into(&self.eth).merge(&self); + let evm_opts = figment.extract::()?; + let mut config = Config::try_from(figment)?.sanitized(); + let Self { to, mut sig, @@ -117,13 +132,13 @@ impl CallArgs { labels, data, json, + .. } = self; if let Some(data) = data { sig = Some(data); } - let mut config = Config::from(ð); let provider = utils::get_provider(&config)?; let sender = SenderKind::from_wallet_opts(eth.wallet).await?; let from = sender.address(); @@ -160,22 +175,20 @@ impl CallArgs { .await?; if trace { - let figment = - Config::figment_with_root(find_project_root_path(None).unwrap()).merge(eth.rpc); - let evm_opts = figment.extract::()?; if let Some(BlockId::Number(BlockNumberOrTag::Number(block_number))) = self.block { // Override Config `fork_block_number` (if set) with CLI value. config.fork_block_number = Some(block_number); } - let (mut env, fork, chain) = + let (mut env, fork, chain, alphanet) = TracingExecutor::get_fork_material(&config, evm_opts).await?; // modify settings that usually set in eth_call env.cfg.disable_block_gas_limit = true; env.block.gas_limit = U256::MAX; - let mut executor = TracingExecutor::new(env, fork, evm_version, debug, decode_internal); + let mut executor = + TracingExecutor::new(env, fork, evm_version, debug, decode_internal, alphanet); let value = tx.value.unwrap_or_default(); let input = tx.inner.input.into_input().unwrap_or_default(); @@ -202,6 +215,26 @@ impl CallArgs { } } +impl figment::Provider for CallArgs { + fn metadata(&self) -> Metadata { + Metadata::named("CallArgs") + } + + fn data(&self) -> Result, figment::Error> { + let mut map = Map::new(); + + if self.alphanet { + map.insert("alphanet".into(), self.alphanet.into()); + } + + if let Some(evm_version) = self.evm_version { + map.insert("evm_version".into(), figment::value::Value::serialize(evm_version)?); + } + + Ok(Map::from([(Config::selected_profile(), map)])) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index b7f31cfc7..7a5e7980c 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -10,7 +10,14 @@ use foundry_cli::{ }; use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE}; use foundry_compilers::artifacts::EvmVersion; -use foundry_config::{find_project_root_path, Config}; +use foundry_config::{ + figment::{ + self, + value::{Dict, Map}, + Figment, Metadata, Profile, + }, + Config, +}; use foundry_evm::{ executors::{EvmError, TracingExecutor}, opts::EvmOpts, @@ -75,6 +82,10 @@ pub struct RunArgs { /// See also, https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second #[arg(long, value_name = "NO_RATE_LIMITS", visible_alias = "no-rpc-rate-limit")] pub no_rate_limit: bool, + + /// Enables Alphanet features. + #[arg(long)] + pub alphanet: bool, } impl RunArgs { @@ -84,8 +95,7 @@ impl RunArgs { /// /// Note: This executes the transaction(s) as is: Cheatcodes are disabled pub async fn run(self) -> Result<()> { - let figment = - Config::figment_with_root(find_project_root_path(None).unwrap()).merge(self.rpc); + let figment = Into::::into(&self.rpc).merge(&self); let evm_opts = figment.extract::()?; let mut config = Config::try_from(figment)?.sanitized(); @@ -122,7 +132,8 @@ impl RunArgs { // we need to fork off the parent block config.fork_block_number = Some(tx_block_number - 1); - let (mut env, fork, chain) = TracingExecutor::get_fork_material(&config, evm_opts).await?; + let (mut env, fork, chain, alphanet) = + TracingExecutor::get_fork_material(&config, evm_opts).await?; let mut evm_version = self.evm_version; @@ -146,8 +157,14 @@ impl RunArgs { } } - let mut executor = - TracingExecutor::new(env.clone(), fork, evm_version, self.debug, self.decode_internal); + let mut executor = TracingExecutor::new( + env.clone(), + fork, + evm_version, + self.debug, + self.decode_internal, + alphanet, + ); let mut env = EnvWithHandlerCfg::new_with_spec_id(Box::new(env.clone()), executor.spec_id()); @@ -230,3 +247,23 @@ impl RunArgs { Ok(()) } } + +impl figment::Provider for RunArgs { + fn metadata(&self) -> Metadata { + Metadata::named("RunArgs") + } + + fn data(&self) -> Result, figment::Error> { + let mut map = Map::new(); + + if self.alphanet { + map.insert("alphanet".into(), self.alphanet.into()); + } + + if let Some(evm_version) = self.evm_version { + map.insert("evm_version".into(), figment::value::Value::serialize(evm_version)?); + } + + Ok(Map::from([(Config::selected_profile(), map)])) + } +} diff --git a/crates/common/src/evm.rs b/crates/common/src/evm.rs index 98355c890..43f2d5c53 100644 --- a/crates/common/src/evm.rs +++ b/crates/common/src/evm.rs @@ -144,6 +144,11 @@ pub struct EvmArgs { #[arg(long)] #[serde(skip)] pub isolate: bool, + + /// Whether to enable Alphanet features. + #[arg(long)] + #[serde(skip)] + pub alphanet: bool, } // Make this set of options a `figment::Provider` so that it can be merged into the `Config` @@ -170,6 +175,10 @@ impl Provider for EvmArgs { dict.insert("isolate".to_string(), self.isolate.into()); } + if self.alphanet { + dict.insert("alphanet".to_string(), self.alphanet.into()); + } + if self.always_use_create_2_factory { dict.insert( "always_use_create_2_factory".to_string(), diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 7033cc31a..ca7226e18 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -447,6 +447,9 @@ pub struct Config { #[serde(default, skip_serializing_if = "Option::is_none")] pub eof_version: Option, + /// Whether to enable Alphanet features. + pub alphanet: bool, + /// Warnings gathered when loading the Config. See [`WarningsProvider`] for more information #[serde(rename = "__warnings", default, skip_serializing)] pub warnings: Vec, @@ -910,7 +913,7 @@ impl Config { /// Returns the [SpecId] derived from the configured [EvmVersion] #[inline] pub fn evm_spec_id(&self) -> SpecId { - evm_spec_id(&self.evm_version) + evm_spec_id(&self.evm_version, self.alphanet) } /// Returns whether the compiler version should be auto-detected @@ -2139,6 +2142,7 @@ impl Default for Config { warnings: vec![], extra_args: vec![], eof_version: None, + alphanet: false, _non_exhaustive: (), } } diff --git a/crates/config/src/utils.rs b/crates/config/src/utils.rs index d8d23711c..dff36e4fd 100644 --- a/crates/config/src/utils.rs +++ b/crates/config/src/utils.rs @@ -296,7 +296,10 @@ impl FromStr for Numeric { /// Returns the [SpecId] derived from [EvmVersion] #[inline] -pub fn evm_spec_id(evm_version: &EvmVersion) -> SpecId { +pub fn evm_spec_id(evm_version: &EvmVersion, alphanet: bool) -> SpecId { + if alphanet { + return SpecId::PRAGUE_EOF; + } match evm_version { EvmVersion::Homestead => SpecId::HOMESTEAD, EvmVersion::TangerineWhistle => SpecId::TANGERINE, diff --git a/crates/evm/core/src/lib.rs b/crates/evm/core/src/lib.rs index ed10c5d75..26b392c43 100644 --- a/crates/evm/core/src/lib.rs +++ b/crates/evm/core/src/lib.rs @@ -47,6 +47,10 @@ pub trait InspectorExt: Inspector { // Simulates `console.log` invocation. fn console_log(&mut self, _input: String) {} + + fn is_alphanet(&self) -> bool { + false + } } impl InspectorExt for NoOpInspector {} diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index d676c90b3..f9e674e09 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -63,6 +63,9 @@ pub struct EvmOpts { /// Whether to disable block gas limit checks. pub disable_block_gas_limit: bool, + + /// whether to enable Alphanet features. + pub alphanet: bool, } impl EvmOpts { diff --git a/crates/evm/core/src/precompiles.rs b/crates/evm/core/src/precompiles.rs index 03ab18dff..2544258d3 100644 --- a/crates/evm/core/src/precompiles.rs +++ b/crates/evm/core/src/precompiles.rs @@ -1,4 +1,8 @@ -use alloy_primitives::{address, Address}; +use alloy_primitives::{address, Address, Bytes, B256}; +use revm::{ + precompile::{secp256r1::p256_verify as revm_p256_verify, PrecompileWithAddress}, + primitives::{Precompile, PrecompileResult}, +}; /// The ECRecover precompile address. pub const EC_RECOVER: Address = address!("0000000000000000000000000000000000000001"); @@ -42,4 +46,28 @@ pub const PRECOMPILES: &[Address] = &[ EC_PAIRING, BLAKE_2F, POINT_EVALUATION, + ALPHANET_P256_ADDRESS, ]; + +/// [EIP-7212](https://eips.ethereum.org/EIPS/eip-7212) secp256r1 precompile address on Alphanet. +/// +/// +pub const ALPHANET_P256_ADDRESS: Address = address!("0000000000000000000000000000000000000014"); + +/// Wrapper around revm P256 precompile, matching EIP-7212 spec. +/// +/// Per Optimism implementation, P256 precompile returns empty bytes on failure, but per EIP-7212 it +/// should be 32 bytes of zeros instead. +pub fn p256_verify(input: &Bytes, gas_limit: u64) -> PrecompileResult { + revm_p256_verify(input, gas_limit).map(|mut result| { + if result.bytes.is_empty() { + result.bytes = B256::default().into(); + } + + result + }) +} + +/// [EIP-7212](https://eips.ethereum.org/EIPS/eip-7212#specification) secp256r1 precompile. +pub const ALPHANET_P256: PrecompileWithAddress = + PrecompileWithAddress(ALPHANET_P256_ADDRESS, Precompile::Standard(p256_verify)); diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index 8860b1683..21284e780 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -1,5 +1,5 @@ pub use crate::ic::*; -use crate::{constants::DEFAULT_CREATE2_DEPLOYER, InspectorExt}; +use crate::{constants::DEFAULT_CREATE2_DEPLOYER, precompiles::ALPHANET_P256, InspectorExt}; use alloy_json_abi::{Function, JsonAbi}; use alloy_primitives::{Address, Selector, TxKind, U256}; use alloy_rpc_types::{Block, Transaction}; @@ -207,6 +207,20 @@ pub fn create2_handler_register>( }); } +/// Adds Alphanet P256 precompile to the list of loaded precompiles. +pub fn alphanet_handler_register>( + handler: &mut EvmHandler<'_, I, DB>, +) { + let prev = handler.pre_execution.load_precompiles.clone(); + handler.pre_execution.load_precompiles = Arc::new(move || { + let mut loaded_precompiles = prev(); + + loaded_precompiles.extend([ALPHANET_P256]); + + loaded_precompiles + }); +} + /// Creates a new EVM with the given inspector. pub fn new_evm_with_inspector<'a, DB, I>( db: DB, @@ -232,10 +246,15 @@ where .build() */ - let context = revm::Context::new(revm::EvmContext::new_with_env(db, env), inspector); let mut handler = revm::Handler::new(handler_cfg); handler.append_handler_register_plain(revm::inspector_handle_register); + if inspector.is_alphanet() { + handler.append_handler_register_plain(alphanet_handler_register); + } handler.append_handler_register_plain(create2_handler_register); + + let context = revm::Context::new(revm::EvmContext::new_with_env(db, env), inspector); + revm::Evm::new(context, handler) } @@ -261,11 +280,16 @@ where I: InspectorExt, { let handler_cfg = HandlerCfg::new(inner.spec_id()); - let context = - revm::Context::new(revm::EvmContext { inner, precompiles: Default::default() }, inspector); + let mut handler = revm::Handler::new(handler_cfg); handler.append_handler_register_plain(revm::inspector_handle_register); + if inspector.is_alphanet() { + handler.append_handler_register_plain(alphanet_handler_register); + } handler.append_handler_register_plain(create2_handler_register); + + let context = + revm::Context::new(revm::EvmContext { inner, precompiles: Default::default() }, inspector); revm::Evm::new(context, handler) } diff --git a/crates/evm/evm/src/executors/trace.rs b/crates/evm/evm/src/executors/trace.rs index cc3e68776..69c68442b 100644 --- a/crates/evm/evm/src/executors/trace.rs +++ b/crates/evm/evm/src/executors/trace.rs @@ -18,6 +18,7 @@ impl TracingExecutor { version: Option, debug: bool, decode_internal: bool, + alphanet: bool, ) -> Self { let db = Backend::spawn(fork); let trace_mode = @@ -30,8 +31,8 @@ impl TracingExecutor { // configures a bare version of the evm executor: no cheatcode inspector is enabled, // tracing will be enabled only for the targeted transaction executor: ExecutorBuilder::new() - .inspectors(|stack| stack.trace_mode(trace_mode)) - .spec(evm_spec_id(&version.unwrap_or_default())) + .inspectors(|stack| stack.trace_mode(trace_mode).alphanet(alphanet)) + .spec(evm_spec_id(&version.unwrap_or_default(), alphanet)) .build(env, db), } } @@ -45,7 +46,7 @@ impl TracingExecutor { pub async fn get_fork_material( config: &Config, mut evm_opts: EvmOpts, - ) -> eyre::Result<(Env, Option, Option)> { + ) -> eyre::Result<(Env, Option, Option, bool)> { evm_opts.fork_url = Some(config.get_rpc_url_or_localhost_http()?.into_owned()); evm_opts.fork_block_number = config.fork_block_number; @@ -53,7 +54,7 @@ impl TracingExecutor { let fork = evm_opts.get_fork(config, env.clone()); - Ok((env, fork, evm_opts.get_remote_chain_id().await)) + Ok((env, fork, evm_opts.get_remote_chain_id().await, evm_opts.alphanet)) } } diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index a6f2f3a1f..719998365 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -58,6 +58,8 @@ pub struct InspectorStackBuilder { /// 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. pub enable_isolation: bool, + /// Whether to enable Alphanet features. + pub alphanet: bool, } impl InspectorStackBuilder { @@ -140,6 +142,14 @@ impl InspectorStackBuilder { self } + /// Set whether to enable Alphanet features. + /// For description of call isolation, see [`InspectorStack::enable_isolation`]. + #[inline] + pub fn alphanet(mut self, yes: bool) -> Self { + self.alphanet = yes; + self + } + /// Builds the stack of inspectors to use when transacting/committing on the EVM. pub fn build(self) -> InspectorStack { let Self { @@ -153,6 +163,7 @@ impl InspectorStackBuilder { print, chisel_state, enable_isolation, + alphanet, } = self; let mut stack = InspectorStack::new(); @@ -172,6 +183,7 @@ impl InspectorStackBuilder { stack.tracing(trace_mode); stack.enable_isolation(enable_isolation); + stack.alphanet(alphanet); // environment, must come after all of the inspectors if let Some(block) = block { @@ -284,6 +296,7 @@ pub struct InspectorStackInner { pub printer: Option, pub tracer: Option, pub enable_isolation: bool, + pub alphanet: bool, /// Flag marking if we are in the inner EVM context. pub in_inner_context: bool, @@ -392,6 +405,12 @@ impl InspectorStack { self.enable_isolation = yes; } + /// Set whether to enable call isolation. + #[inline] + pub fn alphanet(&mut self, yes: bool) { + self.alphanet = yes; + } + /// Set whether to enable the log collector. #[inline] pub fn collect_logs(&mut self, yes: bool) { @@ -914,6 +933,10 @@ impl<'a, DB: DatabaseExt> InspectorExt for InspectorStackRefMut<'a> { inspector, input )); } + + fn is_alphanet(&self) -> bool { + self.inner.alphanet + } } impl Inspector for InspectorStack { @@ -999,6 +1022,10 @@ impl InspectorExt for InspectorStack { ) -> bool { self.as_mut().should_use_create2_factory(ecx, inputs) } + + fn is_alphanet(&self) -> bool { + self.alphanet + } } impl<'a> Deref for InspectorStackRefMut<'a> { diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 248d676db..ab868f5bc 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -336,6 +336,7 @@ impl TestArgs { .with_fork(evm_opts.get_fork(&config, env.clone())) .with_test_options(test_options) .enable_isolation(evm_opts.isolate) + .alphanet(evm_opts.alphanet) .build(project_root, &output, env, evm_opts)?; let mut maybe_override_mt = |flag, maybe_regex: Option<&Regex>| { diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 9ac3069a6..4df00f794 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -72,6 +72,8 @@ pub struct MultiContractRunner { pub test_options: TestOptions, /// Whether to enable call isolation pub isolation: bool, + /// Whether to enable Alphanet features. + pub alphanet: bool, /// Known contracts linked with computed library addresses. pub known_contracts: ContractsByArtifact, /// Libraries to deploy. @@ -256,6 +258,7 @@ impl MultiContractRunner { .trace_mode(trace_mode) .coverage(self.coverage) .enable_isolation(self.isolation) + .alphanet(self.alphanet) }) .spec(self.evm_spec) .gas_limit(self.evm_opts.gas_limit()) @@ -315,6 +318,8 @@ pub struct MultiContractRunnerBuilder { pub decode_internal: InternalTraceMode, /// Whether to enable call isolation pub isolation: bool, + /// Whether to enable Alphanet features. + pub alphanet: bool, /// Settings related to fuzz and/or invariant tests pub test_options: Option, } @@ -332,6 +337,7 @@ impl MultiContractRunnerBuilder { isolation: Default::default(), test_options: Default::default(), decode_internal: Default::default(), + alphanet: Default::default(), } } @@ -380,6 +386,11 @@ impl MultiContractRunnerBuilder { self } + pub fn alphanet(mut self, enable: bool) -> Self { + self.alphanet = enable; + self + } + /// Given an EVM, proceeds to return a runner which is able to execute all tests /// against that evm pub fn build( @@ -448,6 +459,7 @@ impl MultiContractRunnerBuilder { decode_internal: self.decode_internal, test_options: self.test_options.unwrap_or_default(), isolation: self.isolation, + alphanet: self.alphanet, known_contracts, libs_to_deploy, libraries, diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index fcec5f15a..0e38e15e7 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -148,6 +148,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { legacy_assertions: false, extra_args: vec![], eof_version: None, + alphanet: false, _non_exhaustive: (), }; prj.write_config(input.clone()); diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index e2f86ce3b..1b2618f91 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -580,7 +580,9 @@ impl ScriptConfig { // We need to enable tracing to decode contract names: local or external. let mut builder = ExecutorBuilder::new() .inspectors(|stack| { - stack.trace_mode(if debug { TraceMode::Debug } else { TraceMode::Call }) + stack + .trace_mode(if debug { TraceMode::Debug } else { TraceMode::Call }) + .alphanet(self.evm_opts.alphanet) }) .spec(self.config.evm_spec_id()) .gas_limit(self.evm_opts.gas_limit()) diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index aa3ffbe0e..11da12c43 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -322,11 +322,17 @@ impl VerifyBytecodeArgs { fork_config.fork_block_number = Some(simulation_block - 1); fork_config.evm_version = etherscan_metadata.evm_version()?.unwrap_or(EvmVersion::default()); - let (mut env, fork, _chain) = + let (mut env, fork, _chain, alphanet) = TracingExecutor::get_fork_material(&fork_config, evm_opts).await?; - let mut executor = - TracingExecutor::new(env.clone(), fork, Some(fork_config.evm_version), false, false); + let mut executor = TracingExecutor::new( + env.clone(), + fork, + Some(fork_config.evm_version), + false, + false, + alphanet, + ); env.block.number = U256::from(simulation_block); let block = provider.get_block(simulation_block.into(), true.into()).await?; From 0bb5864d86096410a0d2c4d511f825caebb8c480 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sun, 18 Aug 2024 00:53:01 +0800 Subject: [PATCH 084/184] fix: update formatting for fixed bytes (#8687) * fix: update formatting for fixed bytes * fix: update formatting for fixed bytes --- Cargo.lock | 5 ----- Cargo.toml | 2 -- crates/common/fmt/src/dynamic.rs | 4 +++- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b81c6670f..495ede825 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9813,8 +9813,3 @@ dependencies = [ "cc", "pkg-config", ] - -[[patch.unused]] -name = "alloy-node-bindings" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" diff --git a/Cargo.toml b/Cargo.toml index e78d00492..238bc38bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -178,7 +178,6 @@ alloy-eips = { version = "0.2.1", default-features = false } alloy-genesis = { version = "0.2.1", default-features = false } alloy-json-rpc = { version = "0.2.1", default-features = false } alloy-network = { version = "0.2.1", default-features = false } -alloy-node-bindings = { version = "0.2.1", default-features = false } alloy-provider = { version = "0.2.1", default-features = false } alloy-pubsub = { version = "0.2.1", default-features = false } alloy-rpc-client = { version = "0.2.1", default-features = false } @@ -272,7 +271,6 @@ alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } diff --git a/crates/common/fmt/src/dynamic.rs b/crates/common/fmt/src/dynamic.rs index 4c59989e9..e13d6ab96 100644 --- a/crates/common/fmt/src/dynamic.rs +++ b/crates/common/fmt/src/dynamic.rs @@ -15,7 +15,9 @@ impl DynValueFormatter { DynSolValue::Address(inner) => write!(f, "{inner}"), DynSolValue::Function(inner) => write!(f, "{inner}"), DynSolValue::Bytes(inner) => f.write_str(&hex::encode_prefixed(inner)), - DynSolValue::FixedBytes(inner, _) => write!(f, "{inner}"), + DynSolValue::FixedBytes(word, size) => { + f.write_str(&hex::encode_prefixed(&word[..*size])) + } DynSolValue::Uint(inner, _) => { if self.raw { write!(f, "{inner}") From 8549aadc66bac1b480a303e7dc3fb9309dffb325 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 18 Aug 2024 00:50:13 +0000 Subject: [PATCH 085/184] chore(deps): weekly `cargo update` (#8690) --- Cargo.lock | 192 ++++++++++++++++++++++++++--------------------------- 1 file changed, 96 insertions(+), 96 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 495ede825..207ee83d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -316,7 +316,7 @@ checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -551,7 +551,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -568,7 +568,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", "syn-solidity", "tiny-keccak", ] @@ -586,7 +586,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.74", + "syn 2.0.75", "syn-solidity", ] @@ -1051,9 +1051,9 @@ dependencies = [ [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "ascii-canvas" @@ -1094,7 +1094,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -1116,7 +1116,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -1127,7 +1127,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -1180,7 +1180,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -1257,9 +1257,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.38.0" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d073fcc95d01301c115011f8f23bc436d66f01b8265d149e994a2d8318c903c" +checksum = "70ebbbc319551583b9233a74b359ede7349102e779fc12371d2478e80b50d218" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1279,9 +1279,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.37.0" +version = "1.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1074e818fbe4f9169242d78448b15be8916a79daa38ea1231f2e2e10d993fcd2" +checksum = "11822090cf501c316c6f75711d77b96fba30658e3867a7762e5e2f5d32d31e81" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1301,9 +1301,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.38.0" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29755c51e33fa3f678598f64324a169cf4b7d3c4865d2709d4308f53366a92a4" +checksum = "78a2a06ff89176123945d1bbe865603c4d7101bea216a550bb4d2e4e9ba74d74" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1323,9 +1323,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.37.0" +version = "1.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e52dc3fd7dfa6c01a69cf3903e00aa467261639138a05b06cd92314d2c8fb07" +checksum = "a20a91795850826a6f456f4a48eff1dfa59a0e69bdbf5b8c50518fd372106574" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1419,9 +1419,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.6.2" +version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce87155eba55e11768b8c1afa607f3e864ae82f03caf63258b37455b0ad02537" +checksum = "0abbf454960d0db2ad12684a1640120e7557294b0ff8e2f11236290a1b293225" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -1463,9 +1463,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.2.0" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe321a6b21f5d8eabd0ade9c55d3d0335f3c3157fc2b3e87f05f34b539e4df5" +checksum = "6cee7cadb433c781d3299b916fbf620fea813bf38f49db282fb6858141a05cc8" dependencies = [ "base64-simd", "bytes", @@ -1745,9 +1745,9 @@ checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "bytemuck" -version = "1.16.3" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83" +checksum = "6fd4c6dcc3b0aea2f5c0b4b82c2b15fe39ddbc76041a310848f4706edf76bb31" [[package]] name = "byteorder" @@ -1811,9 +1811,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" +checksum = "3054fea8a20d8ff3968d5b22cc27501d2b08dc4decdb31b184323f00c5ef23bb" dependencies = [ "serde", ] @@ -1923,9 +1923,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb8dd288a69fc53a1996d7ecfbf4a20d59065bff137ce7e56bbd620de191189" +checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" dependencies = [ "jobserver", "libc", @@ -2041,9 +2041,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.15" +version = "4.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d8838454fda655dafd3accb2b6e2bea645b9e4078abe84a22ceb947235c5cc" +checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" dependencies = [ "clap_builder", "clap_derive", @@ -2066,9 +2066,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.16" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c677cd0126f3026d8b093fa29eae5d812fde5c05bc66dbb29d0374eea95113a" +checksum = "1ee158892bd7ce77aa15c208abbdb73e155d191c287a659b57abd5adb92feb03" dependencies = [ "clap", ] @@ -2092,7 +2092,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -2519,7 +2519,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -2530,7 +2530,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -2603,7 +2603,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -2624,7 +2624,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -2634,7 +2634,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -2647,7 +2647,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.0", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -2667,7 +2667,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -2774,7 +2774,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -2901,7 +2901,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -3051,7 +3051,7 @@ dependencies = [ "regex", "serde", "serde_json", - "syn 2.0.74", + "syn 2.0.75", "toml 0.8.19", "walkdir", ] @@ -3079,7 +3079,7 @@ dependencies = [ "serde", "serde_json", "strum", - "syn 2.0.74", + "syn 2.0.75", "tempfile", "thiserror", "tiny-keccak", @@ -3457,7 +3457,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -4030,7 +4030,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -4190,7 +4190,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -4706,7 +4706,7 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -5356,9 +5356,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.155" +version = "0.2.156" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a" [[package]] name = "libdbus-sys" @@ -5572,7 +5572,7 @@ checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -5654,7 +5654,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -5904,7 +5904,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -6016,7 +6016,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -6202,7 +6202,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -6261,7 +6261,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -6345,7 +6345,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -6403,7 +6403,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -6522,7 +6522,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -6598,7 +6598,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", "version_check", "yansi", ] @@ -6685,7 +6685,7 @@ dependencies = [ "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -7466,9 +7466,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.1.14" +version = "2.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79da19444d9da7a9a82b80ecf059eceba6d3129d84a8610fd25ff2364f255466" +checksum = "aeb7ac86243095b70a7920639507b71d51a63390d1ba26c4f60a552fbb914a37" dependencies = [ "sdd", ] @@ -7503,7 +7503,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -7650,22 +7650,22 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.207" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2" +checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.207" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e" +checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -7676,14 +7676,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] name = "serde_json" -version = "1.0.124" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d" +checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" dependencies = [ "indexmap 2.4.0", "itoa", @@ -7720,7 +7720,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -7779,7 +7779,7 @@ checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -7974,9 +7974,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "snapbox" -version = "0.6.16" +version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "027c936207f85d10d015e21faf5c676c7e08c453ed371adf55c0874c443ca77a" +checksum = "840b73eb3148bc3cbc10ebe00ec9bc6d96033e658d022c4adcbf3f35596fd64a" dependencies = [ "anstream", "anstyle", @@ -8073,7 +8073,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac" dependencies = [ "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -8139,7 +8139,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -8207,9 +8207,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.74" +version = "2.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" dependencies = [ "proc-macro2", "quote", @@ -8225,7 +8225,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -8327,7 +8327,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -8438,9 +8438,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.2" +version = "1.39.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" +checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" dependencies = [ "backtrace", "bytes", @@ -8462,7 +8462,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -8732,7 +8732,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -9132,7 +9132,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", "wasm-bindgen-shared", ] @@ -9166,7 +9166,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -9274,9 +9274,9 @@ dependencies = [ [[package]] name = "which" -version = "6.0.2" +version = "6.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d9c5ed668ee1f17edb3b627225343d210006a90bb1e3745ce1f30b1fb115075" +checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f" dependencies = [ "either", "home", @@ -9383,7 +9383,7 @@ checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -9394,7 +9394,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -9405,7 +9405,7 @@ checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -9416,7 +9416,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -9701,7 +9701,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -9721,7 +9721,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] From c0a27282604ea5de1ff3d4ce415fe628ccb1ea51 Mon Sep 17 00:00:00 2001 From: greged93 <82421016+greged93@users.noreply.github.com> Date: Mon, 19 Aug 2024 10:51:40 -0700 Subject: [PATCH 086/184] fix(anvil): storage diff (#8691) fix the inspector for steps tracing --- crates/anvil/src/eth/backend/executor.rs | 2 +- crates/anvil/src/eth/backend/mem/inspector.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index b46f29c38..c84ad5200 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -81,7 +81,7 @@ impl ExecutedTransaction { pub struct ExecutedTransactions { /// The block created after executing the `included` transactions pub block: BlockInfo, - /// All transactions included in the + /// All transactions included in the block pub included: Vec>, /// All transactions that were invalid at the point of their execution and were not included in /// the block diff --git a/crates/anvil/src/eth/backend/mem/inspector.rs b/crates/anvil/src/eth/backend/mem/inspector.rs index 56e53acc3..b354a9a5c 100644 --- a/crates/anvil/src/eth/backend/mem/inspector.rs +++ b/crates/anvil/src/eth/backend/mem/inspector.rs @@ -50,7 +50,7 @@ impl Inspector { /// Enables steps recording for `Tracer`. pub fn with_steps_tracing(mut self) -> Self { - self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all())); + self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().with_state_diffs())); self } From 1710187c614f01598116e67aaf4cda76e7b532ec Mon Sep 17 00:00:00 2001 From: zhiqiangxu <652732310@qq.com> Date: Tue, 20 Aug 2024 04:15:46 +0800 Subject: [PATCH 087/184] fix comment of `--sender` (#8692) mod comment of --sender --- crates/common/src/evm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/common/src/evm.rs b/crates/common/src/evm.rs index 43f2d5c53..d7f026552 100644 --- a/crates/common/src/evm.rs +++ b/crates/common/src/evm.rs @@ -85,7 +85,7 @@ pub struct EvmArgs { #[serde(skip_serializing_if = "Option::is_none")] pub initial_balance: Option, - /// The address which will be executing tests. + /// The address which will be executing tests/scripts. #[arg(long, value_name = "ADDRESS")] #[serde(skip_serializing_if = "Option::is_none")] pub sender: Option
, From afd86803fc89a95e05427f82b9fbeae0a7c7b049 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 21 Aug 2024 02:41:07 +0800 Subject: [PATCH 088/184] fix: console.log formatting (#8702) * fix: console.log formatting * no deref * clippy --- crates/common/fmt/src/console.rs | 110 ++++++++++++++++--------------- 1 file changed, 58 insertions(+), 52 deletions(-) diff --git a/crates/common/fmt/src/console.rs b/crates/common/fmt/src/console.rs index 45f12ffbf..7473ccdec 100644 --- a/crates/common/fmt/src/console.rs +++ b/crates/common/fmt/src/console.rs @@ -21,17 +21,17 @@ pub enum FormatSpec { } impl FormatSpec { - fn from_chars(iter: &mut Peekable) -> Option + fn from_chars(iter: &mut Peekable) -> Result where I: Iterator, { - match iter.next()? { - 's' => Some(Self::String), - 'd' => Some(Self::Number), - 'i' => Some(Self::Integer), - 'o' => Some(Self::Object), - 'e' => Some(Self::Exponential(None)), - 'x' => Some(Self::Hexadecimal), + match iter.next().ok_or_else(String::new)? { + 's' => Ok(Self::String), + 'd' => Ok(Self::Number), + 'i' => Ok(Self::Integer), + 'o' => Ok(Self::Object), + 'e' => Ok(Self::Exponential(None)), + 'x' => Ok(Self::Hexadecimal), ch if ch.is_ascii_digit() => { let mut num = ch.to_string(); while let Some(&ch) = iter.peek() { @@ -44,16 +44,17 @@ impl FormatSpec { } if let Some(&ch) = iter.peek() { if ch == 'e' { + let num = num.parse().map_err(|_| num)?; iter.next(); - Some(Self::Exponential(Some(num.parse().ok()?))) + Ok(Self::Exponential(Some(num))) } else { - None + Err(num) } } else { - None + Err(num) } } - _ => None, + ch => Err(String::from(ch)), } } } @@ -248,29 +249,25 @@ impl ConsoleFmt for [u8] { /// assert_eq!(formatted, "foo has 3 characters"); /// ``` pub fn console_format(spec: &str, values: &[&dyn ConsoleFmt]) -> String { - let mut values = values.iter().copied(); + let mut values = values.iter().copied().peekable(); let mut result = String::with_capacity(spec.len()); // for the first space - let mut write_space = true; - let last_value = if spec.is_empty() { - // we still want to print any remaining values - write_space = false; - values.next() + let mut write_space = if spec.is_empty() { + false } else { - format_spec(spec, &mut values, &mut result) + format_spec(spec, &mut values, &mut result); + true }; // append any remaining values with the standard format - if let Some(v) = last_value { - for v in std::iter::once(v).chain(values) { - let fmt = v.fmt(FormatSpec::String); - if write_space { - result.push(' '); - } - result.push_str(&fmt); - write_space = true; + for v in values { + let fmt = v.fmt(FormatSpec::String); + if write_space { + result.push(' '); } + result.push_str(&fmt); + write_space = true; } result @@ -278,46 +275,48 @@ pub fn console_format(spec: &str, values: &[&dyn ConsoleFmt]) -> String { fn format_spec<'a>( s: &str, - values: &mut impl Iterator, + values: &mut Peekable>, result: &mut String, -) -> Option<&'a dyn ConsoleFmt> { +) { let mut expect_fmt = false; - let mut current_value = values.next(); let mut chars = s.chars().peekable(); - while let Some(ch) = chars.next() { + while chars.peek().is_some() { if expect_fmt { expect_fmt = false; - let mut iter = std::iter::once(ch).chain(chars.by_ref()).peekable(); - if let Some(spec) = FormatSpec::from_chars(&mut iter) { - // format and write the value - if let Some(value) = current_value { - let string = value.fmt(spec); - result.push_str(&string); - current_value = values.next(); + match FormatSpec::from_chars(&mut chars) { + Ok(spec) => { + let value = values.next().expect("value existence is checked"); + // format and write the value + result.push_str(&value.fmt(spec)); } - } else { - // invalid specifier or a second `%`, in both cases we ignore - result.push('%'); - result.push(ch); - } - } else if ch == '%' { - if let Some(&next_ch) = chars.peek() { - if next_ch == '%' { + Err(consumed) => { + // on parser failure, write '%' and consumed characters result.push('%'); - chars.next(); + result.push_str(&consumed); + } + } + } else { + let ch = chars.next().unwrap(); + if ch == '%' { + if let Some(&next_ch) = chars.peek() { + if next_ch == '%' { + result.push('%'); + chars.next(); + } else if values.peek().is_some() { + // only try formatting if there are values to format + expect_fmt = true; + } else { + result.push(ch); + } } else { - expect_fmt = true; + result.push(ch); } } else { result.push(ch); } - } else { - result.push(ch); } } - - current_value } #[cfg(test)] @@ -468,6 +467,13 @@ mod tests { fmt_1("%x", &I256::try_from(-100000000000i64).unwrap()) ); assert_eq!("100", fmt_1("%o", &I256::try_from(100).unwrap())); + + // make sure that %byte values are not consumed when there are no values + assert_eq!("%333d%3e%5F", console_format("%333d%3e%5F", &[])); + assert_eq!( + "%5d123456.789%2f%3f%e1", + console_format("%5d%3e%2f%3f%e1", &[&U256::from(123456789)]) + ); } #[test] From f808d08a76672b3e26bc41bd3d9666ca01f52a53 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 21 Aug 2024 10:33:21 +0300 Subject: [PATCH 089/184] fix(coverage): ensure contract hash to record coverage for is not zero (#8698) --- crates/evm/coverage/src/inspector.rs | 20 ++++++++--- crates/forge/tests/cli/coverage.rs | 50 ++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/crates/evm/coverage/src/inspector.rs b/crates/evm/coverage/src/inspector.rs index 4e25ddb51..73d2ff148 100644 --- a/crates/evm/coverage/src/inspector.rs +++ b/crates/evm/coverage/src/inspector.rs @@ -1,4 +1,5 @@ use crate::{HitMap, HitMaps}; +use alloy_primitives::B256; use revm::{interpreter::Interpreter, Database, EvmContext, Inspector}; #[derive(Clone, Debug, Default)] @@ -10,15 +11,26 @@ pub struct CoverageCollector { impl Inspector for CoverageCollector { #[inline] fn initialize_interp(&mut self, interp: &mut Interpreter, _context: &mut EvmContext) { - let hash = interp.contract.hash.expect("Contract hash is None"); self.maps - .entry(hash) + .entry(get_contract_hash(interp)) .or_insert_with(|| HitMap::new(interp.contract.bytecode.original_bytes())); } #[inline] fn step(&mut self, interp: &mut Interpreter, _context: &mut EvmContext) { - let hash = interp.contract.hash.expect("Contract hash is None"); - self.maps.entry(hash).and_modify(|map| map.hit(interp.program_counter())); + self.maps + .entry(get_contract_hash(interp)) + .and_modify(|map| map.hit(interp.program_counter())); + } +} + +/// Helper function for extracting contract hash used to record coverage hit map. +/// If contract hash available in interpreter contract is zero (contract not yet created but going +/// to be created in current tx) then it hash is calculated from contract bytecode. +fn get_contract_hash(interp: &mut Interpreter) -> B256 { + let mut hash = interp.contract.hash.expect("Contract hash is None"); + if hash == B256::ZERO { + hash = interp.contract.bytecode.hash_slow(); } + hash } diff --git a/crates/forge/tests/cli/coverage.rs b/crates/forge/tests/cli/coverage.rs index 1b668afac..381a31c12 100644 --- a/crates/forge/tests/cli/coverage.rs +++ b/crates/forge/tests/cli/coverage.rs @@ -1271,3 +1271,53 @@ contract AContractTest is DSTest { "#]]); }); + +forgetest!(test_constructors_coverage, |prj, cmd| { + prj.insert_ds_test(); + prj.add_source( + "AContract.sol", + r#" +contract AContract { + bool public active; + + constructor() { + active = true; + } +} + +contract BContract { + bool public active; + + constructor() { + active = true; + } +} + "#, + ) + .unwrap(); + + prj.add_source( + "AContractTest.sol", + r#" +import "./test.sol"; +import "./AContract.sol"; + +contract AContractTest is DSTest { + function test_constructors() public { + AContract a = new AContract(); + BContract b = new BContract(); + } +} + "#, + ) + .unwrap(); + + cmd.arg("coverage").args(["--summary".to_string()]).assert_success().stdout_eq(str![[r#" +... +| File | % Lines | % Statements | % Branches | % Funcs | +|-------------------|---------------|---------------|---------------|---------------| +| src/AContract.sol | 100.00% (2/2) | 100.00% (2/2) | 100.00% (0/0) | 100.00% (2/2) | +| Total | 100.00% (2/2) | 100.00% (2/2) | 100.00% (0/0) | 100.00% (2/2) | + +"#]]); +}); From facd7f98378556653f16bc229f531dd80a548f9b Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Wed, 21 Aug 2024 02:00:55 -0700 Subject: [PATCH 090/184] feat(`forge verify-bytecode`): support alternative block explorers + predeploys (#8510) --- crates/forge/bin/cmd/create.rs | 2 +- crates/forge/bin/main.rs | 2 +- crates/forge/bin/opts.rs | 10 +- crates/forge/tests/cli/verify_bytecode.rs | 239 ++++++- crates/verify/src/bytecode.rs | 825 +++++++++------------- crates/verify/src/etherscan/mod.rs | 42 +- crates/verify/src/lib.rs | 388 +--------- crates/verify/src/provider.rs | 9 +- crates/verify/src/sourcify.rs | 8 +- crates/verify/src/types.rs | 44 ++ crates/verify/src/utils.rs | 423 +++++++++++ crates/verify/src/verify.rs | 367 ++++++++++ 12 files changed, 1434 insertions(+), 925 deletions(-) create mode 100644 crates/verify/src/types.rs create mode 100644 crates/verify/src/utils.rs create mode 100644 crates/verify/src/verify.rs diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index eb30edd29..b549b39a0 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -202,7 +202,7 @@ impl CreateArgs { let context = verify.resolve_context().await?; - verify.verification_provider()?.preflight_check(verify, context).await?; + verify.verification_provider()?.preflight_verify_check(verify, context).await?; Ok(()) } diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs index dc4a18dd6..81ee0b867 100644 --- a/crates/forge/bin/main.rs +++ b/crates/forge/bin/main.rs @@ -55,6 +55,7 @@ fn main() -> Result<()> { ForgeSubcommand::Debug(cmd) => utils::block_on(cmd.run()), ForgeSubcommand::VerifyContract(args) => utils::block_on(args.run()), ForgeSubcommand::VerifyCheck(args) => utils::block_on(args.run()), + ForgeSubcommand::VerifyBytecode(cmd) => utils::block_on(cmd.run()), ForgeSubcommand::Clone(cmd) => utils::block_on(cmd.run()), ForgeSubcommand::Cache(cmd) => match cmd.sub { CacheSubcommands::Clean(cmd) => cmd.run(), @@ -117,7 +118,6 @@ fn main() -> Result<()> { ForgeSubcommand::Generate(cmd) => match cmd.sub { GenerateSubcommands::Test(cmd) => cmd.run(), }, - ForgeSubcommand::VerifyBytecode(cmd) => utils::block_on(cmd.run()), ForgeSubcommand::Soldeer(cmd) => cmd.run(), ForgeSubcommand::Eip712(cmd) => cmd.run(), ForgeSubcommand::BindJson(cmd) => cmd.run(), diff --git a/crates/forge/bin/opts.rs b/crates/forge/bin/opts.rs index b86d19c17..fbe1f67df 100644 --- a/crates/forge/bin/opts.rs +++ b/crates/forge/bin/opts.rs @@ -6,7 +6,7 @@ use crate::cmd::{ }; use clap::{Parser, Subcommand, ValueHint}; use forge_script::ScriptArgs; -use forge_verify::{bytecode::VerifyBytecodeArgs, VerifyArgs, VerifyCheckArgs}; +use forge_verify::{VerifyArgs, VerifyBytecodeArgs, VerifyCheckArgs}; use std::path::PathBuf; const VERSION_MESSAGE: &str = concat!( @@ -87,6 +87,10 @@ pub enum ForgeSubcommand { #[command(visible_alias = "vc")] VerifyCheck(VerifyCheckArgs), + /// Verify the deployed bytecode against its source on Etherscan. + #[clap(visible_alias = "vb")] + VerifyBytecode(VerifyBytecodeArgs), + /// Deploy a smart contract. #[command(visible_alias = "c")] Create(CreateArgs), @@ -158,10 +162,6 @@ pub enum ForgeSubcommand { /// Generate scaffold files. Generate(generate::GenerateArgs), - /// Verify the deployed bytecode against its source. - #[clap(visible_alias = "vb")] - VerifyBytecode(VerifyBytecodeArgs), - /// Soldeer dependency manager. Soldeer(soldeer::SoldeerArgs), diff --git a/crates/forge/tests/cli/verify_bytecode.rs b/crates/forge/tests/cli/verify_bytecode.rs index 846fcc47d..2b28570e7 100644 --- a/crates/forge/tests/cli/verify_bytecode.rs +++ b/crates/forge/tests/cli/verify_bytecode.rs @@ -7,12 +7,16 @@ use foundry_test_utils::{ TestCommand, TestProject, }; +#[allow(clippy::too_many_arguments)] fn test_verify_bytecode( prj: TestProject, mut cmd: TestCommand, addr: &str, contract_name: &str, + constructor_args: Option>, config: Config, + verifier: &str, + verifier_url: &str, expected_matches: (&str, &str), ) { let etherscan_key = next_etherscan_api_key(); @@ -29,6 +33,68 @@ fn test_verify_bytecode( prj.add_source(contract_name, &source_code).unwrap(); prj.write_config(config); + let mut args = vec![ + "verify-bytecode", + addr, + contract_name, + "--etherscan-api-key", + ðerscan_key, + "--verifier", + verifier, + "--verifier-url", + verifier_url, + "--rpc-url", + &rpc_url, + ]; + + if let Some(constructor_args) = constructor_args { + args.push("--constructor-args"); + args.extend(constructor_args.iter()); + } + + let output = cmd.forge_fuse().args(args).assert_success().get_output().stdout_lossy(); + + assert!(output + .contains(format!("Creation code matched with status {}", expected_matches.0).as_str())); + assert!(output + .contains(format!("Runtime code matched with status {}", expected_matches.1).as_str())); +} + +#[allow(clippy::too_many_arguments)] +fn test_verify_bytecode_with_ignore( + prj: TestProject, + mut cmd: TestCommand, + addr: &str, + contract_name: &str, + config: Config, + verifier: &str, + verifier_url: &str, + expected_matches: (&str, &str), + ignore: &str, + chain: &str, +) { + let etherscan_key = next_etherscan_api_key(); + let rpc_url = next_http_archive_rpc_endpoint(); + + // fetch and flatten source code + let source_code = cmd + .cast_fuse() + .args([ + "etherscan-source", + addr, + "--flatten", + "--etherscan-api-key", + ðerscan_key, + "--chain", + chain, + ]) + .assert_success() + .get_output() + .stdout_lossy(); + + prj.add_source(contract_name, &source_code).unwrap(); + prj.write_config(config); + let output = cmd .forge_fuse() .args([ @@ -37,25 +103,44 @@ fn test_verify_bytecode( contract_name, "--etherscan-api-key", ðerscan_key, + "--verifier", + verifier, + "--verifier-url", + verifier_url, "--rpc-url", &rpc_url, + "--ignore", + ignore, ]) .assert_success() .get_output() .stdout_lossy(); - assert!(output - .contains(format!("Creation code matched with status {}", expected_matches.0).as_str())); - assert!(output - .contains(format!("Runtime code matched with status {}", expected_matches.1).as_str())); -} + if ignore == "creation" { + assert!(!output.contains( + format!("Creation code matched with status {}", expected_matches.0).as_str() + )); + } else { + assert!(output.contains( + format!("Creation code matched with status {}", expected_matches.0).as_str() + )); + } + if ignore == "runtime" { + assert!(!output + .contains(format!("Runtime code matched with status {}", expected_matches.1).as_str())); + } else { + assert!(output + .contains(format!("Runtime code matched with status {}", expected_matches.1).as_str())); + } +} forgetest_async!(can_verify_bytecode_no_metadata, |prj, cmd| { test_verify_bytecode( prj, cmd, "0xba2492e52F45651B60B8B38d4Ea5E2390C64Ffb1", "SystemConfig", + None, Config { evm_version: EvmVersion::London, optimizer_runs: 999999, @@ -64,7 +149,9 @@ forgetest_async!(can_verify_bytecode_no_metadata, |prj, cmd| { bytecode_hash: BytecodeHash::None, ..Default::default() }, - ("full", "full"), + "etherscan", + "https://api.etherscan.io/api", + ("partial", "partial"), ); }); @@ -74,12 +161,152 @@ forgetest_async!(can_verify_bytecode_with_metadata, |prj, cmd| { cmd, "0xb8901acb165ed027e32754e0ffe830802919727f", "L1_ETH_Bridge", + None, Config { evm_version: EvmVersion::Paris, optimizer_runs: 50000, optimizer: true, ..Default::default() }, + "etherscan", + "https://api.etherscan.io/api", + ("partial", "partial"), + ); +}); + +// Test non-CREATE2 deployed contract with blockscout +forgetest_async!(can_verify_bytecode_with_blockscout, |prj, cmd| { + test_verify_bytecode( + prj, + cmd, + "0x70f44C13944d49a236E3cD7a94f48f5daB6C619b", + "StrategyManager", + None, + Config { + evm_version: EvmVersion::London, + optimizer: true, + optimizer_runs: 200, + ..Default::default() + }, + "blockscout", + "https://eth.blockscout.com/api", ("partial", "partial"), ); }); + +// Test CREATE2 deployed contract with blockscout +forgetest_async!(can_vb_create2_with_blockscout, |prj, cmd| { + test_verify_bytecode( + prj, + cmd, + "0xba2492e52F45651B60B8B38d4Ea5E2390C64Ffb1", + "SystemConfig", + None, + Config { + evm_version: EvmVersion::London, + optimizer_runs: 999999, + optimizer: true, + cbor_metadata: false, + bytecode_hash: BytecodeHash::None, + ..Default::default() + }, + "blockscout", + "https://eth.blockscout.com/api", + ("partial", "partial"), + ); +}); + +// Test `--constructor-args` +forgetest_async!(can_verify_bytecode_with_constructor_args, |prj, cmd| { + let constructor_args = vec![ + "0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A", + "0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338", + "0xD92145c07f8Ed1D392c1B88017934E301CC1c3Cd", + ]; + test_verify_bytecode( + prj, + cmd, + "0x70f44C13944d49a236E3cD7a94f48f5daB6C619b", + "StrategyManager", + Some(constructor_args), + Config { + evm_version: EvmVersion::London, + optimizer: true, + optimizer_runs: 200, + ..Default::default() + }, + "etherscan", + "https://api.etherscan.io/api", + ("partial", "partial"), + ); +}); + +// `--ignore` tests +forgetest_async!(can_ignore_creation, |prj, cmd| { + test_verify_bytecode_with_ignore( + prj, + cmd, + "0xba2492e52F45651B60B8B38d4Ea5E2390C64Ffb1", + "SystemConfig", + Config { + evm_version: EvmVersion::London, + optimizer_runs: 999999, + optimizer: true, + cbor_metadata: false, + bytecode_hash: BytecodeHash::None, + ..Default::default() + }, + "etherscan", + "https://api.etherscan.io/api", + ("ignored", "partial"), + "creation", + "1", + ); +}); + +forgetest_async!(can_ignore_runtime, |prj, cmd| { + test_verify_bytecode_with_ignore( + prj, + cmd, + "0xba2492e52F45651B60B8B38d4Ea5E2390C64Ffb1", + "SystemConfig", + Config { + evm_version: EvmVersion::London, + optimizer_runs: 999999, + optimizer: true, + cbor_metadata: false, + bytecode_hash: BytecodeHash::None, + ..Default::default() + }, + "etherscan", + "https://api.etherscan.io/api", + ("partial", "ignored"), + "runtime", + "1", + ); +}); + +// Test predeploy contracts +// TODO: Add test utils for base such as basescan keys and alchemy keys. +// WETH9 Predeploy +// forgetest_async!(can_verify_predeploys, |prj, cmd| { +// test_verify_bytecode_with_ignore( +// prj, +// cmd, +// "0x4200000000000000000000000000000000000006", +// "WETH9", +// Config { +// evm_version: EvmVersion::default(), +// optimizer: true, +// optimizer_runs: 10000, +// cbor_metadata: true, +// bytecode_hash: BytecodeHash::Bzzr1, +// ..Default::default() +// }, +// "etherscan", +// "https://api.basescan.org/api", +// ("ignored", "partial"), +// "creation", +// "base", +// ); +// }); diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index 11da12c43..b1e4f7e52 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -1,27 +1,26 @@ -use alloy_dyn_abi::DynSolValue; +//! The `forge verify-bytecode` command. +use crate::{ + etherscan::EtherscanVerificationProvider, + utils::{ + check_and_encode_args, check_explorer_args, configure_env_block, maybe_predeploy_contract, + BytecodeType, JsonResult, + }, + verify::VerifierArgs, +}; use alloy_primitives::{hex, Address, Bytes, U256}; use alloy_provider::Provider; -use alloy_rpc_types::{BlockId, BlockNumberOrTag}; +use alloy_rpc_types::{BlockId, BlockNumberOrTag, Transaction}; use clap::{Parser, ValueHint}; use eyre::{OptionExt, Result}; -use foundry_block_explorers::{contract::Metadata, Client}; use foundry_cli::{ opts::EtherscanOpts, utils::{self, read_constructor_args_file, LoadConfig}, }; -use foundry_common::{abi::encode_args, compile::ProjectCompiler, provider::ProviderBuilder}; -use foundry_compilers::{ - artifacts::{CompactContractBytecode, EvmVersion}, - info::ContractInfo, -}; -use foundry_config::{figment, filter::SkipBuildFilter, impl_figment_convert, Chain, Config}; -use foundry_evm::{ - constants::DEFAULT_CREATE2_DEPLOYER, executors::TracingExecutor, utils::configure_tx_env, -}; -use revm_primitives::{db::Database, EnvWithHandlerCfg, HandlerCfg}; -use semver::Version; -use serde::{Deserialize, Serialize}; -use std::{fmt, path::PathBuf, str::FromStr}; +use foundry_compilers::{artifacts::EvmVersion, info::ContractInfo}; +use foundry_config::{figment, impl_figment_convert, Config}; +use foundry_evm::{constants::DEFAULT_CREATE2_DEPLOYER, utils::configure_tx_env}; +use revm_primitives::AccountInfo; +use std::path::PathBuf; use yansi::Paint; impl_figment_convert!(VerifyBytecodeArgs); @@ -70,20 +69,26 @@ pub struct VerifyBytecodeArgs { pub rpc_url: Option, #[clap(flatten)] - pub etherscan_opts: EtherscanOpts, - - /// Skip building files whose names contain the given filter. - /// - /// `test` and `script` are aliases for `.t.sol` and `.s.sol`. - #[arg(long, num_args(1..))] - pub skip: Option>, + pub etherscan: EtherscanOpts, - /// The path to the project's root directory. - pub root: Option, + /// Verifier options. + #[clap(flatten)] + pub verifier: VerifierArgs, /// Suppress logs and emit json results to stdout #[clap(long, default_value = "false")] pub json: bool, + + /// The project's root path. + /// + /// By default root of the Git repository, if in one, + /// or the current working directory. + #[arg(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + pub root: Option, + + /// Ignore verification for creation or runtime bytecode. + #[clap(long, value_name = "BYTECODE_TYPE")] + pub ignore: Option, } impl figment::Provider for VerifyBytecodeArgs { @@ -94,7 +99,7 @@ impl figment::Provider for VerifyBytecodeArgs { fn data( &self, ) -> Result, figment::Error> { - let mut dict = self.etherscan_opts.dict(); + let mut dict = self.etherscan.dict(); if let Some(block) = &self.block { dict.insert("block".into(), figment::value::Value::serialize(block)?); } @@ -112,8 +117,28 @@ impl VerifyBytecodeArgs { pub async fn run(mut self) -> Result<()> { // Setup let config = self.load_config_emit_warnings(); - let provider = ProviderBuilder::new(&config.get_rpc_url_or_localhost_http()?).build()?; + let provider = utils::get_provider(&config)?; + // If chain is not set, we try to get it from the RPC. + // If RPC is not set, the default chain is used. + let chain = match config.get_rpc_url() { + Some(_) => utils::get_chain(config.chain, &provider).await?, + None => config.chain.unwrap_or_default(), + }; + + // Set Etherscan options. + self.etherscan.chain = Some(chain); + self.etherscan.key = config.get_etherscan_config_with_chain(Some(chain))?.map(|c| c.key); + + // Etherscan client + let etherscan = EtherscanVerificationProvider.client( + self.etherscan.chain.unwrap_or_default(), + self.verifier.verifier_url.as_deref(), + self.etherscan.key().as_deref(), + &config, + )?; + + // Get the bytecode at the address, bailing if it doesn't exist. let code = provider.get_code_at(self.address).await?; if code.is_empty() { eyre::bail!("No bytecode found at address {}", self.address); @@ -127,29 +152,158 @@ impl VerifyBytecodeArgs { ); } - // If chain is not set, we try to get it from the RPC - // If RPC is not set, the default chain is used - let chain = if config.get_rpc_url().is_some() { - let chain_id = provider.get_chain_id().await?; - Chain::from(chain_id) + let mut json_results: Vec = vec![]; + + // Get creation tx hash. + let creation_data = etherscan.contract_creation_data(self.address).await; + + // Check if contract is a predeploy + let (creation_data, maybe_predeploy) = maybe_predeploy_contract(creation_data)?; + + trace!(maybe_predeploy = ?maybe_predeploy); + + // Get the constructor args using `source_code` endpoint. + let source_code = etherscan.contract_source_code(self.address).await?; + + // Check if the contract name matches. + let name = source_code.items.first().map(|item| item.contract_name.to_owned()); + if name.as_ref() != Some(&self.contract.name) { + eyre::bail!("Contract name mismatch"); + } + + // Obtain Etherscan compilation metadata. + let etherscan_metadata = source_code.items.first().unwrap(); + + // Obtain local artifact + let artifact = if let Ok(local_bytecode) = + crate::utils::build_using_cache(&self, etherscan_metadata, &config) + { + trace!("using cache"); + local_bytecode } else { - config.chain.unwrap_or_default() + crate::utils::build_project(&self, &config)? }; - // Set Etherscan options - self.etherscan_opts.chain = Some(chain); - self.etherscan_opts.key = - config.get_etherscan_config_with_chain(Some(chain))?.map(|c| c.key); + // Get local bytecode (creation code) + let local_bytecode = artifact + .bytecode + .as_ref() + .and_then(|b| b.to_owned().into_bytes()) + .ok_or_eyre("Unlinked bytecode is not supported for verification")?; + + // Get and encode user provided constructor args + let provided_constructor_args = if let Some(path) = self.constructor_args_path.to_owned() { + // Read from file + Some(read_constructor_args_file(path)?) + } else { + self.constructor_args.to_owned() + } + .map(|args| check_and_encode_args(&artifact, args)) + .transpose()? + .or(self.encoded_constructor_args.to_owned().map(hex::decode).transpose()?); - // If etherscan key is not set, we can't proceed with etherscan verification - let Some(key) = self.etherscan_opts.key.clone() else { - eyre::bail!("Etherscan API key is required for verification"); + let mut constructor_args = if let Some(provided) = provided_constructor_args { + provided.into() + } else { + // If no constructor args were provided, try to retrieve them from the explorer. + check_explorer_args(source_code.clone())? }; - let etherscan = Client::new(chain, key)?; - // Get creation tx hash - let creation_data = etherscan.contract_creation_data(self.address).await?; + if maybe_predeploy { + if !self.json { + println!( + "{}", + format!("Attempting to verify predeployed contract at {:?}. Ignoring creation code verification.", self.address) + .yellow() + .bold() + ) + } + + // Append constructor args to the local_bytecode. + trace!(%constructor_args); + let mut local_bytecode_vec = local_bytecode.to_vec(); + local_bytecode_vec.extend_from_slice(&constructor_args); + + // Deploy at genesis + let gen_blk_num = 0_u64; + let (mut fork_config, evm_opts) = config.clone().load_config_and_evm_opts()?; + let (mut env, mut executor) = crate::utils::get_tracing_executor( + &mut fork_config, + gen_blk_num, + etherscan_metadata.evm_version()?.unwrap_or(EvmVersion::default()), + evm_opts, + ) + .await?; + + env.block.number = U256::ZERO; // Genesis block + let genesis_block = provider.get_block(gen_blk_num.into(), true.into()).await?; + + // Setup genesis tx and env. + let deployer = Address::with_last_byte(0x1); + let mut gen_tx = Transaction { + from: deployer, + to: None, + input: Bytes::from(local_bytecode_vec), + ..Default::default() + }; + + if let Some(ref block) = genesis_block { + configure_env_block(&mut env, block); + gen_tx.max_fee_per_gas = Some(block.header.base_fee_per_gas.unwrap_or_default()); + gen_tx.gas = block.header.gas_limit; + gen_tx.gas_price = Some(block.header.base_fee_per_gas.unwrap_or_default()); + } + configure_tx_env(&mut env, &gen_tx); + + // Seed deployer account with funds + let account_info = AccountInfo { + balance: U256::from(100 * 10_u128.pow(18)), + nonce: 0, + ..Default::default() + }; + executor.backend_mut().insert_account_info(deployer, account_info); + + let fork_address = + crate::utils::deploy_contract(&mut executor, &env, config.evm_spec_id(), &gen_tx)?; + + // Compare runtime bytecode + let (deployed_bytecode, onchain_runtime_code) = crate::utils::get_runtime_codes( + &mut executor, + &provider, + self.address, + fork_address, + None, + ) + .await?; + + let match_type = crate::utils::match_bytecodes( + &deployed_bytecode.original_bytes(), + &onchain_runtime_code, + &constructor_args, + true, + config.bytecode_hash, + ); + + crate::utils::print_result( + &self, + match_type, + BytecodeType::Runtime, + &mut json_results, + etherscan_metadata, + &config, + ); + + if self.json { + println!("{}", serde_json::to_string(&json_results)?); + } + + return Ok(()); + } + + // We can unwrap directly as maybe_predeploy is false + let creation_data = creation_data.unwrap(); + // Get transaction and receipt. trace!(creation_tx_hash = ?creation_data.transaction_hash); let mut transaction = provider .get_transaction_by_hash(creation_data.transaction_hash) @@ -162,7 +316,6 @@ impl VerifyBytecodeArgs { .get_transaction_receipt(creation_data.transaction_hash) .await .or_else(|e| eyre::bail!("Couldn't fetch transaction receipt from RPC: {:?}", e))?; - let receipt = if let Some(receipt) = receipt { receipt } else { @@ -172,7 +325,7 @@ impl VerifyBytecodeArgs { ); }; - // Extract creation code + // Extract creation code from creation tx input. let maybe_creation_code = if receipt.to.is_none() && receipt.contract_address == Some(self.address) { &transaction.input @@ -185,127 +338,73 @@ impl VerifyBytecodeArgs { ); }; - // Get the constructor args using `source_code` endpoint - let source_code = etherscan.contract_source_code(self.address).await?; - - // Check if the contract name matches - let name = source_code.items.first().map(|item| item.contract_name.to_owned()); - if name.as_ref() != Some(&self.contract.name) { - eyre::bail!("Contract name mismatch"); - } - - // Obtain Etherscan compilation metadata - let etherscan_metadata = source_code.items.first().unwrap(); - - // Obtain local artifact - let artifact = - if let Ok(local_bytecode) = self.build_using_cache(etherscan_metadata, &config) { - trace!("using cache"); - local_bytecode - } else { - self.build_project(&config)? - }; - - let local_bytecode = artifact - .bytecode - .and_then(|b| b.into_bytes()) - .ok_or_eyre("Unlinked bytecode is not supported for verification")?; - - // Get the constructor args from etherscan - let mut constructor_args = if let Some(args) = source_code.items.first() { - args.constructor_arguments.clone() - } else { - eyre::bail!("No constructor arguments found for contract at address {}", self.address); - }; - - // Get and encode user provided constructor args - let provided_constructor_args = if let Some(path) = self.constructor_args_path.to_owned() { - // Read from file - Some(read_constructor_args_file(path)?) - } else { - self.constructor_args.to_owned() - } - .map(|args| { - if let Some(constructor) = artifact.abi.as_ref().and_then(|abi| abi.constructor()) { - if constructor.inputs.len() != args.len() { - eyre::bail!( - "Mismatch of constructor arguments length. Expected {}, got {}", - constructor.inputs.len(), - args.len() - ); - } - encode_args(&constructor.inputs, &args) - .map(|args| DynSolValue::Tuple(args).abi_encode()) - } else { - Ok(Vec::new()) - } - }) - .transpose()? - .or(self.encoded_constructor_args.to_owned().map(hex::decode).transpose()?); - - if let Some(provided) = provided_constructor_args { - constructor_args = provided.into(); - } else { - // In some cases, Etherscan will return incorrect constructor arguments. If this - // happens, try extracting arguments ourselves. - if !maybe_creation_code.ends_with(&constructor_args) { - trace!("mismatch of constructor args with etherscan"); - // If local bytecode is longer than on-chain one, this is probably not a match. - if maybe_creation_code.len() >= local_bytecode.len() { - constructor_args = - Bytes::copy_from_slice(&maybe_creation_code[local_bytecode.len()..]); - trace!( - "setting constructor args to latest {} bytes of bytecode", - constructor_args.len() - ); - } + // In some cases, Etherscan will return incorrect constructor arguments. If this + // happens, try extracting arguments ourselves. + if !maybe_creation_code.ends_with(&constructor_args) { + trace!("mismatch of constructor args with etherscan"); + // If local bytecode is longer than on-chain one, this is probably not a match. + if maybe_creation_code.len() >= local_bytecode.len() { + constructor_args = + Bytes::copy_from_slice(&maybe_creation_code[local_bytecode.len()..]); + trace!( + target: "forge::verify", + "setting constructor args to latest {} bytes of bytecode", + constructor_args.len() + ); } } - // Append constructor args to the local_bytecode + // Append constructor args to the local_bytecode. trace!(%constructor_args); let mut local_bytecode_vec = local_bytecode.to_vec(); local_bytecode_vec.extend_from_slice(&constructor_args); - // Cmp creation code with locally built bytecode and maybe_creation_code - let match_type = match_bytecodes( - local_bytecode_vec.as_slice(), - maybe_creation_code, - &constructor_args, - false, - ); - - let mut json_results: Vec = vec![]; - self.print_result( - match_type, - BytecodeType::Creation, - &mut json_results, - etherscan_metadata, - &config, - ); + trace!(ignore = ?self.ignore); + // Check if `--ignore` is set to `creation`. + if !self.ignore.is_some_and(|b| b.is_creation()) { + // Compare creation code with locally built bytecode and `maybe_creation_code`. + let match_type = crate::utils::match_bytecodes( + local_bytecode_vec.as_slice(), + maybe_creation_code, + &constructor_args, + false, + config.bytecode_hash, + ); - // If the creation code does not match, the runtime also won't match. Hence return. - if match_type.is_none() { - self.print_result( - None, - BytecodeType::Runtime, + crate::utils::print_result( + &self, + match_type, + BytecodeType::Creation, &mut json_results, etherscan_metadata, &config, ); - if self.json { - println!("{}", serde_json::to_string(&json_results)?); + + // If the creation code does not match, the runtime also won't match. Hence return. + if match_type.is_none() { + crate::utils::print_result( + &self, + None, + BytecodeType::Runtime, + &mut json_results, + etherscan_metadata, + &config, + ); + if self.json { + println!("{}", serde_json::to_string(&json_results)?); + } + return Ok(()); } - return Ok(()); } - // Get contract creation block - let simulation_block = match self.block { - Some(BlockId::Number(BlockNumberOrTag::Number(block))) => block, - Some(_) => eyre::bail!("Invalid block number"), - None => { - let provider = utils::get_provider(&config)?; - provider + if !self.ignore.is_some_and(|b| b.is_runtime()) { + // Get contract creation block. + let simulation_block = match self.block { + Some(BlockId::Number(BlockNumberOrTag::Number(block))) => block, + Some(_) => eyre::bail!("Invalid block number"), + None => { + let provider = utils::get_provider(&config)?; + provider .get_transaction_by_hash(creation_data.transaction_hash) .await.or_else(|e| eyre::bail!("Couldn't fetch transaction from RPC: {:?}", e))?.ok_or_else(|| { eyre::eyre!("Transaction not found for hash {}", creation_data.transaction_hash) @@ -313,378 +412,90 @@ impl VerifyBytecodeArgs { .block_number.ok_or_else(|| { eyre::eyre!("Failed to get block number of the contract creation tx, specify using the --block flag") })? - } - }; + } + }; - // Fork the chain at `simulation_block` - - let (mut fork_config, evm_opts) = config.clone().load_config_and_evm_opts()?; - fork_config.fork_block_number = Some(simulation_block - 1); - fork_config.evm_version = - etherscan_metadata.evm_version()?.unwrap_or(EvmVersion::default()); - let (mut env, fork, _chain, alphanet) = - TracingExecutor::get_fork_material(&fork_config, evm_opts).await?; - - let mut executor = TracingExecutor::new( - env.clone(), - fork, - Some(fork_config.evm_version), - false, - false, - alphanet, - ); - env.block.number = U256::from(simulation_block); - let block = provider.get_block(simulation_block.into(), true.into()).await?; - - // Workaround for the NonceTooHigh issue as we're not simulating prior txs of the same - // block. - let prev_block_id = BlockId::number(simulation_block - 1); - let prev_block_nonce = provider - .get_transaction_count(creation_data.contract_creator) - .block_id(prev_block_id) + // Fork the chain at `simulation_block`. + let (mut fork_config, evm_opts) = config.clone().load_config_and_evm_opts()?; + let (mut env, mut executor) = crate::utils::get_tracing_executor( + &mut fork_config, + simulation_block - 1, // env.fork_block_number + etherscan_metadata.evm_version()?.unwrap_or(EvmVersion::default()), + evm_opts, + ) .await?; - transaction.nonce = prev_block_nonce; - - if let Some(ref block) = block { - env.block.timestamp = U256::from(block.header.timestamp); - env.block.coinbase = block.header.miner; - env.block.difficulty = block.header.difficulty; - env.block.prevrandao = Some(block.header.mix_hash.unwrap_or_default()); - env.block.basefee = U256::from(block.header.base_fee_per_gas.unwrap_or_default()); - env.block.gas_limit = U256::from(block.header.gas_limit); - } - - // Replace the `input` with local creation code in the creation tx. - if let Some(to) = transaction.to { - if to == DEFAULT_CREATE2_DEPLOYER { - let mut input = transaction.input[..32].to_vec(); // Salt - input.extend_from_slice(&local_bytecode_vec); - transaction.input = Bytes::from(input); + env.block.number = U256::from(simulation_block); + let block = provider.get_block(simulation_block.into(), true.into()).await?; - // Deploy default CREATE2 deployer - executor.deploy_create2_deployer()?; - } - } else { - transaction.input = Bytes::from(local_bytecode_vec); - } - - configure_tx_env(&mut env, &transaction); + // Workaround for the NonceTooHigh issue as we're not simulating prior txs of the same + // block. + let prev_block_id = BlockId::number(simulation_block - 1); - let env_with_handler = - EnvWithHandlerCfg::new(Box::new(env.clone()), HandlerCfg::new(config.evm_spec_id())); + // Use `transaction.from` instead of `creation_data.contract_creator` to resolve + // blockscout creation data discrepancy in case of CREATE2. + let prev_block_nonce = + provider.get_transaction_count(transaction.from).block_id(prev_block_id).await?; + transaction.nonce = prev_block_nonce; - let contract_address = if let Some(to) = transaction.to { - if to != DEFAULT_CREATE2_DEPLOYER { - eyre::bail!("Transaction `to` address is not the default create2 deployer i.e the tx is not a contract creation tx."); + if let Some(ref block) = block { + configure_env_block(&mut env, block) } - let result = executor.transact_with_env(env_with_handler.clone())?; - - if result.result.len() != 20 { - eyre::bail!("Failed to deploy contract on fork at block {simulation_block}: call result is not exactly 20 bytes"); - } - - Address::from_slice(&result.result) - } else { - let deploy_result = executor.deploy_with_env(env_with_handler, None)?; - deploy_result.address - }; - - // State commited using deploy_with_env, now get the runtime bytecode from the db. - let fork_runtime_code = executor - .backend_mut() - .basic(contract_address)? - .ok_or_else(|| { - eyre::eyre!( - "Failed to get runtime code for contract deployed on fork at address {}", - contract_address - ) - })? - .code - .ok_or_else(|| { - eyre::eyre!( - "Bytecode does not exist for contract deployed on fork at address {}", - contract_address - ) - })?; - - let onchain_runtime_code = - provider.get_code_at(self.address).block_id(BlockId::number(simulation_block)).await?; - - // Compare the onchain runtime bytecode with the runtime code from the fork. - let match_type = match_bytecodes( - &fork_runtime_code.original_bytes(), - &onchain_runtime_code, - &constructor_args, - true, - ); - - self.print_result( - match_type, - BytecodeType::Runtime, - &mut json_results, - etherscan_metadata, - &config, - ); - - if self.json { - println!("{}", serde_json::to_string(&json_results)?); - } - Ok(()) - } - - fn build_project(&self, config: &Config) -> Result { - let project = config.project()?; - let compiler = ProjectCompiler::new(); - - let mut output = compiler.compile(&project)?; - let artifact = output - .remove_contract(&self.contract) - .ok_or_eyre("Build Error: Contract artifact not found locally")?; + // Replace the `input` with local creation code in the creation tx. + if let Some(to) = transaction.to { + if to == DEFAULT_CREATE2_DEPLOYER { + let mut input = transaction.input[..32].to_vec(); // Salt + input.extend_from_slice(&local_bytecode_vec); + transaction.input = Bytes::from(input); - Ok(artifact.into_contract_bytecode()) - } - - fn build_using_cache( - &self, - etherscan_settings: &Metadata, - config: &Config, - ) -> Result { - let project = config.project()?; - let cache = project.read_cache_file()?; - let cached_artifacts = cache.read_artifacts::()?; - - for (key, value) in cached_artifacts { - let name = self.contract.name.to_owned() + ".sol"; - let version = etherscan_settings.compiler_version.to_owned(); - // Ignores vyper - if version.starts_with("vyper:") { - eyre::bail!("Vyper contracts are not supported") - } - // Parse etherscan version string - let version = - version.split('+').next().unwrap_or("").trim_start_matches('v').to_string(); - - // Check if `out/directory` name matches the contract name - if key.ends_with(name.as_str()) { - let name = name.replace(".sol", ".json"); - for artifact in value.into_values().flatten() { - // Check if ABI file matches the name - if !artifact.file.ends_with(&name) { - continue; - } - - // Check if Solidity version matches - if let Ok(version) = Version::parse(&version) { - if !(artifact.version.major == version.major && - artifact.version.minor == version.minor && - artifact.version.patch == version.patch) - { - continue; - } - } - - return Ok(artifact.artifact) + // Deploy default CREATE2 deployer + executor.deploy_create2_deployer()?; } - } - } - - eyre::bail!("couldn't find cached artifact for contract {}", self.contract.name) - } - - fn print_result( - &self, - res: Option, - bytecode_type: BytecodeType, - json_results: &mut Vec, - etherscan_config: &Metadata, - config: &Config, - ) { - if let Some(res) = res { - if !self.json { - println!( - "{} with status {}", - format!("{bytecode_type:?} code matched").green().bold(), - res.green().bold() - ); } else { - let json_res = JsonResult { bytecode_type, match_type: Some(res), message: None }; - json_results.push(json_res); - } - } else if !self.json { - println!( - "{}", - format!( - "{bytecode_type:?} code did not match - this may be due to varying compiler settings" - ) - .red() - .bold() - ); - let mismatches = find_mismatch_in_settings(etherscan_config, config); - for mismatch in mismatches { - println!("{}", mismatch.red().bold()); + transaction.input = Bytes::from(local_bytecode_vec); } - } else { - let json_res = JsonResult { - bytecode_type, - match_type: res, - message: Some(format!( - "{bytecode_type:?} code did not match - this may be due to varying compiler settings" - )), - }; - json_results.push(json_res); - } - } -} - -/// Enum to represent the type of verification: `full` or `partial`. -/// Ref: -#[derive(Debug, Clone, clap::ValueEnum, Default, PartialEq, Eq, Serialize, Deserialize, Copy)] -pub enum VerificationType { - #[default] - #[serde(rename = "full")] - Full, - #[serde(rename = "partial")] - Partial, -} -impl FromStr for VerificationType { - type Err = eyre::Error; - - fn from_str(s: &str) -> Result { - match s { - "full" => Ok(Self::Full), - "partial" => Ok(Self::Partial), - _ => eyre::bail!("Invalid verification type"), - } - } -} + configure_tx_env(&mut env, &transaction); + + let fork_address = crate::utils::deploy_contract( + &mut executor, + &env, + config.evm_spec_id(), + &transaction, + )?; + + // State commited using deploy_with_env, now get the runtime bytecode from the db. + let (fork_runtime_code, onchain_runtime_code) = crate::utils::get_runtime_codes( + &mut executor, + &provider, + self.address, + fork_address, + Some(simulation_block), + ) + .await?; -impl From for String { - fn from(v: VerificationType) -> Self { - match v { - VerificationType::Full => "full".to_string(), - VerificationType::Partial => "partial".to_string(), - } - } -} + // Compare the onchain runtime bytecode with the runtime code from the fork. + let match_type = crate::utils::match_bytecodes( + &fork_runtime_code.original_bytes(), + &onchain_runtime_code, + &constructor_args, + true, + config.bytecode_hash, + ); -impl fmt::Display for VerificationType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Full => write!(f, "full"), - Self::Partial => write!(f, "partial"), + crate::utils::print_result( + &self, + match_type, + BytecodeType::Runtime, + &mut json_results, + etherscan_metadata, + &config, + ); } - } -} - -/// Enum to represent the type of bytecode being verified -#[derive(Debug, Serialize, Deserialize, Clone, Copy)] -pub enum BytecodeType { - #[serde(rename = "creation")] - Creation, - #[serde(rename = "runtime")] - Runtime, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct JsonResult { - pub bytecode_type: BytecodeType, - pub match_type: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub message: Option, -} - -fn match_bytecodes( - local_bytecode: &[u8], - bytecode: &[u8], - constructor_args: &[u8], - is_runtime: bool, -) -> Option { - // 1. Try full match - if local_bytecode == bytecode { - Some(VerificationType::Full) - } else { - is_partial_match(local_bytecode, bytecode, constructor_args, is_runtime) - .then_some(VerificationType::Partial) - } -} - -fn is_partial_match( - mut local_bytecode: &[u8], - mut bytecode: &[u8], - constructor_args: &[u8], - is_runtime: bool, -) -> bool { - // 1. Check length of constructor args - if constructor_args.is_empty() || is_runtime { - // Assume metadata is at the end of the bytecode - return try_extract_and_compare_bytecode(local_bytecode, bytecode) - } - - // If not runtime, extract constructor args from the end of the bytecode - bytecode = &bytecode[..bytecode.len() - constructor_args.len()]; - local_bytecode = &local_bytecode[..local_bytecode.len() - constructor_args.len()]; - - try_extract_and_compare_bytecode(local_bytecode, bytecode) -} -fn try_extract_and_compare_bytecode(mut local_bytecode: &[u8], mut bytecode: &[u8]) -> bool { - local_bytecode = extract_metadata_hash(local_bytecode); - bytecode = extract_metadata_hash(bytecode); - - // Now compare the local code and bytecode - local_bytecode == bytecode -} - -/// @dev This assumes that the metadata is at the end of the bytecode -fn extract_metadata_hash(bytecode: &[u8]) -> &[u8] { - // Get the last two bytes of the bytecode to find the length of CBOR metadata - let metadata_len = &bytecode[bytecode.len() - 2..]; - let metadata_len = u16::from_be_bytes([metadata_len[0], metadata_len[1]]); - - if metadata_len as usize <= bytecode.len() { - if ciborium::from_reader::( - &bytecode[bytecode.len() - 2 - metadata_len as usize..bytecode.len() - 2], - ) - .is_ok() - { - &bytecode[..bytecode.len() - 2 - metadata_len as usize] - } else { - bytecode + if self.json { + println!("{}", serde_json::to_string(&json_results)?); } - } else { - bytecode - } -} - -fn find_mismatch_in_settings( - etherscan_settings: &Metadata, - local_settings: &Config, -) -> Vec { - let mut mismatches: Vec = vec![]; - if etherscan_settings.evm_version != local_settings.evm_version.to_string().to_lowercase() { - let str = format!( - "EVM version mismatch: local={}, onchain={}", - local_settings.evm_version, etherscan_settings.evm_version - ); - mismatches.push(str); - } - let local_optimizer: u64 = if local_settings.optimizer { 1 } else { 0 }; - if etherscan_settings.optimization_used != local_optimizer { - let str = format!( - "Optimizer mismatch: local={}, onchain={}", - local_settings.optimizer, etherscan_settings.optimization_used - ); - mismatches.push(str); - } - if etherscan_settings.runs != local_settings.optimizer_runs as u64 { - let str = format!( - "Optimizer runs mismatch: local={}, onchain={}", - local_settings.optimizer_runs, etherscan_settings.runs - ); - mismatches.push(str); + Ok(()) } - - mismatches } diff --git a/crates/verify/src/etherscan/mod.rs b/crates/verify/src/etherscan/mod.rs index 6fd6ffb7e..3109a3d08 100644 --- a/crates/verify/src/etherscan/mod.rs +++ b/crates/verify/src/etherscan/mod.rs @@ -1,5 +1,8 @@ -use super::{provider::VerificationProvider, VerifyArgs, VerifyCheckArgs}; -use crate::{provider::VerificationContext, retry::RETRY_CHECK_ON_VERIFY}; +use crate::{ + provider::{VerificationContext, VerificationProvider}, + retry::RETRY_CHECK_ON_VERIFY, + verify::{VerifyArgs, VerifyCheckArgs}, +}; use alloy_json_abi::Function; use alloy_primitives::hex; use alloy_provider::Provider; @@ -10,7 +13,7 @@ use foundry_block_explorers::{ verify::{CodeFormat, VerifyContract}, Client, }; -use foundry_cli::utils::{self, read_constructor_args_file, LoadConfig}; +use foundry_cli::utils::{get_provider, read_constructor_args_file, LoadConfig}; use foundry_common::{ abi::encode_function_args, retry::{Retry, RetryError}, @@ -26,6 +29,7 @@ use semver::{BuildMetadata, Version}; use std::fmt::Debug; mod flatten; + mod standard_json; pub static RE_BUILD_COMMIT: Lazy = @@ -48,17 +52,17 @@ trait EtherscanSourceProvider: Send + Sync + Debug { #[async_trait::async_trait] impl VerificationProvider for EtherscanVerificationProvider { - async fn preflight_check( + async fn preflight_verify_check( &mut self, args: VerifyArgs, context: VerificationContext, ) -> Result<()> { - let _ = self.prepare_request(&args, &context).await?; + let _ = self.prepare_verify_request(&args, &context).await?; Ok(()) } async fn verify(&mut self, args: VerifyArgs, context: VerificationContext) -> Result<()> { - let (etherscan, verify_args) = self.prepare_request(&args, &context).await?; + let (etherscan, verify_args) = self.prepare_verify_request(&args, &context).await?; if !args.skip_is_verified_check && self.is_contract_verified(ðerscan, &verify_args).await? @@ -72,7 +76,7 @@ impl VerificationProvider for EtherscanVerificationProvider { return Ok(()) } - trace!(target: "forge::verify", ?verify_args, "submitting verification request"); + trace!(?verify_args, "submitting verification request"); let retry: Retry = args.retry.into(); let resp = retry @@ -87,11 +91,11 @@ impl VerificationProvider for EtherscanVerificationProvider { .wrap_err_with(|| { // valid json let args = serde_json::to_string(&verify_args).unwrap(); - error!(target: "forge::verify", ?args, "Failed to submit verification"); + error!(?args, "Failed to submit verification"); format!("Failed to submit contract verification, payload:\n{args}") })?; - trace!(target: "forge::verify", ?resp, "Received verification response"); + trace!(?resp, "Received verification response"); if resp.status == "0" { if resp.result == "Contract source code already verified" @@ -162,7 +166,7 @@ impl VerificationProvider for EtherscanVerificationProvider { .wrap_err("Failed to request verification status") .map_err(RetryError::Retry)?; - trace!(target: "forge::verify", ?resp, "Received verification response"); + trace!(?resp, "Received verification response"); eprintln!( "Contract verification status:\nResponse: `{}`\nDetails: `{}`", @@ -209,8 +213,8 @@ impl EtherscanVerificationProvider { } } - /// Configures the API request to the etherscan API using the given [`VerifyArgs`]. - async fn prepare_request( + /// Configures the API request to the Etherscan API using the given [`VerifyArgs`]. + async fn prepare_verify_request( &mut self, args: &VerifyArgs, context: &VerificationContext, @@ -227,7 +231,7 @@ impl EtherscanVerificationProvider { Ok((etherscan, verify_args)) } - /// Queries the etherscan API to verify if the contract is already verified. + /// Queries the Etherscan API to verify if the contract is already verified. async fn is_contract_verified( &self, etherscan: &Client, @@ -245,7 +249,7 @@ impl EtherscanVerificationProvider { Ok(true) } - /// Create an etherscan client + /// Create an Etherscan client. pub(crate) fn client( &self, chain: Chain, @@ -284,10 +288,10 @@ impl EtherscanVerificationProvider { builder .with_api_key(etherscan_key.unwrap_or_default()) .build() - .wrap_err("Failed to create etherscan client") + .wrap_err("Failed to create Etherscan client") } - /// Creates the `VerifyContract` etherscan request in order to verify the contract + /// Creates the `VerifyContract` Etherscan request in order to verify the contract /// /// If `--flatten` is set to `true` then this will send with [`CodeFormat::SingleFile`] /// otherwise this will use the [`CodeFormat::StandardJsonInput`] @@ -317,7 +321,7 @@ impl EtherscanVerificationProvider { // we explicitly set this __undocumented__ argument to true if provided by the user, // though this info is also available in the compiler settings of the standard json // object if standard json is used - // unclear how etherscan interprets this field in standard-json mode + // unclear how Etherscan interprets this field in standard-json mode verify_args = verify_args.via_ir(true); } @@ -377,7 +381,7 @@ impl EtherscanVerificationProvider { args: &VerifyArgs, context: &VerificationContext, ) -> Result { - let provider = utils::get_provider(&context.config)?; + let provider = get_provider(&context.config)?; let client = self.client( args.etherscan.chain.unwrap_or_default(), args.verifier.verifier_url.as_deref(), @@ -575,6 +579,6 @@ mod tests { let context = args.resolve_context().await.unwrap(); let mut etherscan = EtherscanVerificationProvider::default(); - etherscan.preflight_check(args, context).await.unwrap(); + etherscan.preflight_verify_check(args, context).await.unwrap(); }); } diff --git a/crates/verify/src/lib.rs b/crates/verify/src/lib.rs index 5edda5b08..9d2372964 100644 --- a/crates/verify/src/lib.rs +++ b/crates/verify/src/lib.rs @@ -1,396 +1,26 @@ -//! # foundry-verify -//! //! Smart contract verification. #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -#[macro_use] -extern crate tracing; - -use alloy_primitives::Address; -use alloy_provider::Provider; -use clap::{Parser, ValueHint}; -use eyre::Result; -use foundry_cli::{ - opts::{EtherscanOpts, RpcOpts}, - utils::{self, LoadConfig}, -}; -use foundry_common::{compile::ProjectCompiler, ContractsByArtifact}; -use foundry_compilers::{artifacts::EvmVersion, compilers::solc::Solc, info::ContractInfo}; -use foundry_config::{figment, impl_figment_convert, impl_figment_convert_cast, Config, SolcReq}; -use itertools::Itertools; -use provider::VerificationProviderType; -use reqwest::Url; -use revm_primitives::HashSet; -use std::path::PathBuf; - mod etherscan; -use etherscan::EtherscanVerificationProvider; pub mod provider; -use provider::VerificationProvider; pub mod bytecode; -pub mod retry; -mod sourcify; +pub use bytecode::VerifyBytecodeArgs; +pub mod retry; pub use retry::RetryArgs; -use crate::provider::VerificationContext; - -/// Verification provider arguments -#[derive(Clone, Debug, Parser)] -pub struct VerifierArgs { - /// The contract verification provider to use. - #[arg(long, help_heading = "Verifier options", default_value = "etherscan", value_enum)] - pub verifier: VerificationProviderType, - - /// The verifier URL, if using a custom provider - #[arg(long, help_heading = "Verifier options", env = "VERIFIER_URL")] - pub verifier_url: Option, -} - -impl Default for VerifierArgs { - fn default() -> Self { - Self { verifier: VerificationProviderType::Etherscan, verifier_url: None } - } -} - -/// CLI arguments for `forge verify`. -#[derive(Clone, Debug, Parser)] -pub struct VerifyArgs { - /// The address of the contract to verify. - pub address: Address, - - /// The contract identifier in the form `:`. - pub contract: Option, - - /// The ABI-encoded constructor arguments. - #[arg( - long, - conflicts_with = "constructor_args_path", - value_name = "ARGS", - visible_alias = "encoded-constructor-args" - )] - pub constructor_args: Option, - - /// The path to a file containing the constructor arguments. - #[arg(long, value_hint = ValueHint::FilePath, value_name = "PATH")] - pub constructor_args_path: Option, - - /// Try to extract constructor arguments from on-chain creation code. - #[arg(long)] - pub guess_constructor_args: bool, - - /// The `solc` version to use to build the smart contract. - #[arg(long, value_name = "VERSION")] - pub compiler_version: Option, - - /// The number of optimization runs used to build the smart contract. - #[arg(long, visible_alias = "optimizer-runs", value_name = "NUM")] - pub num_of_optimizations: Option, - - /// Flatten the source code before verifying. - #[arg(long)] - pub flatten: bool, - - /// Do not compile the flattened smart contract before verifying (if --flatten is passed). - #[arg(short, long)] - pub force: bool, - - /// Do not check if the contract is already verified before verifying. - #[arg(long)] - pub skip_is_verified_check: bool, - - /// Wait for verification result after submission. - #[arg(long)] - pub watch: bool, - - /// Set pre-linked libraries. - #[arg(long, help_heading = "Linker options", env = "DAPP_LIBRARIES")] - pub libraries: Vec, - - /// The project's root path. - /// - /// By default root of the Git repository, if in one, - /// or the current working directory. - #[arg(long, value_hint = ValueHint::DirPath, value_name = "PATH")] - pub root: Option, - - /// Prints the standard json compiler input. - /// - /// The standard json compiler input can be used to manually submit contract verification in - /// the browser. - #[arg(long, conflicts_with = "flatten")] - pub show_standard_json_input: bool, - - /// Use the Yul intermediate representation compilation pipeline. - #[arg(long)] - pub via_ir: bool, - - /// The EVM version to use. - /// - /// Overrides the version specified in the config. - #[arg(long)] - pub evm_version: Option, - - #[command(flatten)] - pub etherscan: EtherscanOpts, - - #[command(flatten)] - pub rpc: RpcOpts, - - #[command(flatten)] - pub retry: RetryArgs, - - #[command(flatten)] - pub verifier: VerifierArgs, -} - -impl_figment_convert!(VerifyArgs); - -impl figment::Provider for VerifyArgs { - fn metadata(&self) -> figment::Metadata { - figment::Metadata::named("Verify Provider") - } - - fn data( - &self, - ) -> Result, figment::Error> { - let mut dict = self.etherscan.dict(); - dict.extend(self.rpc.dict()); - - if let Some(root) = self.root.as_ref() { - dict.insert("root".to_string(), figment::value::Value::serialize(root)?); - } - if let Some(optimizer_runs) = self.num_of_optimizations { - dict.insert("optimizer".to_string(), figment::value::Value::serialize(true)?); - dict.insert( - "optimizer_runs".to_string(), - figment::value::Value::serialize(optimizer_runs)?, - ); - } - if let Some(evm_version) = self.evm_version { - dict.insert("evm_version".to_string(), figment::value::Value::serialize(evm_version)?); - } - if self.via_ir { - dict.insert("via_ir".to_string(), figment::value::Value::serialize(self.via_ir)?); - } - Ok(figment::value::Map::from([(Config::selected_profile(), dict)])) - } -} - -impl VerifyArgs { - /// Run the verify command to submit the contract's source code for verification on etherscan - pub async fn run(mut self) -> Result<()> { - let config = self.load_config_emit_warnings(); - - if self.guess_constructor_args && config.get_rpc_url().is_none() { - eyre::bail!( - "You have to provide a valid RPC URL to use --guess-constructor-args feature" - ) - } - - // If chain is not set, we try to get it from the RPC - // If RPC is not set, the default chain is used - let chain = match config.get_rpc_url() { - Some(_) => { - let provider = utils::get_provider(&config)?; - utils::get_chain(config.chain, provider).await? - } - None => config.chain.unwrap_or_default(), - }; - - let context = self.resolve_context().await?; - - self.etherscan.chain = Some(chain); - self.etherscan.key = config.get_etherscan_config_with_chain(Some(chain))?.map(|c| c.key); - - if self.show_standard_json_input { - let args = EtherscanVerificationProvider::default() - .create_verify_request(&self, &context) - .await?; - println!("{}", args.source); - return Ok(()) - } - - let verifier_url = self.verifier.verifier_url.clone(); - println!("Start verifying contract `{}` deployed on {chain}", self.address); - self.verifier.verifier.client(&self.etherscan.key())?.verify(self, context).await.map_err(|err| { - if let Some(verifier_url) = verifier_url { - match Url::parse(&verifier_url) { - Ok(url) => { - if is_host_only(&url) { - return err.wrap_err(format!( - "Provided URL `{verifier_url}` is host only.\n Did you mean to use the API endpoint`{verifier_url}/api` ?" - )) - } - } - Err(url_err) => { - return err.wrap_err(format!( - "Invalid URL {verifier_url} provided: {url_err}" - )) - } - } - } - - err - }) - } - - /// Returns the configured verification provider - pub fn verification_provider(&self) -> Result> { - self.verifier.verifier.client(&self.etherscan.key()) - } - - /// Resolves [VerificationContext] object either from entered contract name or by trying to - /// match bytecode located at given address. - pub async fn resolve_context(&self) -> Result { - let mut config = self.load_config_emit_warnings(); - config.libraries.extend(self.libraries.clone()); - - let project = config.project()?; - - if let Some(ref contract) = self.contract { - let contract_path = if let Some(ref path) = contract.path { - project.root().join(PathBuf::from(path)) - } else { - project.find_contract_path(&contract.name)? - }; - - let version = if let Some(ref version) = self.compiler_version { - version.trim_start_matches('v').parse()? - } else if let Some(ref solc) = config.solc { - match solc { - SolcReq::Version(version) => version.to_owned(), - SolcReq::Local(solc) => Solc::new(solc)?.version, - } - } else if let Some(entry) = project - .read_cache_file() - .ok() - .and_then(|mut cache| cache.files.remove(&contract_path)) - { - let unique_versions = entry - .artifacts - .get(&contract.name) - .map(|artifacts| artifacts.keys().collect::>()) - .unwrap_or_default(); - - if unique_versions.is_empty() { - eyre::bail!("No matching artifact found for {}", contract.name); - } else if unique_versions.len() > 1 { - warn!( - "Ambiguous compiler versions found in cache: {}", - unique_versions.iter().join(", ") - ); - eyre::bail!("Compiler version has to be set in `foundry.toml`. If the project was not deployed with foundry, specify the version through `--compiler-version` flag.") - } - - unique_versions.into_iter().next().unwrap().to_owned() - } else { - eyre::bail!("If cache is disabled, compiler version must be either provided with `--compiler-version` option or set in foundry.toml") - }; - - VerificationContext::new(contract_path, contract.name.clone(), version, config) - } else { - if config.get_rpc_url().is_none() { - eyre::bail!("You have to provide a contract name or a valid RPC URL") - } - let provider = utils::get_provider(&config)?; - let code = provider.get_code_at(self.address).await?; - - let output = ProjectCompiler::new().compile(&project)?; - let contracts = ContractsByArtifact::new( - output.artifact_ids().map(|(id, artifact)| (id, artifact.clone().into())), - ); - - let Some((artifact_id, _)) = contracts.find_by_deployed_code_exact(&code) else { - eyre::bail!(format!( - "Bytecode at {} does not match any local contracts", - self.address - )) - }; - - VerificationContext::new( - artifact_id.source.clone(), - artifact_id.name.split('.').next().unwrap().to_owned(), - artifact_id.version.clone(), - config, - ) - } - } -} - -/// Check verification status arguments -#[derive(Clone, Debug, Parser)] -pub struct VerifyCheckArgs { - /// The verification ID. - /// - /// For Etherscan - Submission GUID. - /// - /// For Sourcify - Contract Address. - id: String, - - #[command(flatten)] - retry: RetryArgs, - - #[command(flatten)] - etherscan: EtherscanOpts, - - #[command(flatten)] - verifier: VerifierArgs, -} - -impl_figment_convert_cast!(VerifyCheckArgs); - -impl VerifyCheckArgs { - /// Run the verify command to submit the contract's source code for verification on etherscan - pub async fn run(self) -> Result<()> { - println!("Checking verification status on {}", self.etherscan.chain.unwrap_or_default()); - self.verifier.verifier.client(&self.etherscan.key())?.check(self).await - } -} - -impl figment::Provider for VerifyCheckArgs { - fn metadata(&self) -> figment::Metadata { - figment::Metadata::named("Verify Check Provider") - } - - fn data( - &self, - ) -> Result, figment::Error> { - self.etherscan.data() - } -} +mod sourcify; -/// Returns `true` if the URL only consists of host. -/// -/// This is used to check user input url for missing /api path -#[inline] -fn is_host_only(url: &Url) -> bool { - matches!(url.path(), "/" | "") -} +pub mod verify; +pub use verify::{VerifierArgs, VerifyArgs, VerifyCheckArgs}; -#[cfg(test)] -mod tests { - use super::*; +mod types; - #[test] - fn test_host_only() { - assert!(!is_host_only(&Url::parse("https://blockscout.net/api").unwrap())); - assert!(is_host_only(&Url::parse("https://blockscout.net/").unwrap())); - assert!(is_host_only(&Url::parse("https://blockscout.net").unwrap())); - } +mod utils; - #[test] - fn can_parse_verify_contract() { - let args: VerifyArgs = VerifyArgs::parse_from([ - "foundry-cli", - "0x0000000000000000000000000000000000000000", - "src/Domains.sol:Domains", - "--via-ir", - ]); - assert!(args.via_ir); - } -} +#[macro_use] +extern crate tracing; diff --git a/crates/verify/src/provider.rs b/crates/verify/src/provider.rs index 46ca0b944..1506585ab 100644 --- a/crates/verify/src/provider.rs +++ b/crates/verify/src/provider.rs @@ -1,6 +1,7 @@ -use super::{ - etherscan::EtherscanVerificationProvider, sourcify::SourcifyVerificationProvider, VerifyArgs, - VerifyCheckArgs, +use crate::{ + etherscan::EtherscanVerificationProvider, + sourcify::SourcifyVerificationProvider, + verify::{VerifyArgs, VerifyCheckArgs}, }; use alloy_json_abi::JsonAbi; use async_trait::async_trait; @@ -101,7 +102,7 @@ pub trait VerificationProvider { /// [`VerifyArgs`] are valid to begin with. This should prevent situations where there's a /// contract deployment that's executed before the verify request and the subsequent verify task /// fails due to misconfiguration. - async fn preflight_check( + async fn preflight_verify_check( &mut self, args: VerifyArgs, context: VerificationContext, diff --git a/crates/verify/src/sourcify.rs b/crates/verify/src/sourcify.rs index 58cb2b4b9..81fadd09e 100644 --- a/crates/verify/src/sourcify.rs +++ b/crates/verify/src/sourcify.rs @@ -1,5 +1,7 @@ -use super::{provider::VerificationProvider, VerifyArgs, VerifyCheckArgs}; -use crate::provider::VerificationContext; +use crate::{ + provider::{VerificationContext, VerificationProvider}, + verify::{VerifyArgs, VerifyCheckArgs}, +}; use async_trait::async_trait; use eyre::Result; use foundry_common::{fs, retry::Retry}; @@ -17,7 +19,7 @@ pub struct SourcifyVerificationProvider; #[async_trait] impl VerificationProvider for SourcifyVerificationProvider { - async fn preflight_check( + async fn preflight_verify_check( &mut self, args: VerifyArgs, context: VerificationContext, diff --git a/crates/verify/src/types.rs b/crates/verify/src/types.rs new file mode 100644 index 000000000..20d864996 --- /dev/null +++ b/crates/verify/src/types.rs @@ -0,0 +1,44 @@ +use eyre::Result; +use serde::{Deserialize, Serialize}; +use std::{fmt, str::FromStr}; + +/// Enum to represent the type of verification: `full` or `partial`. +/// Ref: +#[derive(Debug, Clone, clap::ValueEnum, Default, PartialEq, Eq, Serialize, Deserialize, Copy)] +pub enum VerificationType { + #[default] + #[serde(rename = "full")] + Full, + #[serde(rename = "partial")] + Partial, +} + +impl FromStr for VerificationType { + type Err = eyre::Error; + + fn from_str(s: &str) -> Result { + match s { + "full" => Ok(Self::Full), + "partial" => Ok(Self::Partial), + _ => eyre::bail!("Invalid verification type"), + } + } +} + +impl From for String { + fn from(v: VerificationType) -> Self { + match v { + VerificationType::Full => "full".to_string(), + VerificationType::Partial => "partial".to_string(), + } + } +} + +impl fmt::Display for VerificationType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Full => write!(f, "full"), + Self::Partial => write!(f, "partial"), + } + } +} diff --git a/crates/verify/src/utils.rs b/crates/verify/src/utils.rs new file mode 100644 index 000000000..d5545df67 --- /dev/null +++ b/crates/verify/src/utils.rs @@ -0,0 +1,423 @@ +use crate::{bytecode::VerifyBytecodeArgs, types::VerificationType}; +use alloy_dyn_abi::DynSolValue; +use alloy_primitives::{Address, Bytes, U256}; +use alloy_provider::Provider; +use alloy_rpc_types::{Block, BlockId, Transaction}; +use clap::ValueEnum; +use eyre::{OptionExt, Result}; +use foundry_block_explorers::{ + contract::{ContractCreationData, ContractMetadata, Metadata}, + errors::EtherscanError, +}; +use foundry_common::{abi::encode_args, compile::ProjectCompiler, provider::RetryProvider}; +use foundry_compilers::artifacts::{BytecodeHash, CompactContractBytecode, EvmVersion}; +use foundry_config::Config; +use foundry_evm::{constants::DEFAULT_CREATE2_DEPLOYER, executors::TracingExecutor, opts::EvmOpts}; +use reqwest::Url; +use revm_primitives::{ + db::Database, + env::{EnvWithHandlerCfg, HandlerCfg}, + Bytecode, Env, SpecId, +}; +use semver::Version; +use serde::{Deserialize, Serialize}; +use yansi::Paint; + +/// Enum to represent the type of bytecode being verified +#[derive(Debug, Serialize, Deserialize, Clone, Copy, ValueEnum)] +pub enum BytecodeType { + #[serde(rename = "creation")] + Creation, + #[serde(rename = "runtime")] + Runtime, +} + +impl BytecodeType { + /// Check if the bytecode type is creation + pub fn is_creation(&self) -> bool { + matches!(self, Self::Creation) + } + + /// Check if the bytecode type is runtime + pub fn is_runtime(&self) -> bool { + matches!(self, Self::Runtime) + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct JsonResult { + pub bytecode_type: BytecodeType, + pub match_type: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub message: Option, +} + +pub fn match_bytecodes( + local_bytecode: &[u8], + bytecode: &[u8], + constructor_args: &[u8], + is_runtime: bool, + bytecode_hash: BytecodeHash, +) -> Option { + // 1. Try full match + if local_bytecode == bytecode { + // If the bytecode_hash = 'none' in Config. Then it's always a partial match according to + // sourcify definitions. Ref: https://docs.sourcify.dev/docs/full-vs-partial-match/. + if bytecode_hash == BytecodeHash::None { + return Some(VerificationType::Partial); + } + + Some(VerificationType::Full) + } else { + is_partial_match(local_bytecode, bytecode, constructor_args, is_runtime) + .then_some(VerificationType::Partial) + } +} + +pub fn build_project( + args: &VerifyBytecodeArgs, + config: &Config, +) -> Result { + let project = config.project()?; + let compiler = ProjectCompiler::new(); + + let mut output = compiler.compile(&project)?; + + let artifact = output + .remove_contract(&args.contract) + .ok_or_eyre("Build Error: Contract artifact not found locally")?; + + Ok(artifact.into_contract_bytecode()) +} + +pub fn build_using_cache( + args: &VerifyBytecodeArgs, + etherscan_settings: &Metadata, + config: &Config, +) -> Result { + let project = config.project()?; + let cache = project.read_cache_file()?; + let cached_artifacts = cache.read_artifacts::()?; + + for (key, value) in cached_artifacts { + let name = args.contract.name.to_owned() + ".sol"; + let version = etherscan_settings.compiler_version.to_owned(); + // Ignores vyper + if version.starts_with("vyper:") { + eyre::bail!("Vyper contracts are not supported") + } + // Parse etherscan version string + let version = version.split('+').next().unwrap_or("").trim_start_matches('v').to_string(); + + // Check if `out/directory` name matches the contract name + if key.ends_with(name.as_str()) { + let name = name.replace(".sol", ".json"); + for artifact in value.into_values().flatten() { + // Check if ABI file matches the name + if !artifact.file.ends_with(&name) { + continue; + } + + // Check if Solidity version matches + if let Ok(version) = Version::parse(&version) { + if !(artifact.version.major == version.major && + artifact.version.minor == version.minor && + artifact.version.patch == version.patch) + { + continue; + } + } + + return Ok(artifact.artifact) + } + } + } + + eyre::bail!("couldn't find cached artifact for contract {}", args.contract.name) +} + +pub fn print_result( + args: &VerifyBytecodeArgs, + res: Option, + bytecode_type: BytecodeType, + json_results: &mut Vec, + etherscan_config: &Metadata, + config: &Config, +) { + if let Some(res) = res { + if !args.json { + println!( + "{} with status {}", + format!("{bytecode_type:?} code matched").green().bold(), + res.green().bold() + ); + } else { + let json_res = JsonResult { bytecode_type, match_type: Some(res), message: None }; + json_results.push(json_res); + } + } else if !args.json { + println!( + "{}", + format!( + "{bytecode_type:?} code did not match - this may be due to varying compiler settings" + ) + .red() + .bold() + ); + let mismatches = find_mismatch_in_settings(etherscan_config, config); + for mismatch in mismatches { + println!("{}", mismatch.red().bold()); + } + } else { + let json_res = JsonResult { + bytecode_type, + match_type: res, + message: Some(format!( + "{bytecode_type:?} code did not match - this may be due to varying compiler settings" + )), + }; + json_results.push(json_res); + } +} + +fn is_partial_match( + mut local_bytecode: &[u8], + mut bytecode: &[u8], + constructor_args: &[u8], + is_runtime: bool, +) -> bool { + // 1. Check length of constructor args + if constructor_args.is_empty() || is_runtime { + // Assume metadata is at the end of the bytecode + return try_extract_and_compare_bytecode(local_bytecode, bytecode) + } + + // If not runtime, extract constructor args from the end of the bytecode + bytecode = &bytecode[..bytecode.len() - constructor_args.len()]; + local_bytecode = &local_bytecode[..local_bytecode.len() - constructor_args.len()]; + + try_extract_and_compare_bytecode(local_bytecode, bytecode) +} + +fn try_extract_and_compare_bytecode(mut local_bytecode: &[u8], mut bytecode: &[u8]) -> bool { + local_bytecode = extract_metadata_hash(local_bytecode); + bytecode = extract_metadata_hash(bytecode); + + // Now compare the local code and bytecode + local_bytecode == bytecode +} + +/// @dev This assumes that the metadata is at the end of the bytecode +fn extract_metadata_hash(bytecode: &[u8]) -> &[u8] { + // Get the last two bytes of the bytecode to find the length of CBOR metadata + let metadata_len = &bytecode[bytecode.len() - 2..]; + let metadata_len = u16::from_be_bytes([metadata_len[0], metadata_len[1]]); + + if metadata_len as usize <= bytecode.len() { + if ciborium::from_reader::( + &bytecode[bytecode.len() - 2 - metadata_len as usize..bytecode.len() - 2], + ) + .is_ok() + { + &bytecode[..bytecode.len() - 2 - metadata_len as usize] + } else { + bytecode + } + } else { + bytecode + } +} + +fn find_mismatch_in_settings( + etherscan_settings: &Metadata, + local_settings: &Config, +) -> Vec { + let mut mismatches: Vec = vec![]; + if etherscan_settings.evm_version != local_settings.evm_version.to_string().to_lowercase() { + let str = format!( + "EVM version mismatch: local={}, onchain={}", + local_settings.evm_version, etherscan_settings.evm_version + ); + mismatches.push(str); + } + let local_optimizer: u64 = if local_settings.optimizer { 1 } else { 0 }; + if etherscan_settings.optimization_used != local_optimizer { + let str = format!( + "Optimizer mismatch: local={}, onchain={}", + local_settings.optimizer, etherscan_settings.optimization_used + ); + mismatches.push(str); + } + if etherscan_settings.runs != local_settings.optimizer_runs as u64 { + let str = format!( + "Optimizer runs mismatch: local={}, onchain={}", + local_settings.optimizer_runs, etherscan_settings.runs + ); + mismatches.push(str); + } + + mismatches +} + +pub fn maybe_predeploy_contract( + creation_data: Result, +) -> Result<(Option, bool), eyre::ErrReport> { + let mut maybe_predeploy = false; + match creation_data { + Ok(creation_data) => Ok((Some(creation_data), maybe_predeploy)), + // Ref: https://explorer.mode.network/api?module=contract&action=getcontractcreation&contractaddresses=0xC0d3c0d3c0D3c0d3C0D3c0D3C0d3C0D3C0D30010 + Err(EtherscanError::EmptyResult { status, message }) + if status == "1" && message == "OK" => + { + maybe_predeploy = true; + Ok((None, maybe_predeploy)) + } + // Ref: https://api.basescan.org/api?module=contract&action=getcontractcreation&contractaddresses=0xC0d3c0d3c0D3c0d3C0D3c0D3C0d3C0D3C0D30010&apiKey=YourAPIKey + Err(EtherscanError::Serde { error: _, content }) if content.contains("GENESIS") => { + maybe_predeploy = true; + Ok((None, maybe_predeploy)) + } + Err(e) => eyre::bail!("Error fetching creation data from verifier-url: {:?}", e), + } +} + +pub fn check_and_encode_args( + artifact: &CompactContractBytecode, + args: Vec, +) -> Result, eyre::ErrReport> { + if let Some(constructor) = artifact.abi.as_ref().and_then(|abi| abi.constructor()) { + if constructor.inputs.len() != args.len() { + eyre::bail!( + "Mismatch of constructor arguments length. Expected {}, got {}", + constructor.inputs.len(), + args.len() + ); + } + encode_args(&constructor.inputs, &args).map(|args| DynSolValue::Tuple(args).abi_encode()) + } else { + Ok(Vec::new()) + } +} + +pub fn check_explorer_args(source_code: ContractMetadata) -> Result { + if let Some(args) = source_code.items.first() { + Ok(args.constructor_arguments.clone()) + } else { + eyre::bail!("No constructor arguments found from block explorer"); + } +} + +pub async fn get_tracing_executor( + fork_config: &mut Config, + fork_blk_num: u64, + evm_version: EvmVersion, + evm_opts: EvmOpts, +) -> Result<(Env, TracingExecutor)> { + fork_config.fork_block_number = Some(fork_blk_num); + fork_config.evm_version = evm_version; + + let (env, fork, _chain, is_alphanet) = + TracingExecutor::get_fork_material(fork_config, evm_opts).await?; + + let executor = TracingExecutor::new( + env.clone(), + fork, + Some(fork_config.evm_version), + false, + false, + is_alphanet, + ); + + Ok((env, executor)) +} + +pub fn configure_env_block(env: &mut Env, block: &Block) { + env.block.timestamp = U256::from(block.header.timestamp); + env.block.coinbase = block.header.miner; + env.block.difficulty = block.header.difficulty; + env.block.prevrandao = Some(block.header.mix_hash.unwrap_or_default()); + env.block.basefee = U256::from(block.header.base_fee_per_gas.unwrap_or_default()); + env.block.gas_limit = U256::from(block.header.gas_limit); +} + +pub fn deploy_contract( + executor: &mut TracingExecutor, + env: &Env, + spec_id: SpecId, + transaction: &Transaction, +) -> Result { + let env_with_handler = EnvWithHandlerCfg::new(Box::new(env.clone()), HandlerCfg::new(spec_id)); + + if let Some(to) = transaction.to { + if to != DEFAULT_CREATE2_DEPLOYER { + eyre::bail!("Transaction `to` address is not the default create2 deployer i.e the tx is not a contract creation tx."); + } + let result = executor.transact_with_env(env_with_handler)?; + + trace!(transact_result = ?result.exit_reason); + if result.result.len() != 20 { + eyre::bail!( + "Failed to deploy contract on fork at block: call result is not exactly 20 bytes" + ); + } + + Ok(Address::from_slice(&result.result)) + } else { + let deploy_result = executor.deploy_with_env(env_with_handler, None)?; + trace!(deploy_result = ?deploy_result.raw.exit_reason); + Ok(deploy_result.address) + } +} + +pub async fn get_runtime_codes( + executor: &mut TracingExecutor, + provider: &RetryProvider, + address: Address, + fork_address: Address, + block: Option, +) -> Result<(Bytecode, Bytes)> { + let fork_runtime_code = executor + .backend_mut() + .basic(fork_address)? + .ok_or_else(|| { + eyre::eyre!( + "Failed to get runtime code for contract deployed on fork at address {}", + fork_address + ) + })? + .code + .ok_or_else(|| { + eyre::eyre!( + "Bytecode does not exist for contract deployed on fork at address {}", + fork_address + ) + })?; + + let onchain_runtime_code = if let Some(block) = block { + provider.get_code_at(address).block_id(BlockId::number(block)).await? + } else { + provider.get_code_at(address).await? + }; + + Ok((fork_runtime_code, onchain_runtime_code)) +} + +/// Returns `true` if the URL only consists of host. +/// +/// This is used to check user input url for missing /api path +#[inline] +pub fn is_host_only(url: &Url) -> bool { + matches!(url.path(), "/" | "") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_host_only() { + assert!(!is_host_only(&Url::parse("https://blockscout.net/api").unwrap())); + assert!(is_host_only(&Url::parse("https://blockscout.net/").unwrap())); + assert!(is_host_only(&Url::parse("https://blockscout.net").unwrap())); + } +} diff --git a/crates/verify/src/verify.rs b/crates/verify/src/verify.rs new file mode 100644 index 000000000..4a3abaa36 --- /dev/null +++ b/crates/verify/src/verify.rs @@ -0,0 +1,367 @@ +//! The `forge verify-bytecode` command. + +use crate::{ + etherscan::EtherscanVerificationProvider, + provider::{VerificationProvider, VerificationProviderType}, + utils::is_host_only, + RetryArgs, +}; +use alloy_primitives::Address; +use alloy_provider::Provider; +use clap::{Parser, ValueHint}; +use eyre::Result; +use foundry_cli::{ + opts::{EtherscanOpts, RpcOpts}, + utils::{self, LoadConfig}, +}; +use foundry_common::{compile::ProjectCompiler, ContractsByArtifact}; +use foundry_compilers::{artifacts::EvmVersion, compilers::solc::Solc, info::ContractInfo}; +use foundry_config::{figment, impl_figment_convert, impl_figment_convert_cast, Config, SolcReq}; +use itertools::Itertools; +use reqwest::Url; +use revm_primitives::HashSet; +use std::path::PathBuf; + +use crate::provider::VerificationContext; + +/// Verification provider arguments +#[derive(Clone, Debug, Parser)] +pub struct VerifierArgs { + /// The contract verification provider to use. + #[arg(long, help_heading = "Verifier options", default_value = "etherscan", value_enum)] + pub verifier: VerificationProviderType, + + /// The verifier URL, if using a custom provider + #[arg(long, help_heading = "Verifier options", env = "VERIFIER_URL")] + pub verifier_url: Option, +} + +impl Default for VerifierArgs { + fn default() -> Self { + Self { verifier: VerificationProviderType::Etherscan, verifier_url: None } + } +} + +/// CLI arguments for `forge verify`. +#[derive(Clone, Debug, Parser)] +pub struct VerifyArgs { + /// The address of the contract to verify. + pub address: Address, + + /// The contract identifier in the form `:`. + pub contract: Option, + + /// The ABI-encoded constructor arguments. + #[arg( + long, + conflicts_with = "constructor_args_path", + value_name = "ARGS", + visible_alias = "encoded-constructor-args" + )] + pub constructor_args: Option, + + /// The path to a file containing the constructor arguments. + #[arg(long, value_hint = ValueHint::FilePath, value_name = "PATH")] + pub constructor_args_path: Option, + + /// Try to extract constructor arguments from on-chain creation code. + #[arg(long)] + pub guess_constructor_args: bool, + + /// The `solc` version to use to build the smart contract. + #[arg(long, value_name = "VERSION")] + pub compiler_version: Option, + + /// The number of optimization runs used to build the smart contract. + #[arg(long, visible_alias = "optimizer-runs", value_name = "NUM")] + pub num_of_optimizations: Option, + + /// Flatten the source code before verifying. + #[arg(long)] + pub flatten: bool, + + /// Do not compile the flattened smart contract before verifying (if --flatten is passed). + #[arg(short, long)] + pub force: bool, + + /// Do not check if the contract is already verified before verifying. + #[arg(long)] + pub skip_is_verified_check: bool, + + /// Wait for verification result after submission. + #[arg(long)] + pub watch: bool, + + /// Set pre-linked libraries. + #[arg(long, help_heading = "Linker options", env = "DAPP_LIBRARIES")] + pub libraries: Vec, + + /// The project's root path. + /// + /// By default root of the Git repository, if in one, + /// or the current working directory. + #[arg(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + pub root: Option, + + /// Prints the standard json compiler input. + /// + /// The standard json compiler input can be used to manually submit contract verification in + /// the browser. + #[arg(long, conflicts_with = "flatten")] + pub show_standard_json_input: bool, + + /// Use the Yul intermediate representation compilation pipeline. + #[arg(long)] + pub via_ir: bool, + + /// The EVM version to use. + /// + /// Overrides the version specified in the config. + #[arg(long)] + pub evm_version: Option, + + #[command(flatten)] + pub etherscan: EtherscanOpts, + + #[command(flatten)] + pub rpc: RpcOpts, + + #[command(flatten)] + pub retry: RetryArgs, + + #[command(flatten)] + pub verifier: VerifierArgs, +} + +impl_figment_convert!(VerifyArgs); + +impl figment::Provider for VerifyArgs { + fn metadata(&self) -> figment::Metadata { + figment::Metadata::named("Verify Provider") + } + + fn data( + &self, + ) -> Result, figment::Error> { + let mut dict = self.etherscan.dict(); + dict.extend(self.rpc.dict()); + + if let Some(root) = self.root.as_ref() { + dict.insert("root".to_string(), figment::value::Value::serialize(root)?); + } + if let Some(optimizer_runs) = self.num_of_optimizations { + dict.insert("optimizer".to_string(), figment::value::Value::serialize(true)?); + dict.insert( + "optimizer_runs".to_string(), + figment::value::Value::serialize(optimizer_runs)?, + ); + } + if let Some(evm_version) = self.evm_version { + dict.insert("evm_version".to_string(), figment::value::Value::serialize(evm_version)?); + } + if self.via_ir { + dict.insert("via_ir".to_string(), figment::value::Value::serialize(self.via_ir)?); + } + Ok(figment::value::Map::from([(Config::selected_profile(), dict)])) + } +} + +impl VerifyArgs { + /// Run the verify command to submit the contract's source code for verification on etherscan + pub async fn run(mut self) -> Result<()> { + let config = self.load_config_emit_warnings(); + + if self.guess_constructor_args && config.get_rpc_url().is_none() { + eyre::bail!( + "You have to provide a valid RPC URL to use --guess-constructor-args feature" + ) + } + + // If chain is not set, we try to get it from the RPC. + // If RPC is not set, the default chain is used. + let chain = match config.get_rpc_url() { + Some(_) => { + let provider = utils::get_provider(&config)?; + utils::get_chain(config.chain, provider).await? + } + None => config.chain.unwrap_or_default(), + }; + + let context = self.resolve_context().await?; + + // Set Etherscan options. + self.etherscan.chain = Some(chain); + self.etherscan.key = config.get_etherscan_config_with_chain(Some(chain))?.map(|c| c.key); + + if self.show_standard_json_input { + let args = EtherscanVerificationProvider::default() + .create_verify_request(&self, &context) + .await?; + println!("{}", args.source); + return Ok(()) + } + + let verifier_url = self.verifier.verifier_url.clone(); + println!("Start verifying contract `{}` deployed on {chain}", self.address); + self.verifier.verifier.client(&self.etherscan.key())?.verify(self, context).await.map_err(|err| { + if let Some(verifier_url) = verifier_url { + match Url::parse(&verifier_url) { + Ok(url) => { + if is_host_only(&url) { + return err.wrap_err(format!( + "Provided URL `{verifier_url}` is host only.\n Did you mean to use the API endpoint`{verifier_url}/api` ?" + )) + } + } + Err(url_err) => { + return err.wrap_err(format!( + "Invalid URL {verifier_url} provided: {url_err}" + )) + } + } + } + + err + }) + } + + /// Returns the configured verification provider + pub fn verification_provider(&self) -> Result> { + self.verifier.verifier.client(&self.etherscan.key()) + } + + /// Resolves [VerificationContext] object either from entered contract name or by trying to + /// match bytecode located at given address. + pub async fn resolve_context(&self) -> Result { + let mut config = self.load_config_emit_warnings(); + config.libraries.extend(self.libraries.clone()); + + let project = config.project()?; + + if let Some(ref contract) = self.contract { + let contract_path = if let Some(ref path) = contract.path { + project.root().join(PathBuf::from(path)) + } else { + project.find_contract_path(&contract.name)? + }; + + let version = if let Some(ref version) = self.compiler_version { + version.trim_start_matches('v').parse()? + } else if let Some(ref solc) = config.solc { + match solc { + SolcReq::Version(version) => version.to_owned(), + SolcReq::Local(solc) => Solc::new(solc)?.version, + } + } else if let Some(entry) = project + .read_cache_file() + .ok() + .and_then(|mut cache| cache.files.remove(&contract_path)) + { + let unique_versions = entry + .artifacts + .get(&contract.name) + .map(|artifacts| artifacts.keys().collect::>()) + .unwrap_or_default(); + + if unique_versions.is_empty() { + eyre::bail!("No matching artifact found for {}", contract.name); + } else if unique_versions.len() > 1 { + warn!( + "Ambiguous compiler versions found in cache: {}", + unique_versions.iter().join(", ") + ); + eyre::bail!("Compiler version has to be set in `foundry.toml`. If the project was not deployed with foundry, specify the version through `--compiler-version` flag.") + } + + unique_versions.into_iter().next().unwrap().to_owned() + } else { + eyre::bail!("If cache is disabled, compiler version must be either provided with `--compiler-version` option or set in foundry.toml") + }; + + VerificationContext::new(contract_path, contract.name.clone(), version, config) + } else { + if config.get_rpc_url().is_none() { + eyre::bail!("You have to provide a contract name or a valid RPC URL") + } + let provider = utils::get_provider(&config)?; + let code = provider.get_code_at(self.address).await?; + + let output = ProjectCompiler::new().compile(&project)?; + let contracts = ContractsByArtifact::new( + output.artifact_ids().map(|(id, artifact)| (id, artifact.clone().into())), + ); + + let Some((artifact_id, _)) = contracts.find_by_deployed_code_exact(&code) else { + eyre::bail!(format!( + "Bytecode at {} does not match any local contracts", + self.address + )) + }; + + VerificationContext::new( + artifact_id.source.clone(), + artifact_id.name.split('.').next().unwrap().to_owned(), + artifact_id.version.clone(), + config, + ) + } + } +} + +/// Check verification status arguments +#[derive(Clone, Debug, Parser)] +pub struct VerifyCheckArgs { + /// The verification ID. + /// + /// For Etherscan - Submission GUID. + /// + /// For Sourcify - Contract Address. + pub id: String, + + #[command(flatten)] + pub retry: RetryArgs, + + #[command(flatten)] + pub etherscan: EtherscanOpts, + + #[command(flatten)] + pub verifier: VerifierArgs, +} + +impl_figment_convert_cast!(VerifyCheckArgs); + +impl VerifyCheckArgs { + /// Run the verify command to submit the contract's source code for verification on etherscan + pub async fn run(self) -> Result<()> { + println!("Checking verification status on {}", self.etherscan.chain.unwrap_or_default()); + self.verifier.verifier.client(&self.etherscan.key())?.check(self).await + } +} + +impl figment::Provider for VerifyCheckArgs { + fn metadata(&self) -> figment::Metadata { + figment::Metadata::named("Verify Check Provider") + } + + fn data( + &self, + ) -> Result, figment::Error> { + self.etherscan.data() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_parse_verify_contract() { + let args: VerifyArgs = VerifyArgs::parse_from([ + "foundry-cli", + "0x0000000000000000000000000000000000000000", + "src/Domains.sol:Domains", + "--via-ir", + ]); + assert!(args.via_ir); + } +} From ddb49a40305a9b10f0be97efae7f0c66bf720e13 Mon Sep 17 00:00:00 2001 From: justinmoore-next Date: Wed, 21 Aug 2024 23:37:56 +0800 Subject: [PATCH 091/184] feat(cast): add `cast hash-message` (#8706) * feat(cast): add `cast hash-message` * Update crates/cast/bin/opts.rs --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/cast/bin/main.rs | 10 +++++++++- crates/cast/bin/opts.rs | 7 +++++++ crates/cast/tests/cli/main.rs | 11 +++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 8bc0b9866..c3bd5039f 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -1,7 +1,7 @@ #[macro_use] extern crate tracing; -use alloy_primitives::{hex, keccak256, Address, B256}; +use alloy_primitives::{eip191_hash_message, hex, keccak256, Address, B256}; use alloy_provider::Provider; use alloy_rpc_types::{BlockId, BlockNumberOrTag::Latest}; use cast::{Cast, SimpleCast}; @@ -515,6 +515,14 @@ async fn main() -> Result<()> { } }; } + CastSubcommand::HashMessage { message } => { + let message = stdin::unwrap_line(message)?; + let input = match message.strip_prefix("0x") { + Some(hex_str) => hex::decode(hex_str)?, + None => message.as_bytes().to_vec(), + }; + println!("{}", eip191_hash_message(input)); + } CastSubcommand::SigEvent { event_string } => { let event_string = stdin::unwrap_line(event_string)?; let parsed_event = get_event(&event_string)?; diff --git a/crates/cast/bin/opts.rs b/crates/cast/bin/opts.rs index 395b4fe27..727458505 100644 --- a/crates/cast/bin/opts.rs +++ b/crates/cast/bin/opts.rs @@ -754,6 +754,13 @@ pub enum CastSubcommand { data: Option, }, + /// Hash a message according to EIP-191. + #[command(visible_aliases = &["--hash-message", "hm"])] + HashMessage { + /// The message to hash. + message: Option, + }, + /// Perform an ENS lookup. #[command(visible_alias = "rn")] ResolveName { diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index d0ff5a881..fcbeaf422 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1057,3 +1057,14 @@ casttest!(send_eip7702, async |_prj, cmd| { "#]]); }); + +casttest!(hash_message, |_prj, cmd| { + let tests = [ + ("hello", "0x50b2c43fd39106bafbba0da34fc430e1f91e3c96ea2acee2bc34119f92b37750"), + ("0x68656c6c6f", "0x50b2c43fd39106bafbba0da34fc430e1f91e3c96ea2acee2bc34119f92b37750"), + ]; + for (message, expected) in tests { + cmd.cast_fuse(); + assert_eq!(cmd.args(["hash-message", message]).stdout_lossy().trim(), expected); + } +}); From 503fbeefe3e10ce624ed5ef80da029aa6bb7eab6 Mon Sep 17 00:00:00 2001 From: James Kim Date: Wed, 21 Aug 2024 16:15:43 -0400 Subject: [PATCH 092/184] fix(anvil): for deposit transactions apply tx.mint value in tx validation (#8704) * apply mint value before checking sufficient funds conditions * remove comment * update log * add docs link * add docs link --- .../core/src/eth/transaction/optimism.rs | 2 +- crates/anvil/src/eth/backend/mem/mod.rs | 34 +++++-- crates/anvil/tests/it/optimism.rs | 89 ++++++++++--------- 3 files changed, 74 insertions(+), 51 deletions(-) diff --git a/crates/anvil/core/src/eth/transaction/optimism.rs b/crates/anvil/core/src/eth/transaction/optimism.rs index fb987ae3d..5c4f35481 100644 --- a/crates/anvil/core/src/eth/transaction/optimism.rs +++ b/crates/anvil/core/src/eth/transaction/optimism.rs @@ -268,7 +268,7 @@ impl DepositTransaction { None } - pub(crate) fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) { + pub fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) { out.put_u8(DEPOSIT_TX_TYPE_ID); self.encode(out); } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 5180e4a04..a089a9d2b 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -2573,16 +2573,32 @@ impl TransactionValidator for Backend { let max_cost = tx.max_cost(); let value = tx.value(); - // check sufficient funds: `gas * price + value` - let req_funds = max_cost.checked_add(value.to()).ok_or_else(|| { - warn!(target: "backend", "[{:?}] cost too high", - tx.hash()); - InvalidTransactionError::InsufficientFunds - })?; - if account.balance < U256::from(req_funds) { - warn!(target: "backend", "[{:?}] insufficient allowance={}, required={} account={:?}", tx.hash(), account.balance, req_funds, *pending.sender()); - return Err(InvalidTransactionError::InsufficientFunds); + + match &tx.transaction { + TypedTransaction::Deposit(deposit_tx) => { + // Deposit transactions + // https://specs.optimism.io/protocol/deposits.html#execution + // 1. no gas cost check required since already have prepaid gas from L1 + // 2. increment account balance by deposited amount before checking for sufficient + // funds `tx.value <= existing account value + deposited value` + if value > account.balance + deposit_tx.mint { + warn!(target: "backend", "[{:?}] insufficient balance={}, required={} account={:?}", tx.hash(), account.balance + deposit_tx.mint, value, *pending.sender()); + return Err(InvalidTransactionError::InsufficientFunds); + } + } + _ => { + // check sufficient funds: `gas * price + value` + let req_funds = max_cost.checked_add(value.to()).ok_or_else(|| { + warn!(target: "backend", "[{:?}] cost too high", tx.hash()); + InvalidTransactionError::InsufficientFunds + })?; + if account.balance < U256::from(req_funds) { + warn!(target: "backend", "[{:?}] insufficient allowance={}, required={} account={:?}", tx.hash(), account.balance, req_funds, *pending.sender()); + return Err(InvalidTransactionError::InsufficientFunds); + } + } } + Ok(()) } diff --git a/crates/anvil/tests/it/optimism.rs b/crates/anvil/tests/it/optimism.rs index 8e23d7ded..f5f4a41ca 100644 --- a/crates/anvil/tests/it/optimism.rs +++ b/crates/anvil/tests/it/optimism.rs @@ -1,13 +1,14 @@ //! Tests for OP chain support. -use crate::utils::http_provider_with_signer; +use crate::utils::{http_provider, http_provider_with_signer}; use alloy_eips::eip2718::Encodable2718; use alloy_network::{EthereumWallet, TransactionBuilder}; -use alloy_primitives::{b256, Address, TxHash, U256}; +use alloy_primitives::{b256, Address, TxHash, TxKind, U256}; use alloy_provider::Provider; use alloy_rpc_types::{optimism::OptimismTransactionFields, TransactionRequest}; use alloy_serde::WithOtherFields; use anvil::{spawn, Hardfork, NodeConfig}; +use anvil_core::eth::transaction::optimism::DepositTransaction; #[tokio::test(flavor = "multi_thread")] async fn test_deposits_not_supported_if_optimism_disabled() { @@ -148,13 +149,11 @@ async fn test_send_value_raw_deposit_transaction() { #[tokio::test(flavor = "multi_thread")] async fn test_deposit_transaction_hash_matches_sepolia() { // enable the Optimism flag - let (api, handle) = + let (_api, handle) = spawn(NodeConfig::test().with_optimism(true).with_hardfork(Some(Hardfork::Paris))).await; let accounts: Vec<_> = handle.dev_wallets().collect(); let signer: EthereumWallet = accounts[0].clone().into(); - let sender_addr = accounts[0].address(); - // https://sepolia-optimism.etherscan.io/tx/0xbf8b5f08c43e4b860715cd64fc0849bbce0d0ea20a76b269e7bc8886d112fca7 let tx_hash: TxHash = "0xbf8b5f08c43e4b860715cd64fc0849bbce0d0ea20a76b269e7bc8886d112fca7" .parse::() @@ -165,52 +164,60 @@ async fn test_deposit_transaction_hash_matches_sepolia() { "7ef861a0dfd7ae78bf3c414cfaa77f13c0205c82eb9365e217b2daa3448c3156b69b27ac94778f2146f48179643473b82931c4cd7b8f153efd94778f2146f48179643473b82931c4cd7b8f153efd872386f26fc10000872386f26fc10000830186a08080", ) .unwrap(); - let deposit_tx_from = "0x778F2146F48179643473B82931c4CD7B8F153eFd".parse::
().unwrap(); let provider = http_provider_with_signer(&handle.http_endpoint(), signer.clone()); - // TODO: necessary right now because transaction validation fails for deposit tx - // with `from` account that doesn't have sufficient ETH balance. - // Should update the tx validation logic for deposit tx to - // 1. check if `tx.value > account.balance + tx.mint` - // 2. don't check `account.balance > gas * price + value` (the gas costs have been prepaid on - // L1) - // source: https://specs.optimism.io/protocol/deposits.html#execution - let fund_account_tx = TransactionRequest::default() - .with_chain_id(31337) - .with_nonce(0) - .with_from(sender_addr) - .with_to(deposit_tx_from) - .with_value(U256::from(1e18)) - .with_gas_limit(21_000) - .with_max_fee_per_gas(20_000_000_000) - .with_max_priority_fee_per_gas(1_000_000_000); - - provider - .send_transaction(WithOtherFields::new(fund_account_tx)) + let receipt = provider + .send_raw_transaction(raw_deposit_tx.as_slice()) .await .unwrap() - .register() + .get_receipt() .await .unwrap(); - // mine block - api.evm_mine(None).await.unwrap(); + assert_eq!(receipt.transaction_hash, tx_hash); +} - let pending = provider - .send_raw_transaction(raw_deposit_tx.as_slice()) - .await - .unwrap() - .register() - .await - .unwrap(); +#[tokio::test(flavor = "multi_thread")] +async fn test_deposit_tx_checks_sufficient_funds_after_applying_deposited_value() { + // enable the Optimism flag + let (_api, handle) = + spawn(NodeConfig::test().with_optimism(true).with_hardfork(Some(Hardfork::Paris))).await; - // mine block - api.evm_mine(None).await.unwrap(); + let provider = http_provider(&handle.http_endpoint()); - let receipt = - provider.get_transaction_receipt(pending.tx_hash().to_owned()).await.unwrap().unwrap(); + let sender = Address::random(); + let recipient = Address::random(); + let send_value = 1_000_000_000_u128; - assert_eq!(pending.tx_hash(), &tx_hash); - assert_eq!(receipt.transaction_hash, tx_hash); + let sender_prev_balance = provider.get_balance(sender).await.unwrap(); + assert_eq!(sender_prev_balance, U256::from(0)); + + let recipient_prev_balance = provider.get_balance(recipient).await.unwrap(); + assert_eq!(recipient_prev_balance, U256::from(0)); + + let deposit_tx = DepositTransaction { + source_hash: b256!("0000000000000000000000000000000000000000000000000000000000000000"), + from: sender, + nonce: 0, + kind: TxKind::Call(recipient), + mint: U256::from(send_value), + value: U256::from(send_value), + gas_limit: 21_000, + is_system_tx: false, + input: Vec::new().into(), + }; + + let mut tx_buffer = Vec::new(); + deposit_tx.encode_2718(&mut tx_buffer); + + provider.send_raw_transaction(&tx_buffer).await.unwrap().get_receipt().await.unwrap(); + + let sender_new_balance = provider.get_balance(sender).await.unwrap(); + // sender should've sent the entire deposited value to recipient + assert_eq!(sender_new_balance, U256::from(0)); + + let recipient_new_balance = provider.get_balance(recipient).await.unwrap(); + // recipient should've received the entire deposited value + assert_eq!(recipient_new_balance, U256::from(send_value)); } From fa0e0c2ca3ae75895dd19173a02faf88509c0608 Mon Sep 17 00:00:00 2001 From: Juan Pablo Villaplana Corrales Date: Wed, 21 Aug 2024 15:03:04 -0600 Subject: [PATCH 093/184] =?UTF-8?q?Issue=20#8664=20|=20Bug=20=F0=9F=90=9B:?= =?UTF-8?q?=20--match-path=20does=20not=20work=20with=20watch=20mode:=20ca?= =?UTF-8?q?nnot=20be=20used=20multiple=20times=20#8664=20(#8709)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Issue #8664 | Bug 🐛: --match-path does not work with watch mode: cannot be used multiple times #8664 * reuse _no_reconfigure var * Remove undescore var name and also remove no needed extra lines * fmt --------- Co-authored-by: Arsenii Kulikov --- crates/forge/bin/cmd/watch.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/forge/bin/cmd/watch.rs b/crates/forge/bin/cmd/watch.rs index 09eb8c666..10562ba12 100644 --- a/crates/forge/bin/cmd/watch.rs +++ b/crates/forge/bin/cmd/watch.rs @@ -260,7 +260,7 @@ pub async fn watch_test(args: TestArgs) -> Result<()> { let config: Config = args.build_args().into(); let filter = args.filter(&config); // Marker to check whether to override the command. - let _no_reconfigure = filter.args().test_pattern.is_some() || + let no_reconfigure = filter.args().test_pattern.is_some() || filter.args().path_pattern.is_some() || filter.args().contract_pattern.is_some() || args.watch.run_all; @@ -303,7 +303,10 @@ pub async fn watch_test(args: TestArgs) -> Result<()> { trace!(?file, "reconfigure test command"); - command.arg("--match-path").arg(&file); + // Before appending `--match-path`, check if it already exists + if !no_reconfigure { + command.arg("--match-path").arg(file); + } }, )?; run(config).await?; From 41cddf7b438068ca5fa9fa9ad9deda678c444a67 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 22 Aug 2024 06:40:32 +0300 Subject: [PATCH 094/184] chore: move more tests to snapbox (#8708) --- crates/forge/tests/cli/build.rs | 31 +++- crates/forge/tests/cli/cmd.rs | 124 ++++++++++------ crates/forge/tests/cli/config.rs | 18 ++- crates/forge/tests/cli/script.rs | 111 +++++++++++---- crates/forge/tests/cli/test_cmd.rs | 134 +++++++++++------- .../can_build_path_with_one_file.stdout | 3 - .../can_build_path_with_three_files.stdout | 3 - .../can_build_path_with_two_files.stdout | 3 - .../fixtures/can_build_skip_contracts.stdout | 3 - .../tests/fixtures/can_build_skip_glob.stdout | 3 - .../tests/fixtures/can_check_snapshot.stdout | 9 -- ...can_detect_dirty_git_status_on_init.stderr | 10 -- ...n_execute_script_and_skip_contracts.stdout | 12 -- .../can_execute_script_command.stdout | 8 -- .../can_execute_script_command_fqn.stdout | 8 -- ...an_execute_script_command_with_args.stdout | 10 -- ...xecute_script_command_with_returned.stdout | 12 -- ...can_execute_script_command_with_sig.stdout | 8 -- .../can_run_test_in_custom_test_folder.stdout | 9 -- .../fixtures/can_set_yul_optimizer.stderr | 8 -- .../can_use_libs_in_multi_fork.stdout | 9 -- .../forge/tests/fixtures/compile_json.stdout | 20 --- .../include_custom_types_in_traces.stdout | 25 ---- .../fixtures/replay_last_run_failures.stdout | 15 -- crates/forge/tests/fixtures/repro_6531.stdout | 17 --- ...xactly_once_with_changed_versions.1.stdout | 9 -- ...xactly_once_with_changed_versions.2.stdout | 9 -- 27 files changed, 283 insertions(+), 348 deletions(-) delete mode 100644 crates/forge/tests/fixtures/can_build_path_with_one_file.stdout delete mode 100644 crates/forge/tests/fixtures/can_build_path_with_three_files.stdout delete mode 100644 crates/forge/tests/fixtures/can_build_path_with_two_files.stdout delete mode 100644 crates/forge/tests/fixtures/can_build_skip_contracts.stdout delete mode 100644 crates/forge/tests/fixtures/can_build_skip_glob.stdout delete mode 100644 crates/forge/tests/fixtures/can_check_snapshot.stdout delete mode 100644 crates/forge/tests/fixtures/can_detect_dirty_git_status_on_init.stderr delete mode 100644 crates/forge/tests/fixtures/can_execute_script_and_skip_contracts.stdout delete mode 100644 crates/forge/tests/fixtures/can_execute_script_command.stdout delete mode 100644 crates/forge/tests/fixtures/can_execute_script_command_fqn.stdout delete mode 100644 crates/forge/tests/fixtures/can_execute_script_command_with_args.stdout delete mode 100644 crates/forge/tests/fixtures/can_execute_script_command_with_returned.stdout delete mode 100644 crates/forge/tests/fixtures/can_execute_script_command_with_sig.stdout delete mode 100644 crates/forge/tests/fixtures/can_run_test_in_custom_test_folder.stdout delete mode 100644 crates/forge/tests/fixtures/can_set_yul_optimizer.stderr delete mode 100644 crates/forge/tests/fixtures/can_use_libs_in_multi_fork.stdout delete mode 100644 crates/forge/tests/fixtures/compile_json.stdout delete mode 100644 crates/forge/tests/fixtures/include_custom_types_in_traces.stdout delete mode 100644 crates/forge/tests/fixtures/replay_last_run_failures.stdout delete mode 100644 crates/forge/tests/fixtures/repro_6531.stdout delete mode 100644 crates/forge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.1.stdout delete mode 100644 crates/forge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.2.stdout diff --git a/crates/forge/tests/cli/build.rs b/crates/forge/tests/cli/build.rs index 6cb17d0d4..779c7aa07 100644 --- a/crates/forge/tests/cli/build.rs +++ b/crates/forge/tests/cli/build.rs @@ -1,5 +1,5 @@ use foundry_config::Config; -use foundry_test_utils::{file, forgetest, str}; +use foundry_test_utils::{forgetest, str}; use globset::Glob; // tests that json is printed when --json is passed @@ -18,9 +18,32 @@ contract Dummy { .unwrap(); // set up command - cmd.args(["compile", "--format-json"]) - .assert() - .stdout_eq(file!["../fixtures/compile_json.stdout": Json]); + cmd.args(["compile", "--format-json"]).assert_success().stdout_eq(str![[r#" +... +{ + "errors": [ + { + "sourceLocation": { + "file": "src/jsonError.sol", + "start": 184, + "end": 193 + }, + "type": "DeclarationError", + "component": "general", + "severity": "error", + "errorCode": "7576", + "message": "Undeclared identifier. Did you mean /"newNumber/"?", + "formattedMessage": "DeclarationError: Undeclared identifier. Did you mean /"newNumber/"?/n --> src/jsonError.sol:7:18:/n |/n7 | number = newnumber; // error here/n | ^^^^^^^^^/n/n" + } + ], + "sources": {}, + "contracts": {}, + "build_infos": [ + { +... +} +... +"#]]); }); // tests build output is as expected diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 23cb3ca8a..cdfd90a73 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -13,8 +13,8 @@ use foundry_test_utils::{ }; use semver::Version; use std::{ - env, fs, - path::{Path, PathBuf}, + fs, + path::Path, process::{Command, Stdio}, str::FromStr, }; @@ -234,11 +234,20 @@ forgetest!(can_detect_dirty_git_status_on_init, |prj, cmd| { fs::create_dir_all(&nested).unwrap(); cmd.current_dir(&nested); - cmd.arg("init"); - cmd.unchecked_output().stderr_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/can_detect_dirty_git_status_on_init.stderr"), - ); + cmd.arg("init").assert_failure().stderr_eq(str![[r#" +... +Error:[..] +The target directory is a part of or on its own an already initialized git repository, +and it requires clean working and staging areas, including no untracked files. + +Check the current git repository's status with `git status`. +Then, you can track files with `git add ...` and then commit them with `git commit`, +ignore them in the `.gitignore` file, or run this command again with the `--no-commit` flag. + +If none of the previous steps worked, please open an issue at: +https://github.com/foundry-rs/foundry/issues/new/choose +... +"#]]); // ensure nothing was emitted, dir is empty assert!(!nested.read_dir().map(|mut i| i.next().is_some()).unwrap_or_default()); @@ -748,11 +757,13 @@ contract ATest is DSTest { ) .unwrap(); - cmd.arg("snapshot"); - - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/can_check_snapshot.stdout"), - ); + cmd.args(["snapshot"]).assert_success().stdout_eq(str![[r#" +... +Ran 1 test for src/ATest.t.sol:ATest +[PASS] testExample() (gas: 168) +Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in [..] +... +"#]]); cmd.arg("--check"); let _ = cmd.output(); @@ -1646,11 +1657,15 @@ forgetest_init!(can_build_skip_contracts, |prj, cmd| { prj.clear(); // only builds the single template contract `src/*` - cmd.args(["build", "--skip", "tests", "--skip", "scripts"]); - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/can_build_skip_contracts.stdout"), - ); + cmd.args(["build", "--skip", "tests", "--skip", "scripts"]).assert_success().stdout_eq(str![[ + r#" +... +Compiling 1 files [..] +[..] +Compiler run successful! +... +"# + ]]); // re-run command let out = cmd.stdout_lossy(); @@ -1671,15 +1686,26 @@ function test_run() external {} // only builds the single template contract `src/*` even if `*.t.sol` or `.s.sol` is absent prj.clear(); - cmd.args(["build", "--skip", "*/test/**", "--skip", "*/script/**", "--force"]); - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/can_build_skip_glob.stdout"), - ); + cmd.args(["build", "--skip", "*/test/**", "--skip", "*/script/**", "--force"]) + .assert_success() + .stdout_eq(str![[r#" +... +Compiling 1 files [..] +[..] +Compiler run successful! +... +"#]]); - cmd.forge_fuse().args(["build", "--skip", "./test/**", "--skip", "./script/**", "--force"]); - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/can_build_skip_glob.stdout"), - ); + cmd.forge_fuse() + .args(["build", "--skip", "./test/**", "--skip", "./script/**", "--force"]) + .assert_success() + .stdout_eq(str![[r#" +... +Compiling 1 files [..] +[..] +Compiler run successful! +... +"#]]); }); forgetest_init!(can_build_specific_paths, |prj, cmd| { @@ -1711,38 +1737,46 @@ function test_bar() external {} // Build 2 files within test dir prj.clear(); - cmd.args(["build", "test", "--force"]); - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/can_build_path_with_two_files.stdout"), - ); + cmd.args(["build", "test", "--force"]).assert_success().stdout_eq(str![[r#" +... +Compiling 2 files with Solc 0.8.23 +[..] +Compiler run successful! +... +"#]]); // Build one file within src dir prj.clear(); cmd.forge_fuse(); - cmd.args(["build", "src", "--force"]); - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/can_build_path_with_one_file.stdout"), - ); + cmd.args(["build", "src", "--force"]).assert_success().stdout_eq(str![[r#" +... +Compiling 1 files with Solc 0.8.23 +[..] +Compiler run successful! +... +"#]]); // Build 3 files from test and src dirs prj.clear(); cmd.forge_fuse(); - cmd.args(["build", "src", "test", "--force"]); - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/can_build_path_with_three_files.stdout"), - ); + cmd.args(["build", "src", "test", "--force"]).assert_success().stdout_eq(str![[r#" +... +Compiling 3 files with Solc 0.8.23 +[..] +Compiler run successful! +... +"#]]); // Build single test file prj.clear(); cmd.forge_fuse(); - cmd.args(["build", "test/Bar.sol", "--force"]); - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/can_build_path_with_one_file.stdout"), - ); + cmd.args(["build", "test/Bar.sol", "--force"]).assert_success().stdout_eq(str![[r#" +... +Compiling 1 files with Solc 0.8.23 +[..] +Compiler run successful! +... +"#]]); }); // checks that build --sizes includes all contracts even if unchanged diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 0e38e15e7..78c76a22a 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -14,7 +14,7 @@ use foundry_config::{ use foundry_evm::opts::EvmOpts; use foundry_test_utils::{ foundry_compilers::artifacts::{remappings::Remapping, EvmVersion}, - util::{pretty_err, OutputExt, TestCommand, OTHER_SOLC_VERSION}, + util::{pretty_err, TestCommand, OTHER_SOLC_VERSION}, }; use path_slash::PathBufExt; use similar_asserts::assert_eq; @@ -399,11 +399,17 @@ contract Foo { ) .unwrap(); - cmd.arg("build"); - cmd.unchecked_output().stderr_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/can_set_yul_optimizer.stderr"), - ); + cmd.arg("build").assert_failure().stderr_eq(str![[r#" +... +Error:[..] +Compiler run failed: +Error (6553): The msize instruction cannot be used when the Yul optimizer is activated because it can change its semantics. Either disable the Yul optimizer or do not use the instruction. + --> src/foo.sol:6:8: + | +6 | assembly { + | ^ (Relevant source part starts here and spans across multiple lines). +... +"#]]); // disable yul optimizer explicitly let config = Config { diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index de3a3b544..68ccec895 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -3,7 +3,7 @@ use crate::constants::TEMPLATE_CONTRACT; use alloy_primitives::{hex, Address, Bytes}; use anvil::{spawn, NodeConfig}; -use foundry_test_utils::{rpc, util::OutputExt, ScriptOutcome, ScriptTester}; +use foundry_test_utils::{rpc, ScriptOutcome, ScriptTester}; use regex::Regex; use serde_json::Value; use std::{env, path::PathBuf, str::FromStr}; @@ -53,11 +53,15 @@ contract Demo { ) .unwrap(); - cmd.arg("script").arg(script); - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/can_execute_script_command.stdout"), - ); + cmd.arg("script").arg(script).assert_success().stdout_eq(str![[r#" +... +Script ran successfully. +Gas used: 22815 + +== Logs == + script ran +... +"#]]); }); // Tests that the `run` command works correctly when path *and* script name is specified @@ -76,11 +80,17 @@ contract Demo { ) .unwrap(); - cmd.arg("script").arg(format!("{}:Demo", script.display())); - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/can_execute_script_command_fqn.stdout"), - ); + cmd.arg("script").arg(format!("{}:Demo", script.display())).assert_success().stdout_eq(str![[ + r#" +... +Script ran successfully. +Gas used: 22815 + +== Logs == + script ran +... +"# + ]]); }); // Tests that the run command can run arbitrary functions @@ -99,10 +109,16 @@ contract Demo { ) .unwrap(); - cmd.arg("script").arg(script).arg("--sig").arg("myFunction()"); - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/can_execute_script_command_with_sig.stdout"), + cmd.arg("script").arg(script).arg("--sig").arg("myFunction()").assert_success().stdout_eq( + str![[r#" +... +Script ran successfully. +Gas used: 22815 + +== Logs == + script ran +... +"#]], ); }); @@ -272,11 +288,24 @@ contract Demo { ) .unwrap(); - cmd.arg("script").arg(script).arg("--sig").arg("run(uint256,uint256)").arg("1").arg("2"); - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/can_execute_script_command_with_args.stdout"), - ); + cmd.arg("script") + .arg(script) + .arg("--sig") + .arg("run(uint256,uint256)") + .arg("1") + .arg("2") + .assert_success() + .stdout_eq(str![[r#" +... +Script ran successfully. +Gas used: 25301 + +== Logs == + script ran + 1 + 2 +... +"#]]); }); // Tests that the run command can run functions with return values @@ -294,11 +323,20 @@ contract Demo { }"#, ) .unwrap(); - cmd.arg("script").arg(script); - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/can_execute_script_command_with_returned.stdout"), - ); + + cmd.arg("script").arg(script).assert_success().stdout_eq(str![[r#" +... +Script ran successfully. +Gas used: 22900 + +== Return == +result: uint256 255 +1: uint8 3 + +== Logs == + script ran +... +"#]]); }); forgetest_async!(can_broadcast_script_skipping_simulation, |prj, cmd| { @@ -930,12 +968,23 @@ contract Demo { }"#, ) .unwrap(); - cmd.arg("script").arg(script).args(["--skip", "tests", "--skip", TEMPLATE_CONTRACT]); - - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/can_execute_script_and_skip_contracts.stdout"), - ); + cmd.arg("script") + .arg(script) + .args(["--skip", "tests", "--skip", TEMPLATE_CONTRACT]) + .assert_success() + .stdout_eq(str![[r#" +... +Script ran successfully. +Gas used: 22900 + +== Return == +result: uint256 255 +1: uint8 3 + +== Logs == + script ran +... +"#]]); }); forgetest_async!(can_run_script_with_empty_setup, |prj, cmd| { diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 97e4c97cf..7c0499916 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -4,7 +4,7 @@ use alloy_primitives::U256; use foundry_config::{Config, FuzzConfig}; use foundry_test_utils::{ rpc, str, - util::{OutputExt, OTHER_SOLC_VERSION, SOLC_VERSION}, + util::{OTHER_SOLC_VERSION, SOLC_VERSION}, }; use similar_asserts::assert_eq; use std::{path::PathBuf, str::FromStr}; @@ -272,11 +272,12 @@ contract MyTest is DSTest { ) .unwrap(); - cmd.arg("test"); - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/can_run_test_in_custom_test_folder.stdout"), - ); + cmd.arg("test").assert_success().stdout_eq(str![[r#" +... +Ran 1 test for src/nested/forge-tests/MyTest.t.sol:MyTest +[PASS] testTrue() (gas: 168) +... +"#]]); }); // checks that forge test repeatedly produces the same output @@ -325,20 +326,37 @@ contract ContractTest is DSTest { let config = Config { solc: Some(SOLC_VERSION.into()), ..Default::default() }; prj.write_config(config); - cmd.arg("test"); - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/runs_tests_exactly_once_with_changed_versions.1.stdout"), - ); + cmd.arg("test").assert_success().stdout_eq(str![[r#" +... +Compiling 2 files with Solc 0.8.23 +Solc 0.8.23 finished in [..] +Compiler run successful! + +Ran 1 test for src/Contract.t.sol:ContractTest +[PASS] testExample() (gas: 190) +Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in [..] + +Ran 1 test suite in [..] 1 tests passed, 0 failed, 0 skipped (1 total tests) +... +"#]]); // pin version let config = Config { solc: Some(OTHER_SOLC_VERSION.into()), ..Default::default() }; prj.write_config(config); - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/runs_tests_exactly_once_with_changed_versions.2.stdout"), - ); + cmd.forge_fuse().arg("test").assert_success().stdout_eq(str![[r#" +... +Compiling 2 files with Solc 0.8.22 +Solc 0.8.22 finished in [..] +Compiler run successful! + +Ran 1 test for src/Contract.t.sol:ContractTest +[PASS] testExample() (gas: 190) +Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in [..] + +Ran 1 test suite in [..] 1 tests passed, 0 failed, 0 skipped (1 total tests) +... +"#]]); }); // tests that libraries are handled correctly in multiforking mode @@ -388,11 +406,12 @@ contract ContractTest is Test { ) .unwrap(); - cmd.arg("test"); - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/can_use_libs_in_multi_fork.stdout"), - ); + cmd.arg("test").assert_success().stdout_eq(str![[r#" +... +Ran 1 test for test/Contract.t.sol:ContractTest +[PASS] test() (gas: 70360) +... +"#]]); }); static FAILING_TEST: &str = r#" @@ -453,13 +472,21 @@ contract USDTCallingTest is Test { ) .unwrap(); - let expected = std::fs::read_to_string( - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/repro_6531.stdout"), - ) - .unwrap() - .replace("", &endpoint); + cmd.args(["test", "-vvvv"]).assert_success().stdout_eq(str![[r#" +... +Compiler run successful! - cmd.args(["test", "-vvvv"]).unchecked_output().stdout_matches_content(&expected); +Ran 1 test for test/Contract.t.sol:USDTCallingTest +[PASS] test() (gas: 9537) +Traces: + [9537] USDTCallingTest::test() + ├─ [0] VM::createSelectFork("[..]") + │ └─ ← [Return] 0 + ├─ [3110] 0xdAC17F958D2ee523a2206206994597C13D831ec7::name() [staticcall] + │ └─ ← [Return] "Tether USD" + └─ ← [Stop][..] +... +"#]]); }); // @@ -486,10 +513,21 @@ contract CustomTypesTest is Test { ) .unwrap(); - cmd.args(["test", "-vvvv"]).unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/include_custom_types_in_traces.stdout"), - ); + cmd.args(["test", "-vvvv"]).assert_failure().stdout_eq(str![[r#" +... +Ran 2 tests for test/Contract.t.sol:CustomTypesTest +[FAIL. Reason: PoolNotInitialized()] testErr() (gas: 254) +Traces: + [254] CustomTypesTest::testErr() + └─ ← [Revert] PoolNotInitialized() + +[PASS] testEvent() (gas: 1268) +Traces: + [1268] CustomTypesTest::testEvent() + ├─ emit MyEvent(a: 100) + └─ ← [Stop][..] +... +"#]]); }); forgetest_init!(can_test_selfdestruct_with_isolation, |prj, cmd| { @@ -664,10 +702,12 @@ contract CounterTest is Test { ) .unwrap(); - cmd.args(["test"]); - let (stderr, _) = cmd.unchecked_output_lossy(); // make sure there are only 61 runs (with proptest shrinking same test results in 298 runs) - assert_eq!(extract_number_of_runs(stderr), 61); + cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" +... +[..]testAddOne(uint256) (runs: 61, μ: [..], ~: [..]) +... +"#]]); }); forgetest_init!(should_exit_early_on_invariant_failure, |prj, cmd| { @@ -700,20 +740,14 @@ contract CounterTest is Test { ) .unwrap(); - cmd.args(["test"]); - let (stderr, _) = cmd.unchecked_output_lossy(); // make sure invariant test exit early with 0 runs - assert_eq!(extract_number_of_runs(stderr), 0); + cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" +... +[..]invariant_early_exit() (runs: 0, calls: 0, reverts: 0) +... +"#]]); }); -fn extract_number_of_runs(stderr: String) -> usize { - let runs = stderr.find("runs:").and_then(|start_runs| { - let runs_split = &stderr[start_runs + 6..]; - runs_split.find(',').map(|end_runs| &runs_split[..end_runs]) - }); - runs.unwrap().parse::().unwrap() -} - forgetest_init!(should_replay_failures_only, |prj, cmd| { prj.wipe_contracts(); prj.add_test( @@ -748,11 +782,13 @@ contract ReplayFailuresTest is Test { assert!(prj.root().join("cache/test-failures").exists()); // Perform only the 2 failing tests from last run. - cmd.forge_fuse(); - cmd.args(["test", "--rerun"]).unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/replay_last_run_failures.stdout"), - ); + cmd.forge_fuse().args(["test", "--rerun"]).assert_failure().stdout_eq(str![[r#" +... +Ran 2 tests for test/ReplayFailures.t.sol:ReplayFailuresTest +[FAIL. Reason: revert: testB failed] testB() (gas: 303) +[FAIL. Reason: revert: testD failed] testD() (gas: 314) +... +"#]]); }); // diff --git a/crates/forge/tests/fixtures/can_build_path_with_one_file.stdout b/crates/forge/tests/fixtures/can_build_path_with_one_file.stdout deleted file mode 100644 index 3213db81a..000000000 --- a/crates/forge/tests/fixtures/can_build_path_with_one_file.stdout +++ /dev/null @@ -1,3 +0,0 @@ -Compiling 1 files with 0.8.23 -Solc 0.8.23 finished in 33.25ms -Compiler run successful! diff --git a/crates/forge/tests/fixtures/can_build_path_with_three_files.stdout b/crates/forge/tests/fixtures/can_build_path_with_three_files.stdout deleted file mode 100644 index 4235acf29..000000000 --- a/crates/forge/tests/fixtures/can_build_path_with_three_files.stdout +++ /dev/null @@ -1,3 +0,0 @@ -Compiling 3 files with 0.8.23 -Solc 0.8.23 finished in 33.25ms -Compiler run successful! diff --git a/crates/forge/tests/fixtures/can_build_path_with_two_files.stdout b/crates/forge/tests/fixtures/can_build_path_with_two_files.stdout deleted file mode 100644 index cf5900340..000000000 --- a/crates/forge/tests/fixtures/can_build_path_with_two_files.stdout +++ /dev/null @@ -1,3 +0,0 @@ -Compiling 2 files with 0.8.23 -Solc 0.8.23 finished in 33.25ms -Compiler run successful! diff --git a/crates/forge/tests/fixtures/can_build_skip_contracts.stdout b/crates/forge/tests/fixtures/can_build_skip_contracts.stdout deleted file mode 100644 index 43949fcce..000000000 --- a/crates/forge/tests/fixtures/can_build_skip_contracts.stdout +++ /dev/null @@ -1,3 +0,0 @@ -Compiling 1 files with 0.8.23 -Solc 0.8.23 finished in 34.45ms -Compiler run successful! diff --git a/crates/forge/tests/fixtures/can_build_skip_glob.stdout b/crates/forge/tests/fixtures/can_build_skip_glob.stdout deleted file mode 100644 index 3213db81a..000000000 --- a/crates/forge/tests/fixtures/can_build_skip_glob.stdout +++ /dev/null @@ -1,3 +0,0 @@ -Compiling 1 files with 0.8.23 -Solc 0.8.23 finished in 33.25ms -Compiler run successful! diff --git a/crates/forge/tests/fixtures/can_check_snapshot.stdout b/crates/forge/tests/fixtures/can_check_snapshot.stdout deleted file mode 100644 index bce1c6972..000000000 --- a/crates/forge/tests/fixtures/can_check_snapshot.stdout +++ /dev/null @@ -1,9 +0,0 @@ -Compiling 2 files with 0.8.23 -Solc 0.8.23 finished in 424.55ms -Compiler run successful! - -Ran 1 test for src/ATest.t.sol:ATest -[PASS] testExample() (gas: 168) -Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.42ms - -Ran 1 test suite: 1 tests passed, 0 failed, 0 skipped (1 total tests) diff --git a/crates/forge/tests/fixtures/can_detect_dirty_git_status_on_init.stderr b/crates/forge/tests/fixtures/can_detect_dirty_git_status_on_init.stderr deleted file mode 100644 index de86b264d..000000000 --- a/crates/forge/tests/fixtures/can_detect_dirty_git_status_on_init.stderr +++ /dev/null @@ -1,10 +0,0 @@ -Error: -The target directory is a part of or on its own an already initialized git repository, -and it requires clean working and staging areas, including no untracked files. - -Check the current git repository's status with `git status`. -Then, you can track files with `git add ...` and then commit them with `git commit`, -ignore them in the `.gitignore` file, or run this command again with the `--no-commit` flag. - -If none of the previous steps worked, please open an issue at: -https://github.com/foundry-rs/foundry/issues/new/choose diff --git a/crates/forge/tests/fixtures/can_execute_script_and_skip_contracts.stdout b/crates/forge/tests/fixtures/can_execute_script_and_skip_contracts.stdout deleted file mode 100644 index fdc8e739f..000000000 --- a/crates/forge/tests/fixtures/can_execute_script_and_skip_contracts.stdout +++ /dev/null @@ -1,12 +0,0 @@ -Compiling 1 files with 0.8.23 -Solc 0.8.23 -Compiler run successful! -Script ran successfully. -Gas used: 22900 - -== Return == -result: uint256 255 -1: uint8 3 - -== Logs == - script ran diff --git a/crates/forge/tests/fixtures/can_execute_script_command.stdout b/crates/forge/tests/fixtures/can_execute_script_command.stdout deleted file mode 100644 index a9717c19d..000000000 --- a/crates/forge/tests/fixtures/can_execute_script_command.stdout +++ /dev/null @@ -1,8 +0,0 @@ -Compiling 1 files with 0.8.23 -Solc 0.8.23 finished in 23.34ms -Compiler run successful! -Script ran successfully. -Gas used: 22815 - -== Logs == - script ran diff --git a/crates/forge/tests/fixtures/can_execute_script_command_fqn.stdout b/crates/forge/tests/fixtures/can_execute_script_command_fqn.stdout deleted file mode 100644 index 1156e916e..000000000 --- a/crates/forge/tests/fixtures/can_execute_script_command_fqn.stdout +++ /dev/null @@ -1,8 +0,0 @@ -Compiling 1 files with 0.8.23 -Solc 0.8.23 finished in 23.70ms -Compiler run successful! -Script ran successfully. -Gas used: 22815 - -== Logs == - script ran diff --git a/crates/forge/tests/fixtures/can_execute_script_command_with_args.stdout b/crates/forge/tests/fixtures/can_execute_script_command_with_args.stdout deleted file mode 100644 index feb193073..000000000 --- a/crates/forge/tests/fixtures/can_execute_script_command_with_args.stdout +++ /dev/null @@ -1,10 +0,0 @@ -Compiling 1 files with 0.8.23 -Solc 0.8.23 finished in 35.28ms -Compiler run successful! -Script ran successfully. -Gas used: 25301 - -== Logs == - script ran - 1 - 2 diff --git a/crates/forge/tests/fixtures/can_execute_script_command_with_returned.stdout b/crates/forge/tests/fixtures/can_execute_script_command_with_returned.stdout deleted file mode 100644 index 62aa01b13..000000000 --- a/crates/forge/tests/fixtures/can_execute_script_command_with_returned.stdout +++ /dev/null @@ -1,12 +0,0 @@ -Compiling 1 files with 0.8.23 -Solc 0.8.23 finished in 1.27s -Compiler run successful! -Script ran successfully. -Gas used: 22900 - -== Return == -result: uint256 255 -1: uint8 3 - -== Logs == - script ran diff --git a/crates/forge/tests/fixtures/can_execute_script_command_with_sig.stdout b/crates/forge/tests/fixtures/can_execute_script_command_with_sig.stdout deleted file mode 100644 index 13a94c698..000000000 --- a/crates/forge/tests/fixtures/can_execute_script_command_with_sig.stdout +++ /dev/null @@ -1,8 +0,0 @@ -Compiling 1 files with 0.8.23 -Solc 0.8.23 finished in 24.49ms -Compiler run successful! -Script ran successfully. -Gas used: 22815 - -== Logs == - script ran diff --git a/crates/forge/tests/fixtures/can_run_test_in_custom_test_folder.stdout b/crates/forge/tests/fixtures/can_run_test_in_custom_test_folder.stdout deleted file mode 100644 index cd92d6ebe..000000000 --- a/crates/forge/tests/fixtures/can_run_test_in_custom_test_folder.stdout +++ /dev/null @@ -1,9 +0,0 @@ -Compiling 2 files with 0.8.23 -Solc 0.8.23 finished in 185.25ms -Compiler run successful! - -Ran 1 test for src/nested/forge-tests/MyTest.t.sol:MyTest -[PASS] testTrue() (gas: 168) -Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.93ms - -Ran 1 test suite: 1 tests passed, 0 failed, 0 skipped (1 total tests) diff --git a/crates/forge/tests/fixtures/can_set_yul_optimizer.stderr b/crates/forge/tests/fixtures/can_set_yul_optimizer.stderr deleted file mode 100644 index 0dd4db95b..000000000 --- a/crates/forge/tests/fixtures/can_set_yul_optimizer.stderr +++ /dev/null @@ -1,8 +0,0 @@ -Error: -Compiler run failed: -Error (6553): The msize instruction cannot be used when the Yul optimizer is activated because it can change its semantics. Either disable the Yul optimizer or do not use the instruction. - --> src/Foo.sol:6:8: - | -6 | assembly { - | ^ (Relevant source part starts here and spans across multiple lines). - diff --git a/crates/forge/tests/fixtures/can_use_libs_in_multi_fork.stdout b/crates/forge/tests/fixtures/can_use_libs_in_multi_fork.stdout deleted file mode 100644 index 70c72887a..000000000 --- a/crates/forge/tests/fixtures/can_use_libs_in_multi_fork.stdout +++ /dev/null @@ -1,9 +0,0 @@ -Compiling 2 files with 0.8.23 -Solc 0.8.23 finished in 1.95s -Compiler run successful! - -Ran 1 test for test/Contract.t.sol:ContractTest -[PASS] test() (gas: 70360) -Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.21s - -Ran 1 test suite: 1 tests passed, 0 failed, 0 skipped (1 total tests) diff --git a/crates/forge/tests/fixtures/compile_json.stdout b/crates/forge/tests/fixtures/compile_json.stdout deleted file mode 100644 index 2a794cc4a..000000000 --- a/crates/forge/tests/fixtures/compile_json.stdout +++ /dev/null @@ -1,20 +0,0 @@ -{ - "contracts": {}, - "errors": [ - { - "sourceLocation": { - "file": "src/jsonError.sol", - "start": 184, - "end": 193 - }, - "type": "DeclarationError", - "component": "general", - "severity": "error", - "errorCode": "7576", - "message": "Undeclared identifier. Did you mean \"newNumber\"?", - "formattedMessage": "DeclarationError: Undeclared identifier. Did you mean \"newNumber\"?\n --> src/jsonError.sol:7:18:\n |\n7 | number = newnumber; // error here\n | ^^^^^^^^^\n\n" - } - ], - "sources": {}, - "build_infos": ["{...}"] -} \ No newline at end of file diff --git a/crates/forge/tests/fixtures/include_custom_types_in_traces.stdout b/crates/forge/tests/fixtures/include_custom_types_in_traces.stdout deleted file mode 100644 index 9b289543f..000000000 --- a/crates/forge/tests/fixtures/include_custom_types_in_traces.stdout +++ /dev/null @@ -1,25 +0,0 @@ -Compiling 1 files with 0.8.23 -Solc 0.8.23 finished in 798.51ms -Compiler run successful! - -Ran 2 tests for test/Contract.t.sol:CustomTypesTest -[FAIL. Reason: PoolNotInitialized()] testErr() (gas: 254) -Traces: - [254] CustomTypesTest::testErr() - └─ ← [Revert] PoolNotInitialized() - -[PASS] testEvent() (gas: 1268) -Traces: - [1268] CustomTypesTest::testEvent() - ├─ emit MyEvent(a: 100) - └─ ← [Stop] - -Suite result: FAILED. 1 passed; 1 failed; 0 skipped; finished in 3.88ms - -Ran 1 test suite: 1 tests passed, 1 failed, 0 skipped (2 total tests) - -Failing tests: -Encountered 1 failing test in test/Contract.t.sol:CustomTypesTest -[FAIL. Reason: PoolNotInitialized()] testErr() (gas: 254) - -Encountered a total of 1 failing tests, 1 tests succeeded diff --git a/crates/forge/tests/fixtures/replay_last_run_failures.stdout b/crates/forge/tests/fixtures/replay_last_run_failures.stdout deleted file mode 100644 index d94983059..000000000 --- a/crates/forge/tests/fixtures/replay_last_run_failures.stdout +++ /dev/null @@ -1,15 +0,0 @@ -No files changed, compilation skipped - -Ran 2 tests for test/ReplayFailures.t.sol:ReplayFailuresTest -[FAIL. Reason: revert: testB failed] testB() (gas: 303) -[FAIL. Reason: revert: testD failed] testD() (gas: 314) -Suite result: FAILED. 0 passed; 2 failed; 0 skipped; finished in 555.95µs (250.31µs CPU time) - -Ran 1 test suite in 3.24ms (555.95µs CPU time): 0 tests passed, 2 failed, 0 skipped (2 total tests) - -Failing tests: -Encountered 2 failing tests in test/ReplayFailures.t.sol:ReplayFailuresTest -[FAIL. Reason: revert: testB failed] testB() (gas: 303) -[FAIL. Reason: revert: testD failed] testD() (gas: 314) - -Encountered a total of 2 failing tests, 0 tests succeeded diff --git a/crates/forge/tests/fixtures/repro_6531.stdout b/crates/forge/tests/fixtures/repro_6531.stdout deleted file mode 100644 index 47a6bb237..000000000 --- a/crates/forge/tests/fixtures/repro_6531.stdout +++ /dev/null @@ -1,17 +0,0 @@ -Compiling 1 files with 0.8.23 - -Compiler run successful! - -Ran 1 test for test/Contract.t.sol:USDTCallingTest -[PASS] test() (gas: 9537) -Traces: - [9537] USDTCallingTest::test() - ├─ [0] VM::createSelectFork("") - │ └─ ← [Return] 0 - ├─ [3110] 0xdAC17F958D2ee523a2206206994597C13D831ec7::name() [staticcall] - │ └─ ← [Return] "Tether USD" - └─ ← [Stop] - -Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.43s - -Ran 1 test suite: 1 tests passed, 0 failed, 0 skipped (1 total tests) diff --git a/crates/forge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.1.stdout b/crates/forge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.1.stdout deleted file mode 100644 index c98d9f93e..000000000 --- a/crates/forge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.1.stdout +++ /dev/null @@ -1,9 +0,0 @@ -Compiling 2 files with 0.8.23 -Solc 0.8.23 finished in 185.25ms -Compiler run successful! - -Ran 1 test for src/Contract.t.sol:ContractTest -[PASS] testExample() (gas: 190) -Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.89ms - -Ran 1 test suite: 1 tests passed, 0 failed, 0 skipped (1 total tests) diff --git a/crates/forge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.2.stdout b/crates/forge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.2.stdout deleted file mode 100644 index abfd712db..000000000 --- a/crates/forge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.2.stdout +++ /dev/null @@ -1,9 +0,0 @@ -Compiling 2 files with 0.8.22 -Solc 0.8.22 finished in 185.25ms -Compiler run successful! - -Ran 1 test for src/Contract.t.sol:ContractTest -[PASS] testExample() (gas: 190) -Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.89ms - -Ran 1 test suite: 1 tests passed, 0 failed, 0 skipped (1 total tests) From 154057a1d61efd59065f6112f9d19d83194b6eec Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Wed, 21 Aug 2024 23:35:31 -0700 Subject: [PATCH 095/184] fix(cast): json quoted strings (#8681) * fix quoted strings * fix test integration * address comments --------- Co-authored-by: Matthias Seitz --- crates/common/fmt/src/dynamic.rs | 8 +++++++- crates/forge/tests/cli/script.rs | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/common/fmt/src/dynamic.rs b/crates/common/fmt/src/dynamic.rs index e13d6ab96..498d209f7 100644 --- a/crates/common/fmt/src/dynamic.rs +++ b/crates/common/fmt/src/dynamic.rs @@ -38,7 +38,13 @@ impl DynValueFormatter { f.write_str("]") } DynSolValue::Tuple(values) => self.tuple(values, f), - DynSolValue::String(inner) => write!(f, "{inner:?}"), // escape strings + DynSolValue::String(inner) => { + if self.raw { + write!(f, "{}", inner.escape_debug()) + } else { + write!(f, "{inner:?}") // escape strings + } + } DynSolValue::Bool(inner) => write!(f, "{inner}"), DynSolValue::CustomStruct { name, prop_names, tuple } => { if self.raw { diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 68ccec895..f64bce205 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -865,7 +865,7 @@ contract Script0 is Script { "true".to_string(), "0x616263646566".to_string(), "(10, 99)".to_string(), - "\"hello\"".to_string(), + "hello".to_string(), ] ); }); @@ -948,7 +948,7 @@ contract Script0 is Script { "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6".to_string(), "true".to_string(), "0x616263646566".to_string(), - "\"hello\"".to_string(), + "hello".to_string(), ] ); }); From 3b9c29dcc54f50ee0ad1f1a33a0456168562f739 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 22 Aug 2024 17:44:13 +0800 Subject: [PATCH 096/184] fix(script): correctly populate both fields for `TransactionRequest`s (#8714) fix(script): correctly populate both fields for TransactionRequests --- crates/script/src/execute.rs | 12 +++++++++++- crates/script/src/runner.rs | 6 +++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index caa3fb2c2..410d89610 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -8,6 +8,7 @@ use alloy_dyn_abi::FunctionExt; use alloy_json_abi::{Function, InternalType, JsonAbi}; use alloy_primitives::{Address, Bytes}; use alloy_provider::Provider; +use alloy_rpc_types::TransactionInput; use async_recursion::async_recursion; use eyre::{OptionExt, Result}; use foundry_cheatcodes::ScriptWallets; @@ -284,7 +285,16 @@ impl ExecutedState { let decoder = self.build_trace_decoder(&self.build_data.known_contracts).await?; - let txs = self.execution_result.transactions.clone().unwrap_or_default(); + let mut txs = self.execution_result.transactions.clone().unwrap_or_default(); + + // Ensure that unsigned transactions have both `data` and `input` populated to avoid + // issues with eth_estimateGas and eth_sendTransaction requests. + for tx in &mut txs { + if let Some(req) = tx.transaction.as_unsigned_mut() { + req.input = + TransactionInput::maybe_both(std::mem::take(&mut req.input).into_input()); + } + } let rpc_data = RpcData::from_transactions(&txs); if rpc_data.is_multi_chain() { diff --git a/crates/script/src/runner.rs b/crates/script/src/runner.rs index 584180256..2b3fc5253 100644 --- a/crates/script/src/runner.rs +++ b/crates/script/src/runner.rs @@ -1,7 +1,7 @@ use super::ScriptResult; use crate::build::ScriptPredeployLibraries; use alloy_primitives::{Address, Bytes, TxKind, U256}; -use alloy_rpc_types::{TransactionInput, TransactionRequest}; +use alloy_rpc_types::TransactionRequest; use eyre::Result; use foundry_cheatcodes::BroadcastableTransaction; use foundry_config::Config; @@ -75,7 +75,7 @@ impl ScriptRunner { rpc: self.evm_opts.fork_url.clone(), transaction: TransactionRequest { from: Some(self.evm_opts.sender), - input: TransactionInput::both(code.clone()), + input: code.clone().into(), nonce: Some(sender_nonce + library_transactions.len() as u64), ..Default::default() } @@ -109,7 +109,7 @@ impl ScriptRunner { rpc: self.evm_opts.fork_url.clone(), transaction: TransactionRequest { from: Some(self.evm_opts.sender), - input: TransactionInput::both(calldata.clone().into()), + input: calldata.into(), nonce: Some(sender_nonce + library_transactions.len() as u64), to: Some(TxKind::Call(DEFAULT_CREATE2_DEPLOYER)), ..Default::default() From 33aae9e9a8d27cc0d88c423a3f8888f9b14dffa0 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 22 Aug 2024 18:43:09 +0800 Subject: [PATCH 097/184] feat: add transaction timeout config (#8669) * feat: add transaction timeout config * clipppy * fix doc --- crates/cast/bin/cmd/send.rs | 16 ++++++++-- crates/cast/bin/main.rs | 2 +- crates/cast/src/lib.rs | 5 +++- crates/config/src/lib.rs | 4 +++ crates/forge/bin/cmd/create.rs | 51 +++++++++++++++++++++++++++----- crates/forge/tests/cli/config.rs | 1 + crates/script/src/broadcast.rs | 18 +++++++++-- crates/script/src/lib.rs | 7 +++++ crates/script/src/progress.rs | 8 +++-- crates/script/src/receipts.rs | 3 +- 10 files changed, 98 insertions(+), 17 deletions(-) diff --git a/crates/cast/bin/cmd/send.rs b/crates/cast/bin/cmd/send.rs index 82c7f15d9..573bb2bd1 100644 --- a/crates/cast/bin/cmd/send.rs +++ b/crates/cast/bin/cmd/send.rs @@ -50,6 +50,10 @@ pub struct SendTxArgs { #[arg(long, requires = "from")] unlocked: bool, + /// Timeout for sending the transaction. + #[arg(long, env = "ETH_TIMEOUT")] + pub timeout: Option, + #[command(flatten)] tx: TransactionOpts, @@ -98,6 +102,7 @@ impl SendTxArgs { command, unlocked, path, + timeout, } = self; let blob_data = if let Some(path) = path { Some(std::fs::read(path)?) } else { None }; @@ -126,6 +131,8 @@ impl SendTxArgs { .await? .with_blob_data(blob_data)?; + let timeout = timeout.unwrap_or(config.transaction_timeout); + // 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 @@ -152,7 +159,7 @@ impl SendTxArgs { let (tx, _) = builder.build(config.sender).await?; - cast_send(provider, tx, cast_async, confirmations, to_json).await + cast_send(provider, tx, cast_async, confirmations, timeout, to_json).await // Case 2: // An option to use a local signer was provided. // If we cannot successfully instantiate a local signer, then we will assume we don't have @@ -171,7 +178,7 @@ impl SendTxArgs { .wallet(wallet) .on_provider(&provider); - cast_send(provider, tx, cast_async, confirmations, to_json).await + cast_send(provider, tx, cast_async, confirmations, timeout, to_json).await } } } @@ -181,6 +188,7 @@ async fn cast_send, T: Transport + Clone>( tx: WithOtherFields, cast_async: bool, confs: u64, + timeout: u64, to_json: bool, ) -> Result<()> { let cast = Cast::new(provider); @@ -191,7 +199,9 @@ async fn cast_send, T: Transport + Clone>( if cast_async { println!("{tx_hash:#x}"); } else { - let receipt = cast.receipt(format!("{tx_hash:#x}"), None, confs, false, to_json).await?; + let receipt = cast + .receipt(format!("{tx_hash:#x}"), None, confs, Some(timeout), false, to_json) + .await?; println!("{receipt}"); } diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index c3bd5039f..501955827 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -396,7 +396,7 @@ async fn main() -> Result<()> { println!( "{}", Cast::new(provider) - .receipt(tx_hash, field, confirmations, cast_async, json) + .receipt(tx_hash, field, confirmations, None, cast_async, json) .await? ); } diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 7c2d970a7..95952bfbb 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -42,6 +42,7 @@ use std::{ path::PathBuf, str::FromStr, sync::atomic::{AtomicBool, Ordering}, + time::Duration, }; use tokio::signal::ctrl_c; @@ -690,7 +691,7 @@ where /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("http://localhost:8545").await?; /// let cast = Cast::new(provider); /// let tx_hash = "0xf8d1713ea15a81482958fb7ddf884baee8d3bcc478c5f2f604e008dc788ee4fc"; - /// let receipt = cast.receipt(tx_hash.to_string(), None, 1, false, false).await?; + /// let receipt = cast.receipt(tx_hash.to_string(), None, 1, None, false, false).await?; /// println!("{}", receipt); /// # Ok(()) /// # } @@ -700,6 +701,7 @@ where tx_hash: String, field: Option, confs: u64, + timeout: Option, cast_async: bool, to_json: bool, ) -> Result { @@ -716,6 +718,7 @@ where } else { PendingTransactionBuilder::new(self.provider.root(), tx_hash) .with_required_confirmations(confs) + .with_timeout(timeout.map(Duration::from_secs)) .get_receipt() .await? } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index ca7226e18..cedf96e6e 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -450,6 +450,9 @@ pub struct Config { /// Whether to enable Alphanet features. pub alphanet: bool, + /// Timeout for transactions in seconds. + pub transaction_timeout: u64, + /// Warnings gathered when loading the Config. See [`WarningsProvider`] for more information #[serde(rename = "__warnings", default, skip_serializing)] pub warnings: Vec, @@ -2143,6 +2146,7 @@ impl Default for Config { extra_args: vec![], eof_version: None, alphanet: false, + transaction_timeout: 120, _non_exhaustive: (), } } diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index b549b39a0..3bcf6b36c 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -20,9 +20,19 @@ use foundry_common::{ fmt::parse_tokens, }; use foundry_compilers::{artifacts::BytecodeObject, info::ContractInfo, utils::canonicalize}; +use foundry_config::{ + figment::{ + self, + value::{Dict, Map}, + Metadata, Profile, + }, + merge_impl_figment_convert, Config, +}; use serde_json::json; use std::{borrow::Borrow, marker::PhantomData, path::PathBuf, sync::Arc}; +merge_impl_figment_convert!(CreateArgs, opts, eth); + /// CLI arguments for `forge create`. #[derive(Clone, Debug, Parser)] pub struct CreateArgs { @@ -65,6 +75,10 @@ pub struct CreateArgs { #[arg(long, requires = "verify")] show_standard_json_input: bool, + /// Timeout to use for broadcasting transactions. + #[arg(long, env = "ETH_TIMEOUT")] + pub timeout: Option, + #[command(flatten)] opts: CoreBuildArgs, @@ -84,8 +98,9 @@ pub struct CreateArgs { impl CreateArgs { /// Executes the command to create a contract pub async fn run(mut self) -> Result<()> { + let config = self.try_load_config_emit_warnings()?; // Find Project & Compile - let project = self.opts.project()?; + let project = config.project()?; let target_path = if let Some(ref mut path) = self.contract.path { canonicalize(project.root().join(path))? @@ -114,7 +129,6 @@ impl CreateArgs { }; // Add arguments to constructor - let config = self.eth.try_load_config_emit_warnings()?; let provider = utils::get_provider(&config)?; let params = match abi.constructor { Some(ref v) => { @@ -138,7 +152,8 @@ impl CreateArgs { if self.unlocked { // Deploy with unlocked account let sender = self.eth.wallet.from.expect("required"); - self.deploy(abi, bin, params, provider, chain_id, sender).await + self.deploy(abi, bin, params, provider, chain_id, sender, config.transaction_timeout) + .await } else { // Deploy with signer let signer = self.eth.wallet.signer().await?; @@ -146,7 +161,8 @@ impl CreateArgs { let provider = ProviderBuilder::<_, _, AnyNetwork>::default() .wallet(EthereumWallet::new(signer)) .on_provider(provider); - self.deploy(abi, bin, params, provider, chain_id, deployer).await + self.deploy(abi, bin, params, provider, chain_id, deployer, config.transaction_timeout) + .await } } @@ -207,6 +223,7 @@ impl CreateArgs { } /// Deploys the contract + #[allow(clippy::too_many_arguments)] async fn deploy, T: Transport + Clone>( self, abi: JsonAbi, @@ -215,12 +232,13 @@ impl CreateArgs { provider: P, chain: u64, deployer_address: Address, + timeout: u64, ) -> Result<()> { let bin = bin.into_bytes().unwrap_or_else(|| { panic!("no bytecode found in bin object for {}", self.contract.name) }); let provider = Arc::new(provider); - let factory = ContractFactory::new(abi.clone(), bin.clone(), provider.clone()); + let factory = ContractFactory::new(abi.clone(), bin.clone(), provider.clone(), timeout); let is_args_empty = args.is_empty(); let mut deployer = @@ -367,6 +385,20 @@ impl CreateArgs { } } +impl figment::Provider for CreateArgs { + fn metadata(&self) -> Metadata { + Metadata::named("Create Args Provider") + } + + fn data(&self) -> Result, figment::Error> { + let mut dict = Dict::default(); + if let Some(timeout) = self.timeout { + dict.insert("transaction_timeout".to_string(), timeout.into()); + } + Ok(Map::from([(Config::selected_profile(), dict)])) + } +} + /// `ContractFactory` is a [`DeploymentTxFactory`] object with an /// [`Arc`] middleware. This type alias exists to preserve backwards /// compatibility with less-abstract Contracts. @@ -414,6 +446,7 @@ pub struct Deployer { abi: JsonAbi, client: B, confs: usize, + timeout: u64, _p: PhantomData

, _t: PhantomData, } @@ -428,6 +461,7 @@ where abi: self.abi.clone(), client: self.client.clone(), confs: self.confs, + timeout: self.timeout, _p: PhantomData, _t: PhantomData, } @@ -504,6 +538,7 @@ pub struct DeploymentTxFactory { client: B, abi: JsonAbi, bytecode: Bytes, + timeout: u64, _p: PhantomData

, _t: PhantomData, } @@ -517,6 +552,7 @@ where client: self.client.clone(), abi: self.abi.clone(), bytecode: self.bytecode.clone(), + timeout: self.timeout, _p: PhantomData, _t: PhantomData, } @@ -532,8 +568,8 @@ where /// Creates a factory for deployment of the Contract with bytecode, and the /// constructor defined in the abi. The client will be used to send any deployment /// transaction. - pub fn new(abi: JsonAbi, bytecode: Bytes, client: B) -> Self { - Self { client, abi, bytecode, _p: PhantomData, _t: PhantomData } + pub fn new(abi: JsonAbi, bytecode: Bytes, client: B, timeout: u64) -> Self { + Self { client, abi, bytecode, timeout, _p: PhantomData, _t: PhantomData } } /// Create a deployment tx using the provided tokens as constructor @@ -567,6 +603,7 @@ where abi: self.abi, tx, confs: 1, + timeout: self.timeout, _p: PhantomData, _t: PhantomData, }) diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 78c76a22a..cfad5206f 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -149,6 +149,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { extra_args: vec![], eof_version: None, alphanet: false, + transaction_timeout: 120, _non_exhaustive: (), }; prj.write_config(input.clone()); diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index a1113bb2d..302be1509 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -170,7 +170,14 @@ impl BundledState { .map(|(sequence_idx, sequence)| async move { let rpc_url = sequence.rpc_url(); let provider = Arc::new(get_http_provider(rpc_url)); - progress_ref.wait_for_pending(sequence_idx, sequence, &provider).await + progress_ref + .wait_for_pending( + sequence_idx, + sequence, + &provider, + self.script_config.config.transaction_timeout, + ) + .await }) .collect::>(); @@ -368,7 +375,14 @@ impl BundledState { self.sequence.save(true, false)?; sequence = self.sequence.sequences_mut().get_mut(i).unwrap(); - progress.wait_for_pending(i, sequence, &provider).await? + progress + .wait_for_pending( + i, + sequence, + &provider, + self.script_config.config.transaction_timeout, + ) + .await? } // Checkpoint save self.sequence.save(true, false)?; diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 1b2618f91..6af605690 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -181,6 +181,10 @@ pub struct ScriptArgs { )] pub with_gas_price: Option, + /// Timeout to use for broadcasting transactions. + #[arg(long, env = "ETH_TIMEOUT")] + pub timeout: Option, + #[command(flatten)] pub opts: CoreBuildArgs, @@ -453,6 +457,9 @@ impl Provider for ScriptArgs { figment::value::Value::from(etherscan_api_key.to_string()), ); } + if let Some(timeout) = self.timeout { + dict.insert("transaction_timeout".to_string(), timeout.into()); + } Ok(Map::from([(Config::selected_profile(), dict)])) } } diff --git a/crates/script/src/progress.rs b/crates/script/src/progress.rs index 6f028688b..e88885de3 100644 --- a/crates/script/src/progress.rs +++ b/crates/script/src/progress.rs @@ -168,6 +168,7 @@ impl ScriptProgress { sequence_idx: usize, deployment_sequence: &mut ScriptSequence, provider: &RetryProvider, + timeout: u64, ) -> Result<()> { if deployment_sequence.pending.is_empty() { return Ok(()); @@ -180,8 +181,11 @@ impl ScriptProgress { trace!("Checking status of {count} pending transactions"); - let futs = - deployment_sequence.pending.clone().into_iter().map(|tx| check_tx_status(provider, tx)); + let futs = deployment_sequence + .pending + .clone() + .into_iter() + .map(|tx| check_tx_status(provider, tx, timeout)); let mut tasks = futures::stream::iter(futs).buffer_unordered(10); let mut errors: Vec = vec![]; diff --git a/crates/script/src/receipts.rs b/crates/script/src/receipts.rs index c0fbd5611..c1dad5629 100644 --- a/crates/script/src/receipts.rs +++ b/crates/script/src/receipts.rs @@ -28,6 +28,7 @@ impl From for TxStatus { pub async fn check_tx_status( provider: &RetryProvider, hash: TxHash, + timeout: u64, ) -> (TxHash, Result) { // We use the inner future so that we can use ? operator in the future, but // still neatly return the tuple @@ -40,7 +41,7 @@ pub async fn check_tx_status( loop { if let Ok(receipt) = PendingTransactionBuilder::new(provider, hash) - .with_timeout(Some(Duration::from_secs(120))) + .with_timeout(Some(Duration::from_secs(timeout))) .get_receipt() .await { From 6f0fdffc7e1c61d621671a2b5539966cf069070d Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 22 Aug 2024 14:34:34 +0200 Subject: [PATCH 098/184] test: add builtin redactions to snapbox assertions (#8712) * test: add builtin redactions to snapbox assertions * chore: fmt * fix: don't redact number of runs --- Cargo.lock | 1 + crates/forge/tests/cli/build.rs | 21 +-- crates/forge/tests/cli/cmd.rs | 62 +++++---- crates/forge/tests/cli/config.rs | 8 +- crates/forge/tests/cli/create.rs | 24 ++-- crates/forge/tests/cli/script.rs | 42 +++--- crates/forge/tests/cli/test_cmd.rs | 198 ++++++++++++++++++++--------- crates/test-utils/Cargo.toml | 2 +- crates/test-utils/src/lib.rs | 2 +- crates/test-utils/src/util.rs | 54 ++++++-- 10 files changed, 268 insertions(+), 146 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 207ee83d3..53d92b8cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7981,6 +7981,7 @@ dependencies = [ "anstream", "anstyle", "normalize-line-endings", + "regex", "serde", "serde_json", "similar", diff --git a/crates/forge/tests/cli/build.rs b/crates/forge/tests/cli/build.rs index 779c7aa07..f5848173c 100644 --- a/crates/forge/tests/cli/build.rs +++ b/crates/forge/tests/cli/build.rs @@ -1,5 +1,5 @@ use foundry_config::Config; -use foundry_test_utils::{forgetest, str}; +use foundry_test_utils::{forgetest, snapbox::IntoData, str}; use globset::Glob; // tests that json is printed when --json is passed @@ -19,7 +19,6 @@ contract Dummy { // set up command cmd.args(["compile", "--format-json"]).assert_success().stdout_eq(str![[r#" -... { "errors": [ { @@ -32,23 +31,25 @@ contract Dummy { "component": "general", "severity": "error", "errorCode": "7576", - "message": "Undeclared identifier. Did you mean /"newNumber/"?", - "formattedMessage": "DeclarationError: Undeclared identifier. Did you mean /"newNumber/"?/n --> src/jsonError.sol:7:18:/n |/n7 | number = newnumber; // error here/n | ^^^^^^^^^/n/n" + "message": "Undeclared identifier. Did you mean \"newNumber\"?", + "formattedMessage": "DeclarationError: Undeclared identifier. Did you mean \"newNumber\"?\n [FILE]:7:18:\n |\n7 | number = newnumber; // error here\n | ^^^^^^^^^\n\n" } ], "sources": {}, "contracts": {}, - "build_infos": [ - { -... + "build_infos": "{...}" } -... -"#]]); +"#]].is_json()); }); // tests build output is as expected forgetest_init!(exact_build_output, |prj, cmd| { - cmd.args(["build", "--force"]).assert_success().stdout_eq(str!["Compiling[..]\n..."]); + cmd.args(["build", "--force"]).assert_success().stdout_eq(str![[r#" +Compiling 27 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +"#]]); }); // tests build output is as expected diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index cdfd90a73..9160973e9 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -235,8 +235,7 @@ forgetest!(can_detect_dirty_git_status_on_init, |prj, cmd| { cmd.current_dir(&nested); cmd.arg("init").assert_failure().stderr_eq(str![[r#" -... -Error:[..] +Error: The target directory is a part of or on its own an already initialized git repository, and it requires clean working and staging areas, including no untracked files. @@ -246,7 +245,7 @@ ignore them in the `.gitignore` file, or run this command again with the `--no-c If none of the previous steps worked, please open an issue at: https://github.com/foundry-rs/foundry/issues/new/choose -... + "#]]); // ensure nothing was emitted, dir is empty @@ -398,7 +397,7 @@ forgetest!(can_init_vscode, |prj, cmd| { assert_eq!( settings, serde_json::json!({ - "solidity.packageDefaultDependenciesContractsDirectory": "src", + "solidity.packageDefaultDependenciesContractsDirectory": "src", "solidity.packageDefaultDependenciesDirectory": "lib" }) ); @@ -758,11 +757,16 @@ contract ATest is DSTest { .unwrap(); cmd.args(["snapshot"]).assert_success().stdout_eq(str![[r#" -... +Compiling 2 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + Ran 1 test for src/ATest.t.sol:ATest -[PASS] testExample() (gas: 168) -Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in [..] -... +[PASS] testExample() ([GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + "#]]); cmd.arg("--check"); @@ -1689,22 +1693,20 @@ function test_run() external {} cmd.args(["build", "--skip", "*/test/**", "--skip", "*/script/**", "--force"]) .assert_success() .stdout_eq(str![[r#" -... -Compiling 1 files [..] -[..] +Compiling 1 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] Compiler run successful! -... + "#]]); cmd.forge_fuse() .args(["build", "--skip", "./test/**", "--skip", "./script/**", "--force"]) .assert_success() .stdout_eq(str![[r#" -... -Compiling 1 files [..] -[..] +Compiling 1 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] Compiler run successful! -... + "#]]); }); @@ -1738,44 +1740,40 @@ function test_bar() external {} // Build 2 files within test dir prj.clear(); cmd.args(["build", "test", "--force"]).assert_success().stdout_eq(str![[r#" -... -Compiling 2 files with Solc 0.8.23 -[..] +Compiling 2 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] Compiler run successful! -... + "#]]); // Build one file within src dir prj.clear(); cmd.forge_fuse(); cmd.args(["build", "src", "--force"]).assert_success().stdout_eq(str![[r#" -... -Compiling 1 files with Solc 0.8.23 -[..] +Compiling 1 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] Compiler run successful! -... + "#]]); // Build 3 files from test and src dirs prj.clear(); cmd.forge_fuse(); cmd.args(["build", "src", "test", "--force"]).assert_success().stdout_eq(str![[r#" -... -Compiling 3 files with Solc 0.8.23 -[..] +Compiling 3 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] Compiler run successful! -... + "#]]); // Build single test file prj.clear(); cmd.forge_fuse(); cmd.args(["build", "test/Bar.sol", "--force"]).assert_success().stdout_eq(str![[r#" -... -Compiling 1 files with Solc 0.8.23 -[..] +Compiling 1 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] Compiler run successful! -... + "#]]); }); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index cfad5206f..e943001af 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -401,15 +401,15 @@ contract Foo { .unwrap(); cmd.arg("build").assert_failure().stderr_eq(str![[r#" -... -Error:[..] +Error: Compiler run failed: Error (6553): The msize instruction cannot be used when the Yul optimizer is activated because it can change its semantics. Either disable the Yul optimizer or do not use the instruction. - --> src/foo.sol:6:8: + [FILE]:6:8: | 6 | assembly { | ^ (Relevant source part starts here and spans across multiple lines). -... + + "#]]); // disable yul optimizer explicitly diff --git a/crates/forge/tests/cli/create.rs b/crates/forge/tests/cli/create.rs index 6aaa4f536..c7071dcf5 100644 --- a/crates/forge/tests/cli/create.rs +++ b/crates/forge/tests/cli/create.rs @@ -151,11 +151,12 @@ forgetest_async!(can_create_template_contract, |prj, cmd| { ]); cmd.assert().stdout_eq(str![[r#" -... +Compiling 1 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] Compiler run successful! Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3 -Transaction hash: [..] +[TX_HASH] "#]]); @@ -163,7 +164,7 @@ Transaction hash: [..] No files changed, compilation skipped Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 Deployed to: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 -Transaction hash: [..] +[TX_HASH] "#]]); }); @@ -191,18 +192,19 @@ forgetest_async!(can_create_using_unlocked, |prj, cmd| { ]); cmd.assert().stdout_eq(str![[r#" -... +Compiling 1 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] Compiler run successful! Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3 -Transaction hash: [..] +[TX_HASH] "#]]); cmd.assert().stdout_eq(str![[r#" No files changed, compilation skipped Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 Deployed to: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 -Transaction hash: [..] +[TX_HASH] "#]]); }); @@ -247,11 +249,12 @@ contract ConstructorContract { ]) .assert_success() .stdout_eq(str![[r#" -... +Compiling 1 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] Compiler run successful! Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3 -Transaction hash: [..] +[TX_HASH] "#]]); @@ -283,11 +286,12 @@ contract TupleArrayConstructorContract { ]) .assert() .stdout_eq(str![[r#" -... +Compiling 1 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] Compiler run successful! Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 Deployed to: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 -Transaction hash: [..] +[TX_HASH] "#]]); }); diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index f64bce205..2b92559f2 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -54,13 +54,15 @@ contract Demo { .unwrap(); cmd.arg("script").arg(script).assert_success().stdout_eq(str![[r#" -... +Compiling 1 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! Script ran successfully. -Gas used: 22815 +[GAS] == Logs == script ran -... + "#]]); }); @@ -84,7 +86,7 @@ contract Demo { r#" ... Script ran successfully. -Gas used: 22815 +[GAS] == Logs == script ran @@ -111,13 +113,15 @@ contract Demo { cmd.arg("script").arg(script).arg("--sig").arg("myFunction()").assert_success().stdout_eq( str![[r#" -... +Compiling 1 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! Script ran successfully. -Gas used: 22815 +[GAS] == Logs == script ran -... + "#]], ); }); @@ -296,15 +300,17 @@ contract Demo { .arg("2") .assert_success() .stdout_eq(str![[r#" -... +Compiling 1 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! Script ran successfully. -Gas used: 25301 +[GAS] == Logs == script ran 1 2 -... + "#]]); }); @@ -325,9 +331,11 @@ contract Demo { .unwrap(); cmd.arg("script").arg(script).assert_success().stdout_eq(str![[r#" -... +Compiling 1 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! Script ran successfully. -Gas used: 22900 +[GAS] == Return == result: uint256 255 @@ -335,7 +343,7 @@ result: uint256 255 == Logs == script ran -... + "#]]); }); @@ -973,9 +981,11 @@ contract Demo { .args(["--skip", "tests", "--skip", TEMPLATE_CONTRACT]) .assert_success() .stdout_eq(str![[r#" -... +Compiling 1 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! Script ran successfully. -Gas used: 22900 +[GAS] == Return == result: uint256 255 @@ -983,7 +993,7 @@ result: uint256 255 == Logs == script ran -... + "#]]); }); diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 7c0499916..40890c9db 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -52,7 +52,6 @@ contract Dummy {} // run command and assert cmd.assert_failure().stdout_eq(str![[r#" -... No tests found in project! Forge looks for functions that starts with `test`. "#]]); @@ -75,7 +74,6 @@ contract Dummy {} // run command and assert cmd.assert_failure().stdout_eq(str![[r#" -... No tests match the provided pattern: match-test: `testA.*` no-match-test: `testB.*` @@ -108,7 +106,6 @@ contract TestC { // run command and assert cmd.assert_failure().stdout_eq(str![[r#" -... No tests match the provided pattern: match-test: `testA.*` no-match-test: `testB.*` @@ -197,12 +194,16 @@ contract FailTest is DSTest { .unwrap(); cmd.args(["test", "--match-path", "*src/ATest.t.sol"]).assert_success().stdout_eq(str![[r#" -... +Compiling 3 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + Ran 1 test for src/ATest.t.sol:ATest -[PASS] testPass() (gas: 190) -... -Ran 1 test suite in [..] 1 tests passed, 0 failed, 0 skipped (1 total tests) -... +[PASS] testPass() ([GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + "#]]); }); @@ -240,12 +241,16 @@ contract FailTest is DSTest { let test_path = test_path.to_string_lossy(); cmd.args(["test", "--match-path", test_path.as_ref()]).assert_success().stdout_eq(str![[r#" -... +Compiling 3 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + Ran 1 test for src/ATest.t.sol:ATest -[PASS] testPass() (gas: 190) -... -Ran 1 test suite in [..] 1 tests passed, 0 failed, 0 skipped (1 total tests) -... +[PASS] testPass() ([GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + "#]]); }); @@ -273,10 +278,16 @@ contract MyTest is DSTest { .unwrap(); cmd.arg("test").assert_success().stdout_eq(str![[r#" -... +Compiling 2 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + Ran 1 test for src/nested/forge-tests/MyTest.t.sol:MyTest -[PASS] testTrue() (gas: 168) -... +[PASS] testTrue() ([GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + "#]]); }); @@ -288,13 +299,14 @@ forgetest_init!(can_test_repeatedly, |_prj, cmd| { for _ in 0..5 { cmd.assert_success().stdout_eq(str![[r#" -... +No files changed, compilation skipped + Ran 2 tests for test/Counter.t.sol:CounterTest -[PASS] testFuzz_SetNumber(uint256) (runs: 256, μ: [..], ~: [..]) -[PASS] test_Increment() (gas: 31303) -Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in [..] ([..] CPU time) +[PASS] testFuzz_SetNumber(uint256) (runs: 256, [AVG_GAS]) +[PASS] test_Increment() ([GAS]) +Suite result: ok. 2 passed; 0 failed; 0 skipped; [ELAPSED] -Ran 1 test suite in [..] ([..] CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests) +Ran 1 test suite [ELAPSED]: 2 tests passed, 0 failed, 0 skipped (2 total tests) "#]]); } @@ -327,17 +339,16 @@ contract ContractTest is DSTest { prj.write_config(config); cmd.arg("test").assert_success().stdout_eq(str![[r#" -... -Compiling 2 files with Solc 0.8.23 -Solc 0.8.23 finished in [..] +Compiling 2 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] Compiler run successful! Ran 1 test for src/Contract.t.sol:ContractTest -[PASS] testExample() (gas: 190) -Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in [..] +[PASS] testExample() ([GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) -Ran 1 test suite in [..] 1 tests passed, 0 failed, 0 skipped (1 total tests) -... "#]]); // pin version @@ -345,17 +356,16 @@ Ran 1 test suite in [..] 1 tests passed, 0 failed, 0 skipped (1 total tests) prj.write_config(config); cmd.forge_fuse().arg("test").assert_success().stdout_eq(str![[r#" -... -Compiling 2 files with Solc 0.8.22 -Solc 0.8.22 finished in [..] +Compiling 2 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] Compiler run successful! Ran 1 test for src/Contract.t.sol:ContractTest -[PASS] testExample() (gas: 190) -Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in [..] +[PASS] testExample() ([GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) -Ran 1 test suite in [..] 1 tests passed, 0 failed, 0 skipped (1 total tests) -... "#]]); }); @@ -407,10 +417,16 @@ contract ContractTest is Test { .unwrap(); cmd.arg("test").assert_success().stdout_eq(str![[r#" -... +Compiling 2 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + Ran 1 test for test/Contract.t.sol:ContractTest -[PASS] test() (gas: 70360) -... +[PASS] test() ([GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + "#]]); }); @@ -473,19 +489,24 @@ contract USDTCallingTest is Test { .unwrap(); cmd.args(["test", "-vvvv"]).assert_success().stdout_eq(str![[r#" -... +Compiling 1 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] Compiler run successful! Ran 1 test for test/Contract.t.sol:USDTCallingTest -[PASS] test() (gas: 9537) +[PASS] test() ([GAS]) Traces: [9537] USDTCallingTest::test() ├─ [0] VM::createSelectFork("[..]") │ └─ ← [Return] 0 ├─ [3110] 0xdAC17F958D2ee523a2206206994597C13D831ec7::name() [staticcall] │ └─ ← [Return] "Tether USD" - └─ ← [Stop][..] -... + └─ ← [Stop] + +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + "#]]); }); @@ -514,19 +535,32 @@ contract CustomTypesTest is Test { .unwrap(); cmd.args(["test", "-vvvv"]).assert_failure().stdout_eq(str![[r#" -... +Compiling 1 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + Ran 2 tests for test/Contract.t.sol:CustomTypesTest -[FAIL. Reason: PoolNotInitialized()] testErr() (gas: 254) +[FAIL. Reason: PoolNotInitialized()] testErr() ([GAS]) Traces: [254] CustomTypesTest::testErr() └─ ← [Revert] PoolNotInitialized() -[PASS] testEvent() (gas: 1268) +[PASS] testEvent() ([GAS]) Traces: [1268] CustomTypesTest::testEvent() ├─ emit MyEvent(a: 100) - └─ ← [Stop][..] -... + └─ ← [Stop] + +Suite result: FAILED. 1 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 1 failed, 0 skipped (2 total tests) + +Failing tests: +Encountered 1 failing test in test/Contract.t.sol:CustomTypesTest +[FAIL. Reason: PoolNotInitialized()] testErr() ([GAS]) + +Encountered a total of 1 failing tests, 1 tests succeeded + "#]]); }); @@ -704,9 +738,22 @@ contract CounterTest is Test { // make sure there are only 61 runs (with proptest shrinking same test results in 298 runs) cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" -... -[..]testAddOne(uint256) (runs: 61, μ: [..], ~: [..]) -... +Compiling 23 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/CounterFuzz.t.sol:CounterTest +[FAIL. Reason: panic: arithmetic underflow or overflow (0x11); counterexample: calldata=0xa76d58f5ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff args=[115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]]] testAddOne(uint256) (runs: 61, [AVG_GAS]) +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) + +Failing tests: +Encountered 1 failing test in test/CounterFuzz.t.sol:CounterTest +[FAIL. Reason: panic: arithmetic underflow or overflow (0x11); counterexample: calldata=0xa76d58f5ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff args=[115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]]] testAddOne(uint256) (runs: 61, [AVG_GAS]) + +Encountered a total of 1 failing tests, 0 tests succeeded + "#]]); }); @@ -742,9 +789,22 @@ contract CounterTest is Test { // make sure invariant test exit early with 0 runs cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" -... -[..]invariant_early_exit() (runs: 0, calls: 0, reverts: 0) -... +Compiling 23 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/CounterInvariant.t.sol:CounterTest +[FAIL. Reason: failed to set up invariant testing environment: wrong count] invariant_early_exit() (runs: 0, calls: 0, reverts: 0) +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) + +Failing tests: +Encountered 1 failing test in test/CounterInvariant.t.sol:CounterTest +[FAIL. Reason: failed to set up invariant testing environment: wrong count] invariant_early_exit() (runs: 0, calls: 0, reverts: 0) + +Encountered a total of 1 failing tests, 0 tests succeeded + "#]]); }); @@ -783,11 +843,22 @@ contract ReplayFailuresTest is Test { // Perform only the 2 failing tests from last run. cmd.forge_fuse().args(["test", "--rerun"]).assert_failure().stdout_eq(str![[r#" -... +No files changed, compilation skipped + Ran 2 tests for test/ReplayFailures.t.sol:ReplayFailuresTest -[FAIL. Reason: revert: testB failed] testB() (gas: 303) -[FAIL. Reason: revert: testD failed] testD() (gas: 314) -... +[FAIL. Reason: revert: testB failed] testB() ([GAS]) +[FAIL. Reason: revert: testD failed] testD() ([GAS]) +Suite result: FAILED. 0 passed; 2 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 2 failed, 0 skipped (2 total tests) + +Failing tests: +Encountered 2 failing tests in test/ReplayFailures.t.sol:ReplayFailuresTest +[FAIL. Reason: revert: testB failed] testB() ([GAS]) +[FAIL. Reason: revert: testD failed] testD() ([GAS]) + +Encountered a total of 2 failing tests, 0 tests succeeded + "#]]); }); @@ -1013,7 +1084,12 @@ contract SimpleContractTest is Test { ) .unwrap(); cmd.args(["test", "-vvvv", "--decode-internal"]).assert_success().stdout_eq(str![[r#" -... +Compiling 24 files with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/Simple.sol:SimpleContractTest +[PASS] test() ([GAS]) Traces: [250463] SimpleContractTest::test() ├─ [171014] → new SimpleContract@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f @@ -1029,7 +1105,11 @@ Traces: │ │ └─ ← 0x0000000000000000000000000000000000000000 │ └─ ← [Stop] └─ ← [Stop] -... + +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + "#]]); }); diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index e3cb1f542..db47ce602 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -33,7 +33,7 @@ tracing.workspace = true tracing-subscriber = { workspace = true, features = ["env-filter"] } walkdir.workspace = true rand.workspace = true -snapbox = { version = "0.6.9", features = ["json"] } +snapbox = { version = "0.6.9", features = ["json", "regex"] } [features] # feature for integration tests that test external projects diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index 2a0093278..0fcfb6216 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -28,7 +28,7 @@ pub use script::{ScriptOutcome, ScriptTester}; // re-exports for convenience pub use foundry_compilers; -pub use snapbox::{assert_data_eq, file, str}; +pub use snapbox::{self, assert_data_eq, file, str}; /// Initializes tracing for tests. pub fn init_tracing() { diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 9b6380e07..7ed13cf14 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -920,18 +920,6 @@ impl TestCommand { output } - /// Runs the command and asserts that it resulted in success - #[track_caller] - pub fn assert_success(&mut self) -> OutputAssert { - self.assert().success() - } - - /// Runs the command and asserts that it failed. - #[track_caller] - pub fn assert_failure(&mut self) -> OutputAssert { - self.assert().failure() - } - /// Executes command, applies stdin function and returns output #[track_caller] pub fn execute(&mut self) -> Output { @@ -1063,11 +1051,51 @@ stderr: ) } + /// Runs the command, returning a [`snapbox`] object to assert the command output. + #[track_caller] pub fn assert(&mut self) -> OutputAssert { - OutputAssert::new(self.execute()) + OutputAssert::new(self.execute()).with_assert(test_assert()) + } + + /// Runs the command and asserts that it resulted in success. + #[track_caller] + pub fn assert_success(&mut self) -> OutputAssert { + self.assert().success() + } + + /// Runs the command and asserts that it failed. + #[track_caller] + pub fn assert_failure(&mut self) -> OutputAssert { + self.assert().failure() } } +fn test_assert() -> snapbox::Assert { + snapbox::Assert::new() + .action_env(snapbox::assert::DEFAULT_ACTION_ENV) + .redact_with(test_redactions()) +} + +fn test_redactions() -> snapbox::Redactions { + static REDACTIONS: Lazy = Lazy::new(|| { + let mut r = snapbox::Redactions::new(); + let redactions = [ + ("[SOLC_VERSION]", r"Solc( version)? \d+.\d+.\d+"), + ("[ELAPSED]", r"(finished )?in \d+(\.\d+)?\w?s( \(.*?s CPU time\))?"), + ("[GAS]", r"[Gg]as( used)?: \d+"), + ("[AVG_GAS]", r"μ: \d+, ~: \d+"), + ("[FILE]", r"-->.*\.sol"), + ("[FILE]", r"Location(.|\n)*\.rs(.|\n)*Backtrace"), + ("[TX_HASH]", r"Transaction hash: 0x[0-9A-Fa-f]{64}"), + ]; + for (placeholder, re) in redactions { + r.insert(placeholder, Regex::new(re).expect(re)).expect(re); + } + r + }); + REDACTIONS.clone() +} + /// Extension trait for [`Output`]. /// /// These function will read the path's content and assert that the process' output matches the From 91656a28b104654fd08e3daf2ab7ffc5a2008f2f Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 22 Aug 2024 15:26:32 +0200 Subject: [PATCH 099/184] chore: replace `once_cell` with `std::sync` (#8718) * chore: replace `once_cell` with `std::sync` * chore: fmt * docs --- Cargo.lock | 10 --------- Cargo.toml | 5 ++--- clippy.toml | 2 +- crates/chisel/Cargo.toml | 1 - crates/chisel/benches/session_source.rs | 8 +++---- crates/chisel/src/dispatcher.rs | 8 +++---- crates/cli/Cargo.toml | 1 - crates/cli/src/opts/dependency.rs | 7 +++---- crates/common/Cargo.toml | 1 - crates/common/src/shell.rs | 5 ++--- crates/common/src/term.rs | 8 ++++--- crates/config/Cargo.toml | 1 - crates/config/src/inline/mod.rs | 5 ++--- crates/config/src/resolve.rs | 7 +++---- crates/doc/Cargo.toml | 1 - .../doc/src/preprocessor/infer_hyperlinks.rs | 4 ++-- crates/doc/src/writer/buf_writer.rs | 14 +++++++------ crates/evm/abi/Cargo.toml | 1 - crates/evm/abi/src/console/hardhat.rs | 6 +++--- crates/evm/traces/Cargo.toml | 1 - crates/evm/traces/src/decoder/mod.rs | 8 ++++--- crates/forge/Cargo.toml | 1 - crates/forge/bin/cmd/inspect.rs | 7 +++---- crates/forge/bin/cmd/install.rs | 6 +++--- crates/forge/bin/cmd/snapshot.rs | 4 ++-- crates/forge/tests/it/test_helpers.rs | 17 +++++++-------- crates/test-utils/Cargo.toml | 1 - crates/test-utils/src/rpc.rs | 12 ++++++----- crates/test-utils/src/util.rs | 21 +++++++++---------- crates/verify/Cargo.toml | 1 - crates/verify/src/etherscan/mod.rs | 7 +++---- 31 files changed, 80 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 53d92b8cf..132c4840d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1969,7 +1969,6 @@ dependencies = [ "foundry-compilers", "foundry-config", "foundry-evm", - "once_cell", "regex", "reqwest", "revm", @@ -3324,7 +3323,6 @@ dependencies = [ "indicatif", "itertools 0.13.0", "mockall", - "once_cell", "opener", "parking_lot", "paste", @@ -3373,7 +3371,6 @@ dependencies = [ "foundry-config", "itertools 0.13.0", "mdbook", - "once_cell", "rayon", "regex", "serde", @@ -3482,7 +3479,6 @@ dependencies = [ "foundry-test-utils", "futures", "itertools 0.13.0", - "once_cell", "regex", "reqwest", "revm-primitives", @@ -3601,7 +3597,6 @@ dependencies = [ "foundry-wallets", "futures", "indicatif", - "once_cell", "regex", "serde", "strsim", @@ -3645,7 +3640,6 @@ dependencies = [ "foundry-config", "foundry-macros", "num-format", - "once_cell", "reqwest", "rustc-hash", "semver 1.0.23", @@ -3807,7 +3801,6 @@ dependencies = [ "glob", "globset", "number_prefix", - "once_cell", "path-slash", "regex", "reqwest", @@ -3880,7 +3873,6 @@ dependencies = [ "foundry-macros", "foundry-test-utils", "itertools 0.13.0", - "once_cell", "rustc-hash", ] @@ -3977,7 +3969,6 @@ dependencies = [ "foundry-linking", "futures", "itertools 0.13.0", - "once_cell", "rayon", "revm", "revm-inspectors", @@ -4044,7 +4035,6 @@ dependencies = [ "foundry-common", "foundry-compilers", "foundry-config", - "once_cell", "parking_lot", "rand", "regex", diff --git a/Cargo.toml b/Cargo.toml index 238bc38bd..8563d1add 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ resolver = "2" version = "0.2.0" edition = "2021" # Remember to update clippy.toml as well -rust-version = "1.79" +rust-version = "1.80" authors = ["Foundry Contributors"] license = "MIT OR Apache-2.0" homepage = "https://github.com/foundry-rs/foundry" @@ -230,7 +230,6 @@ futures = "0.3" itertools = "0.13" jsonpath_lib = "0.3" k256 = "0.13" -once_cell = "1" parking_lot = "0.12" rand = "0.8" rustc-hash = "2.0" @@ -287,4 +286,4 @@ alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "511ae alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } revm = { git = "https://github.com/bluealloy/revm", rev = "caadc71" } -revm-primitives = { git = "https://github.com/bluealloy/revm", rev = "caadc71" } \ No newline at end of file +revm-primitives = { git = "https://github.com/bluealloy/revm", rev = "caadc71" } diff --git a/clippy.toml b/clippy.toml index b5a99df72..92b35ffc0 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,4 +1,4 @@ -msrv = "1.79" +msrv = "1.80" # bytes::Bytes is included by default and alloy_primitives::Bytes is a wrapper around it, # so it is safe to ignore it as well ignore-interior-mutability = ["bytes::Bytes", "alloy_primitives::Bytes"] diff --git a/crates/chisel/Cargo.toml b/crates/chisel/Cargo.toml index 64e218f97..bbfeb6ee9 100644 --- a/crates/chisel/Cargo.toml +++ b/crates/chisel/Cargo.toml @@ -50,7 +50,6 @@ alloy-rpc-types.workspace = true clap = { version = "4", features = ["derive", "env", "wrap_help"] } dirs = "5" eyre.workspace = true -once_cell = "1.18.0" regex = "1" reqwest.workspace = true revm.workspace = true diff --git a/crates/chisel/benches/session_source.rs b/crates/chisel/benches/session_source.rs index f54944966..49d127c47 100644 --- a/crates/chisel/benches/session_source.rs +++ b/crates/chisel/benches/session_source.rs @@ -1,12 +1,12 @@ use chisel::session_source::{SessionSource, SessionSourceConfig}; use criterion::{criterion_group, Criterion}; use foundry_compilers::solc::Solc; -use once_cell::sync::Lazy; use semver::Version; -use std::hint::black_box; +use std::{hint::black_box, sync::LazyLock}; use tokio::runtime::Runtime; -static SOLC: Lazy = Lazy::new(|| Solc::find_or_install(&Version::new(0, 8, 19)).unwrap()); +static SOLC: LazyLock = + LazyLock::new(|| Solc::find_or_install(&Version::new(0, 8, 19)).unwrap()); /// Benchmark for the `clone_with_new_line` function in [SessionSource] fn clone_with_new_line(c: &mut Criterion) { @@ -74,7 +74,7 @@ fn rt() -> Runtime { fn main() { // Install before benches if not present - let _ = Lazy::force(&SOLC); + let _ = LazyLock::force(&SOLC); session_source_benches(); diff --git a/crates/chisel/src/dispatcher.rs b/crates/chisel/src/dispatcher.rs index 909b7525c..6afa7ccb9 100644 --- a/crates/chisel/src/dispatcher.rs +++ b/crates/chisel/src/dispatcher.rs @@ -22,7 +22,6 @@ use foundry_evm::{ render_trace_arena, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, }, }; -use once_cell::sync::Lazy; use regex::Regex; use reqwest::Url; use serde::{Deserialize, Serialize}; @@ -33,6 +32,7 @@ use std::{ io::Write, path::{Path, PathBuf}, process::Command, + sync::LazyLock, }; use strum::IntoEnumIterator; use tracing::debug; @@ -48,11 +48,11 @@ pub static COMMAND_LEADER: char = '!'; pub static CHISEL_CHAR: &str = "⚒️"; /// Matches Solidity comments -static COMMENT_RE: Lazy = - Lazy::new(|| Regex::new(r"^\s*(?://.*\s*$)|(/*[\s\S]*?\*/\s*$)").unwrap()); +static COMMENT_RE: LazyLock = + LazyLock::new(|| Regex::new(r"^\s*(?://.*\s*$)|(/*[\s\S]*?\*/\s*$)").unwrap()); /// Matches Ethereum addresses that are not strings -static ADDRESS_RE: Lazy = Lazy::new(|| { +static ADDRESS_RE: LazyLock = LazyLock::new(|| { Regex::new(r#"(?m)(([^"']\s*)|^)(?P

0x[a-fA-F0-9]{40})((\s*[^"'\w])|$)"#).unwrap() }); diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 9c8b3fab8..e8690a0a7 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -35,7 +35,6 @@ dotenvy = "0.15" eyre.workspace = true futures.workspace = true indicatif = "0.17" -once_cell.workspace = true regex = { version = "1", default-features = false } serde.workspace = true strsim = "0.11" diff --git a/crates/cli/src/opts/dependency.rs b/crates/cli/src/opts/dependency.rs index 945386d2b..6fa33a53f 100644 --- a/crates/cli/src/opts/dependency.rs +++ b/crates/cli/src/opts/dependency.rs @@ -1,14 +1,13 @@ //! CLI dependency parsing use eyre::Result; -use once_cell::sync::Lazy; use regex::Regex; -use std::str::FromStr; +use std::{str::FromStr, sync::LazyLock}; -static GH_REPO_REGEX: Lazy = Lazy::new(|| Regex::new(r"[\w-]+/[\w.-]+").unwrap()); +static GH_REPO_REGEX: LazyLock = LazyLock::new(|| Regex::new(r"[\w-]+/[\w.-]+").unwrap()); /// Git repo prefix regex -pub static GH_REPO_PREFIX_REGEX: Lazy = Lazy::new(|| { +pub static GH_REPO_PREFIX_REGEX: LazyLock = LazyLock::new(|| { Regex::new(r"((git@)|(git\+https://)|(https://)|(org-([A-Za-z0-9-])+@))?(?P[A-Za-z0-9-]+)\.(?P[A-Za-z0-9-]+)(/|:)") .unwrap() }); diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 5c843979a..73bd90cc7 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -51,7 +51,6 @@ comfy-table.workspace = true dunce.workspace = true eyre.workspace = true num-format.workspace = true -once_cell.workspace = true reqwest.workspace = true rustc-hash.workspace = true semver.workspace = true diff --git a/crates/common/src/shell.rs b/crates/common/src/shell.rs index 898b154fa..5ce6d5b38 100644 --- a/crates/common/src/shell.rs +++ b/crates/common/src/shell.rs @@ -1,16 +1,15 @@ //! Helpers for printing to output -use once_cell::sync::OnceCell; use serde::Serialize; use std::{ error::Error, fmt, io, io::Write, - sync::{Arc, Mutex}, + sync::{Arc, Mutex, OnceLock}, }; /// Stores the configured shell for the duration of the program -static SHELL: OnceCell = OnceCell::new(); +static SHELL: OnceLock = OnceLock::new(); /// Error indicating that `set_hook` was unable to install the provided ErrorHook #[derive(Clone, Copy, Debug)] diff --git a/crates/common/src/term.rs b/crates/common/src/term.rs index 925456e4c..30aaf6c52 100644 --- a/crates/common/src/term.rs +++ b/crates/common/src/term.rs @@ -3,13 +3,15 @@ use foundry_compilers::{ artifacts::remappings::Remapping, report::{self, BasicStdoutReporter, Reporter}, }; -use once_cell::sync::Lazy; use semver::Version; use std::{ io, io::{prelude::*, IsTerminal}, path::{Path, PathBuf}, - sync::mpsc::{self, TryRecvError}, + sync::{ + mpsc::{self, TryRecvError}, + LazyLock, + }, thread, time::Duration, }; @@ -25,7 +27,7 @@ pub static SPINNERS: &[&[&str]] = &[ &[" ", "▘", "▀", "▜", "█", "▟", "▄", "▖"], ]; -static TERM_SETTINGS: Lazy = Lazy::new(TermSettings::from_env); +static TERM_SETTINGS: LazyLock = LazyLock::new(TermSettings::from_env); /// Helper type to determine the current tty pub struct TermSettings { diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index c29a5fd44..f15598122 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -31,7 +31,6 @@ globset = "0.4" glob = "0.3" Inflector = "0.11" number_prefix = "0.4" -once_cell.workspace = true regex = "1" reqwest.workspace = true semver = { workspace = true, features = ["serde"] } diff --git a/crates/config/src/inline/mod.rs b/crates/config/src/inline/mod.rs index 9bdf1d5d0..36bad2514 100644 --- a/crates/config/src/inline/mod.rs +++ b/crates/config/src/inline/mod.rs @@ -1,6 +1,5 @@ use crate::Config; -use once_cell::sync::Lazy; -use std::collections::HashMap; +use std::{collections::HashMap, sync::LazyLock}; mod conf_parser; pub use conf_parser::*; @@ -15,7 +14,7 @@ pub const INLINE_CONFIG_FUZZ_KEY: &str = "fuzz"; pub const INLINE_CONFIG_INVARIANT_KEY: &str = "invariant"; const INLINE_CONFIG_PREFIX: &str = "forge-config"; -static INLINE_CONFIG_PREFIX_SELECTED_PROFILE: Lazy = Lazy::new(|| { +static INLINE_CONFIG_PREFIX_SELECTED_PROFILE: LazyLock = LazyLock::new(|| { let selected_profile = Config::selected_profile().to_string(); format!("{INLINE_CONFIG_PREFIX}:{selected_profile}.") }); diff --git a/crates/config/src/resolve.rs b/crates/config/src/resolve.rs index 746280f3d..179ecffa2 100644 --- a/crates/config/src/resolve.rs +++ b/crates/config/src/resolve.rs @@ -1,12 +1,11 @@ //! Helper for resolving env vars -use once_cell::sync::Lazy; use regex::Regex; -use std::{env, env::VarError, fmt}; +use std::{env, env::VarError, fmt, sync::LazyLock}; /// A regex that matches `${val}` placeholders -pub static RE_PLACEHOLDER: Lazy = - Lazy::new(|| Regex::new(r"(?m)(?P\$\{\s*(?P.*?)\s*})").unwrap()); +pub static RE_PLACEHOLDER: LazyLock = + LazyLock::new(|| Regex::new(r"(?m)(?P\$\{\s*(?P.*?)\s*})").unwrap()); /// Error when we failed to resolve an env var #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/crates/doc/Cargo.toml b/crates/doc/Cargo.toml index ab74ef697..22abae5be 100644 --- a/crates/doc/Cargo.toml +++ b/crates/doc/Cargo.toml @@ -25,7 +25,6 @@ derive_more = "0.99" eyre.workspace = true itertools.workspace = true mdbook = { version = "0.4", default-features = false, features = ["search"] } -once_cell.workspace = true rayon.workspace = true serde_json.workspace = true serde.workspace = true diff --git a/crates/doc/src/preprocessor/infer_hyperlinks.rs b/crates/doc/src/preprocessor/infer_hyperlinks.rs index a7f7c19c4..613976ec8 100644 --- a/crates/doc/src/preprocessor/infer_hyperlinks.rs +++ b/crates/doc/src/preprocessor/infer_hyperlinks.rs @@ -1,18 +1,18 @@ use super::{Preprocessor, PreprocessorId}; use crate::{Comments, Document, ParseItem, ParseSource}; use forge_fmt::solang_ext::SafeUnwrap; -use once_cell::sync::Lazy; use regex::{Captures, Match, Regex}; use std::{ borrow::Cow, path::{Path, PathBuf}, + sync::LazyLock, }; /// A regex that matches `{identifier-part}` placeholders /// /// Overloaded functions are referenced by including the exact function arguments in the `part` /// section of the placeholder. -static RE_INLINE_LINK: Lazy = Lazy::new(|| { +static RE_INLINE_LINK: LazyLock = LazyLock::new(|| { Regex::new(r"(?m)(\{(?Pxref-)?(?P[a-zA-Z_][0-9a-zA-Z_]*)(-(?P[a-zA-Z_][0-9a-zA-Z_-]*))?}(\[(?P(.*?))\])?)").unwrap() }); diff --git a/crates/doc/src/writer/buf_writer.rs b/crates/doc/src/writer/buf_writer.rs index 7ab47c953..dfec68fe2 100644 --- a/crates/doc/src/writer/buf_writer.rs +++ b/crates/doc/src/writer/buf_writer.rs @@ -1,21 +1,23 @@ use crate::{writer::traits::ParamLike, AsDoc, CommentTag, Comments, Deployment, Markdown}; use itertools::Itertools; -use once_cell::sync::Lazy; use solang_parser::pt::{ErrorParameter, EventParameter, Parameter, VariableDeclaration}; -use std::fmt::{self, Display, Write}; +use std::{ + fmt::{self, Display, Write}, + sync::LazyLock, +}; /// Solidity language name. const SOLIDITY: &str = "solidity"; /// Headers and separator for rendering parameter table. const PARAM_TABLE_HEADERS: &[&str] = &["Name", "Type", "Description"]; -static PARAM_TABLE_SEPARATOR: Lazy = - Lazy::new(|| PARAM_TABLE_HEADERS.iter().map(|h| "-".repeat(h.len())).join("|")); +static PARAM_TABLE_SEPARATOR: LazyLock = + LazyLock::new(|| PARAM_TABLE_HEADERS.iter().map(|h| "-".repeat(h.len())).join("|")); /// Headers and separator for rendering the deployments table. const DEPLOYMENTS_TABLE_HEADERS: &[&str] = &["Network", "Address"]; -static DEPLOYMENTS_TABLE_SEPARATOR: Lazy = - Lazy::new(|| DEPLOYMENTS_TABLE_HEADERS.iter().map(|h| "-".repeat(h.len())).join("|")); +static DEPLOYMENTS_TABLE_SEPARATOR: LazyLock = + LazyLock::new(|| DEPLOYMENTS_TABLE_HEADERS.iter().map(|h| "-".repeat(h.len())).join("|")); /// The buffered writer. /// Writes various display items into the internal buffer. diff --git a/crates/evm/abi/Cargo.toml b/crates/evm/abi/Cargo.toml index 892963acd..330ea6626 100644 --- a/crates/evm/abi/Cargo.toml +++ b/crates/evm/abi/Cargo.toml @@ -22,7 +22,6 @@ alloy-sol-types = { workspace = true, features = ["json"] } derive_more.workspace = true itertools.workspace = true -once_cell.workspace = true rustc-hash.workspace = true [dev-dependencies] diff --git a/crates/evm/abi/src/console/hardhat.rs b/crates/evm/abi/src/console/hardhat.rs index bdacddba5..a99ad8fc6 100644 --- a/crates/evm/abi/src/console/hardhat.rs +++ b/crates/evm/abi/src/console/hardhat.rs @@ -2,8 +2,8 @@ use alloy_primitives::Selector; use alloy_sol_types::sol; use foundry_common_fmt::*; use foundry_macros::ConsoleFmt; -use once_cell::sync::Lazy; use rustc_hash::FxHashMap; +use std::sync::LazyLock; sol!( #[sol(abi)] @@ -39,8 +39,8 @@ pub fn hh_console_selector(input: &[u8]) -> Option<&'static Selector> { /// `hardhat/console.log` logs its events manually, and in functions that accept integers they're /// encoded as `abi.encodeWithSignature("log(int)", p0)`, which is not the canonical ABI encoding /// for `int` that Solidity and [`sol!`] use. -pub static HARDHAT_CONSOLE_SELECTOR_PATCHES: Lazy> = - Lazy::new(|| FxHashMap::from_iter(include!("./patches.rs"))); +pub static HARDHAT_CONSOLE_SELECTOR_PATCHES: LazyLock> = + LazyLock::new(|| FxHashMap::from_iter(include!("./patches.rs"))); #[cfg(test)] mod tests { diff --git a/crates/evm/traces/Cargo.toml b/crates/evm/traces/Cargo.toml index 924268065..96fb8d1c6 100644 --- a/crates/evm/traces/Cargo.toml +++ b/crates/evm/traces/Cargo.toml @@ -35,7 +35,6 @@ revm-inspectors.workspace = true eyre.workspace = true futures.workspace = true itertools.workspace = true -once_cell.workspace = true serde.workspace = true tokio = { workspace = true, features = ["time", "macros"] } tracing.workspace = true diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index c8d1e461a..1bb20381b 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -24,10 +24,12 @@ use foundry_evm_core::{ }, }; use itertools::Itertools; -use once_cell::sync::OnceCell; use revm_inspectors::tracing::types::{DecodedCallLog, DecodedCallTrace}; use rustc_hash::FxHashMap; -use std::collections::{hash_map::Entry, BTreeMap, HashMap}; +use std::{ + collections::{hash_map::Entry, BTreeMap, HashMap}, + sync::OnceLock, +}; mod precompiles; @@ -145,7 +147,7 @@ impl CallTraceDecoder { pub fn new() -> &'static Self { // If you want to take arguments in this function, assign them to the fields of the cloned // lazy instead of removing it - static INIT: OnceCell = OnceCell::new(); + static INIT: OnceLock = OnceLock::new(); INIT.get_or_init(Self::init) } diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index eb3e94406..a4fce5cc3 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -79,7 +79,6 @@ dunce.workspace = true futures.workspace = true indicatif = "0.17" itertools.workspace = true -once_cell.workspace = true parking_lot.workspace = true regex = { version = "1", default-features = false } reqwest = { workspace = true, features = ["json"] } diff --git a/crates/forge/bin/cmd/inspect.rs b/crates/forge/bin/cmd/inspect.rs index c1c9f7db8..d3097854c 100644 --- a/crates/forge/bin/cmd/inspect.rs +++ b/crates/forge/bin/cmd/inspect.rs @@ -16,9 +16,8 @@ use foundry_compilers::{ info::ContractInfo, utils::canonicalize, }; -use once_cell::sync::Lazy; use regex::Regex; -use std::fmt; +use std::{fmt, sync::LazyLock}; /// CLI arguments for `forge inspect`. #[derive(Clone, Debug, Parser)] @@ -398,8 +397,8 @@ fn print_yul(yul: Option<&str>, pretty: bool) -> Result<()> { eyre::bail!("Could not get IR output"); }; - static YUL_COMMENTS: Lazy = - Lazy::new(|| Regex::new(r"(///.*\n\s*)|(\s*/\*\*.*\*/)").unwrap()); + static YUL_COMMENTS: LazyLock = + LazyLock::new(|| Regex::new(r"(///.*\n\s*)|(\s*/\*\*.*\*/)").unwrap()); if pretty { println!("{}", YUL_COMMENTS.replace_all(yul, "")); diff --git a/crates/forge/bin/cmd/install.rs b/crates/forge/bin/cmd/install.rs index 57096f859..448d5b1ad 100644 --- a/crates/forge/bin/cmd/install.rs +++ b/crates/forge/bin/cmd/install.rs @@ -7,18 +7,18 @@ use foundry_cli::{ }; use foundry_common::fs; use foundry_config::{impl_figment_convert_basic, Config}; -use once_cell::sync::Lazy; use regex::Regex; use semver::Version; use std::{ io::IsTerminal, path::{Path, PathBuf}, str, + sync::LazyLock, }; use yansi::Paint; -static DEPENDENCY_VERSION_TAG_REGEX: Lazy = - Lazy::new(|| Regex::new(r"^v?\d+(\.\d+)*$").unwrap()); +static DEPENDENCY_VERSION_TAG_REGEX: LazyLock = + LazyLock::new(|| Regex::new(r"^v?\d+(\.\d+)*$").unwrap()); /// CLI arguments for `forge install`. #[derive(Clone, Debug, Parser)] diff --git a/crates/forge/bin/cmd/snapshot.rs b/crates/forge/bin/cmd/snapshot.rs index 6c102c9d6..8c3d57fc3 100644 --- a/crates/forge/bin/cmd/snapshot.rs +++ b/crates/forge/bin/cmd/snapshot.rs @@ -4,7 +4,6 @@ use clap::{builder::RangedU64ValueParser, Parser, ValueHint}; use eyre::{Context, Result}; use forge::result::{SuiteTestResult, TestKindReport, TestOutcome}; use foundry_cli::utils::STATIC_FUZZ_SEED; -use once_cell::sync::Lazy; use regex::Regex; use std::{ cmp::Ordering, @@ -13,12 +12,13 @@ use std::{ io::{self, BufRead}, path::{Path, PathBuf}, str::FromStr, + sync::LazyLock, }; use yansi::Paint; /// A regex that matches a basic snapshot entry like /// `Test:testDeposit() (gas: 58804)` -pub static RE_BASIC_SNAPSHOT_ENTRY: Lazy = Lazy::new(|| { +pub static RE_BASIC_SNAPSHOT_ENTRY: LazyLock = LazyLock::new(|| { Regex::new(r"(?P(.*?)):(?P(\w+)\s*\((.*?)\))\s*\(((gas:)?\s*(?P\d+)|(runs:\s*(?P\d+),\s*μ:\s*(?P\d+),\s*~:\s*(?P\d+))|(runs:\s*(?P\d+),\s*calls:\s*(?P\d+),\s*reverts:\s*(?P\d+)))\)").unwrap() }); diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 480a924a0..fdef2f2ec 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -20,17 +20,16 @@ use foundry_evm::{ opts::{Env, EvmOpts}, }; use foundry_test_utils::{fd_lock, init_tracing, rpc::next_rpc_endpoint}; -use once_cell::sync::Lazy; use std::{ env, fmt, io::Write, path::{Path, PathBuf}, - sync::Arc, + sync::{Arc, LazyLock}, }; pub const RE_PATH_SEPARATOR: &str = "/"; const TESTDATA: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../testdata"); -static VYPER: Lazy = Lazy::new(|| std::env::temp_dir().join("vyper")); +static VYPER: LazyLock = LazyLock::new(|| std::env::temp_dir().join("vyper")); /// Profile for the tests group. Used to configure separate configurations for test runs. pub enum ForgeTestProfile { @@ -334,16 +333,16 @@ pub fn get_compiled(project: &mut Project) -> ProjectCompileOutput { } /// Default data for the tests group. -pub static TEST_DATA_DEFAULT: Lazy = - Lazy::new(|| ForgeTestData::new(ForgeTestProfile::Default)); +pub static TEST_DATA_DEFAULT: LazyLock = + LazyLock::new(|| ForgeTestData::new(ForgeTestProfile::Default)); /// Data for tests requiring Cancun support on Solc and EVM level. -pub static TEST_DATA_CANCUN: Lazy = - Lazy::new(|| ForgeTestData::new(ForgeTestProfile::Cancun)); +pub static TEST_DATA_CANCUN: LazyLock = + LazyLock::new(|| ForgeTestData::new(ForgeTestProfile::Cancun)); /// Data for tests requiring Cancun support on Solc and EVM level. -pub static TEST_DATA_MULTI_VERSION: Lazy = - Lazy::new(|| ForgeTestData::new(ForgeTestProfile::MultiVersion)); +pub static TEST_DATA_MULTI_VERSION: LazyLock = + LazyLock::new(|| ForgeTestData::new(ForgeTestProfile::MultiVersion)); pub fn manifest_root() -> &'static Path { let mut root = Path::new(env!("CARGO_MANIFEST_DIR")); diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index db47ce602..960e22124 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -24,7 +24,6 @@ alloy-provider.workspace = true eyre.workspace = true fd-lock = "4.0.0" -once_cell.workspace = true parking_lot.workspace = true similar-asserts.workspace = true regex = "1" diff --git a/crates/test-utils/src/rpc.rs b/crates/test-utils/src/rpc.rs index 8d4229da7..96415af3f 100644 --- a/crates/test-utils/src/rpc.rs +++ b/crates/test-utils/src/rpc.rs @@ -1,12 +1,14 @@ //! RPC API keys utilities. use foundry_config::NamedChain; -use once_cell::sync::Lazy; use rand::seq::SliceRandom; -use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{ + atomic::{AtomicUsize, Ordering}, + LazyLock, +}; // List of general purpose infura keys to rotate through -static INFURA_KEYS: Lazy> = Lazy::new(|| { +static INFURA_KEYS: LazyLock> = LazyLock::new(|| { let mut keys = vec![ // "16a8be88795540b9b3903d8de0f7baa5", // "f4a0bdad42674adab5fc0ac077ffab2b", @@ -19,7 +21,7 @@ static INFURA_KEYS: Lazy> = Lazy::new(|| { }); // List of alchemy keys for mainnet -static ALCHEMY_KEYS: Lazy> = Lazy::new(|| { +static ALCHEMY_KEYS: LazyLock> = LazyLock::new(|| { let mut keys = vec![ "ib1f4u1ojm-9lJJypwkeZeG-75TJRB7O", "7mTtk6IW4DwroGnKmG_bOWri2hyaGYhX", @@ -56,7 +58,7 @@ static ALCHEMY_KEYS: Lazy> = Lazy::new(|| { }); // List of etherscan keys for mainnet -static ETHERSCAN_MAINNET_KEYS: Lazy> = Lazy::new(|| { +static ETHERSCAN_MAINNET_KEYS: LazyLock> = LazyLock::new(|| { let mut keys = vec![ "MCAUM7WPE9XP5UQMZPCKIBUJHPM1C24FP6", "JW6RWCG2C5QF8TANH4KC7AYIF1CX7RB5D1", diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 7ed13cf14..5d06a8c69 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -9,7 +9,6 @@ use foundry_compilers::{ ArtifactOutput, ConfigurableArtifacts, PathStyle, ProjectPathsConfig, }; use foundry_config::Config; -use once_cell::sync::Lazy; use parking_lot::Mutex; use regex::Regex; use snapbox::cmd::OutputAssert; @@ -22,27 +21,27 @@ use std::{ process::{ChildStdin, Command, Output, Stdio}, sync::{ atomic::{AtomicUsize, Ordering}, - Arc, + Arc, LazyLock, }, }; -static CURRENT_DIR_LOCK: Lazy> = Lazy::new(|| Mutex::new(())); +static CURRENT_DIR_LOCK: LazyLock> = LazyLock::new(|| Mutex::new(())); /// The commit of forge-std to use. const FORGE_STD_REVISION: &str = include_str!("../../../testdata/forge-std-rev"); /// Stores whether `stdout` is a tty / terminal. -pub static IS_TTY: Lazy = Lazy::new(|| std::io::stdout().is_terminal()); +pub static IS_TTY: LazyLock = LazyLock::new(|| std::io::stdout().is_terminal()); /// Global default template path. Contains the global template project from which all other /// temp projects are initialized. See [`initialize()`] for more info. -static TEMPLATE_PATH: Lazy = - Lazy::new(|| env::temp_dir().join("foundry-forge-test-template")); +static TEMPLATE_PATH: LazyLock = + LazyLock::new(|| env::temp_dir().join("foundry-forge-test-template")); /// Global default template lock. If its contents are not exactly `"1"`, the global template will /// be re-initialized. See [`initialize()`] for more info. -static TEMPLATE_LOCK: Lazy = - Lazy::new(|| env::temp_dir().join("foundry-forge-test-template.lock")); +static TEMPLATE_LOCK: LazyLock = + LazyLock::new(|| env::temp_dir().join("foundry-forge-test-template.lock")); /// Global test identifier. static NEXT_ID: AtomicUsize = AtomicUsize::new(0); @@ -219,7 +218,7 @@ impl ExtTester { /// This doesn't always run `forge init`, instead opting to copy an already-initialized template /// project from a global template path. This is done to speed up tests. /// -/// This used to use a `static` [`Lazy`], but this approach does not with `cargo-nextest` because it +/// This used to use a `static` `Lazy`, but this approach does not with `cargo-nextest` because it /// runs each test in a separate process. Instead, we use a global lock file to ensure that only one /// test can initialize the template at a time. pub fn initialize(target: &Path) { @@ -1077,7 +1076,7 @@ fn test_assert() -> snapbox::Assert { } fn test_redactions() -> snapbox::Redactions { - static REDACTIONS: Lazy = Lazy::new(|| { + static REDACTIONS: LazyLock = LazyLock::new(|| { let mut r = snapbox::Redactions::new(); let redactions = [ ("[SOLC_VERSION]", r"Solc( version)? \d+.\d+.\d+"), @@ -1121,7 +1120,7 @@ pub trait OutputExt { /// Patterns to remove from fixtures before comparing output /// /// This should strip everything that can vary from run to run, like elapsed time, file paths -static IGNORE_IN_FIXTURES: Lazy = Lazy::new(|| { +static IGNORE_IN_FIXTURES: LazyLock = LazyLock::new(|| { let re = &[ // solc version r" ?Solc(?: version)? \d+.\d+.\d+", diff --git a/crates/verify/Cargo.toml b/crates/verify/Cargo.toml index 3ef9b4fb4..4512d7801 100644 --- a/crates/verify/Cargo.toml +++ b/crates/verify/Cargo.toml @@ -37,7 +37,6 @@ async-trait.workspace = true futures.workspace = true semver.workspace = true regex = { version = "1", default-features = false } -once_cell.workspace = true yansi.workspace = true itertools.workspace = true diff --git a/crates/verify/src/etherscan/mod.rs b/crates/verify/src/etherscan/mod.rs index 3109a3d08..9136c200d 100644 --- a/crates/verify/src/etherscan/mod.rs +++ b/crates/verify/src/etherscan/mod.rs @@ -23,17 +23,16 @@ use foundry_compilers::{artifacts::BytecodeObject, Artifact}; use foundry_config::{Chain, Config}; use foundry_evm::constants::DEFAULT_CREATE2_DEPLOYER; use futures::FutureExt; -use once_cell::sync::Lazy; use regex::Regex; use semver::{BuildMetadata, Version}; -use std::fmt::Debug; +use std::{fmt::Debug, sync::LazyLock}; mod flatten; mod standard_json; -pub static RE_BUILD_COMMIT: Lazy = - Lazy::new(|| Regex::new(r"(?Pcommit\.[0-9,a-f]{8})").unwrap()); +pub static RE_BUILD_COMMIT: LazyLock = + LazyLock::new(|| Regex::new(r"(?Pcommit\.[0-9,a-f]{8})").unwrap()); #[derive(Clone, Debug, Default)] #[non_exhaustive] From 93fd555283d4d6da9a4922b57fadad2e3060794c Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 22 Aug 2024 15:41:49 +0200 Subject: [PATCH 100/184] test: unflake cast storage layout test (#8720) --- crates/cast/tests/cli/main.rs | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index fcbeaf422..6ef092cb1 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1,10 +1,11 @@ //! Contains various tests for checking cast commands +use alloy_chains::NamedChain; use alloy_primitives::{address, b256, Address, B256}; use anvil::{Hardfork, NodeConfig}; use foundry_test_utils::{ casttest, - rpc::{next_http_rpc_endpoint, next_ws_rpc_endpoint}, + rpc::{next_http_rpc_endpoint, next_rpc_endpoint, next_ws_rpc_endpoint}, str, util::OutputExt, }; @@ -825,17 +826,20 @@ casttest!(storage, |_prj, cmd| { // casttest!(storage_layout, |_prj, cmd| { - cmd.cast_fuse().args([ - "storage", - "--rpc-url", - "https://mainnet.optimism.io", - "--block", - "110000000", - "--etherscan-api-key", - "JQNGFHINKS1W7Y5FRXU4SPBYF43J3NYK46", - "0xB67c152E69217b5aCB85A2e19dF13423351b0E27", - ]); - let output = r#"| Name | Type | Slot | Offset | Bytes | Value | Hex Value | Contract | + cmd.cast_fuse() + .args([ + "storage", + "--rpc-url", + next_rpc_endpoint(NamedChain::Optimism).as_str(), + "--block", + "110000000", + "--etherscan-api-key", + "JQNGFHINKS1W7Y5FRXU4SPBYF43J3NYK46", + "0xB67c152E69217b5aCB85A2e19dF13423351b0E27", + ]) + .assert_success() + .stdout_eq(str![[r#" +| Name | Type | Slot | Offset | Bytes | Value | Hex Value | Contract | |-------------------------------|-----------------------------------------------------------------|------|--------|-------|---------------------------------------------------|--------------------------------------------------------------------|----------------------------------------------------| | gov | address | 0 | 0 | 20 | 1352965747418285184211909460723571462248744342032 | 0x000000000000000000000000ecfd15165d994c2766fbe0d6bacdc2e8dedfd210 | contracts/perp/PositionManager.sol:PositionManager | | _status | uint256 | 1 | 0 | 32 | 1 | 0x0000000000000000000000000000000000000000000000000000000000000001 | contracts/perp/PositionManager.sol:PositionManager | @@ -863,8 +867,8 @@ casttest!(storage_layout, |_prj, cmd| { | closePositionRequests | mapping(bytes32 => struct PositionManager.ClosePositionRequest) | 20 | 0 | 32 | 0 | 0x0000000000000000000000000000000000000000000000000000000000000000 | contracts/perp/PositionManager.sol:PositionManager | | managers | mapping(address => bool) | 21 | 0 | 32 | 0 | 0x0000000000000000000000000000000000000000000000000000000000000000 | contracts/perp/PositionManager.sol:PositionManager | | approvedManagers | mapping(address => mapping(address => bool)) | 22 | 0 | 32 | 0 | 0x0000000000000000000000000000000000000000000000000000000000000000 | contracts/perp/PositionManager.sol:PositionManager | -"#; - assert_eq!(cmd.stdout_lossy(), output); + +"#]]); }); casttest!(balance, |_prj, cmd| { From 8b4d447b5d55e61784326ef623a3e432da658c04 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 22 Aug 2024 15:43:22 +0200 Subject: [PATCH 101/184] chore: update CODEOWNERS (#8721) --- .github/CODEOWNERS | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4eea49b38..9f19e678f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,10 +1 @@ -* @danipopes @evalir @mattsse - -crates/anvil/ @danipopes @mattsse @evalir -crates/cheatcodes/ @danipopes @mattsse @klkvr @evalir -crates/evm/coverage/ @onbjerg -crates/fmt/ @rkrasiuk -crates/linking/ @klkvr -crates/macros/ @danipopes -crates/script/ @danipopes @mattsse @klkvr -crates/wallets/ @klkvr +* @danipopes @klkvr @mattsse From 5c52be617e0d7578f2a68f56b54ac495def22cb9 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 22 Aug 2024 15:49:21 +0200 Subject: [PATCH 102/184] chore(deps): breaking bumps (#8719) --- Cargo.lock | 145 +++++++++++++-------------- Cargo.toml | 4 +- crates/chisel/Cargo.toml | 2 +- crates/chisel/src/solidity_helper.rs | 2 +- crates/debugger/Cargo.toml | 6 +- crates/debugger/src/tui/draw.rs | 60 +++++------ crates/doc/Cargo.toml | 2 +- crates/evm/abi/src/console/mod.rs | 44 ++++---- crates/forge/Cargo.toml | 2 +- crates/forge/bin/cmd/clone.rs | 4 +- 10 files changed, 132 insertions(+), 139 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 132c4840d..66b4357a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2115,13 +2115,11 @@ dependencies = [ [[package]] name = "clipboard-win" -version = "4.5.0" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" +checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" dependencies = [ "error-code", - "str-buf", - "winapi", ] [[package]] @@ -2237,7 +2235,7 @@ version = "7.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7" dependencies = [ - "crossterm", + "crossterm 0.27.0", "strum", "strum_macros", "unicode-width", @@ -2245,13 +2243,14 @@ dependencies = [ [[package]] name = "compact_str" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +checksum = "6050c3a16ddab2e412160b31f2c871015704239bca62f72f6e5f0be631d3f644" dependencies = [ "castaway", "cfg-if", "itoa", + "rustversion", "ryu", "static_assertions", ] @@ -2319,6 +2318,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -2434,8 +2442,21 @@ dependencies = [ "bitflags 2.6.0", "crossterm_winapi", "libc", - "mio 0.8.11", "parking_lot", + "winapi", +] + +[[package]] +name = "crossterm" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" +dependencies = [ + "bitflags 2.6.0", + "crossterm_winapi", + "mio 1.0.2", + "parking_lot", + "rustix", "signal-hook", "signal-hook-mio", "winapi", @@ -2642,7 +2663,7 @@ version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ - "convert_case", + "convert_case 0.4.0", "proc-macro2", "quote", "rustc_version 0.4.0", @@ -2664,9 +2685,11 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ + "convert_case 0.6.0", "proc-macro2", "quote", "syn 2.0.75", + "unicode-xid", ] [[package]] @@ -2944,13 +2967,9 @@ dependencies = [ [[package]] name = "error-code" -version = "2.3.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21" -dependencies = [ - "libc", - "str-buf", -] +checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" [[package]] name = "eth-keystore" @@ -3148,17 +3167,6 @@ dependencies = [ "bytes", ] -[[package]] -name = "fd-lock" -version = "3.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" -dependencies = [ - "cfg-if", - "rustix", - "windows-sys 0.48.0", -] - [[package]] name = "fd-lock" version = "4.0.2" @@ -3364,7 +3372,7 @@ version = "0.2.0" dependencies = [ "alloy-primitives", "auto_impl", - "derive_more 0.99.18", + "derive_more 1.0.0", "eyre", "forge-fmt", "foundry-compilers", @@ -3824,7 +3832,7 @@ name = "foundry-debugger" version = "0.2.0" dependencies = [ "alloy-primitives", - "crossterm", + "crossterm 0.28.1", "eyre", "foundry-common", "foundry-compilers", @@ -3868,7 +3876,7 @@ version = "0.2.0" dependencies = [ "alloy-primitives", "alloy-sol-types", - "derive_more 0.99.18", + "derive_more 1.0.0", "foundry-common-fmt", "foundry-macros", "foundry-test-utils", @@ -4031,7 +4039,7 @@ dependencies = [ "alloy-primitives", "alloy-provider", "eyre", - "fd-lock 4.0.2", + "fd-lock", "foundry-common", "foundry-compilers", "foundry-config", @@ -5104,6 +5112,16 @@ dependencies = [ "generic-array", ] +[[package]] +name = "instability" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c" +dependencies = [ + "quote", + "syn 2.0.75", +] + [[package]] name = "instant" version = "0.1.13" @@ -5169,15 +5187,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.13.0" @@ -5616,20 +5625,20 @@ checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi 0.3.9", "libc", + "log", "wasi", "windows-sys 0.52.0", ] [[package]] name = "mockall" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43766c2b5203b10de348ffe19f7e54564b64f3d6018ff7648d1e2d6d3a0f0a48" +checksum = "d4c28b3fb6d753d28c20e826cd46ee611fda1cf3cde03a443a974043247c065a" dependencies = [ "cfg-if", "downcast", "fragile", - "lazy_static", "mockall_derive", "predicates", "predicates-tree", @@ -5637,9 +5646,9 @@ dependencies = [ [[package]] name = "mockall_derive" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cbce79ec385a1d4f54baa90a76401eb15d9cab93685f62e7e9f942aa00ae2" +checksum = "341014e7f530314e9a1fdbc7400b244efea7122662c96bfa248c31da5bfb2020" dependencies = [ "cfg-if", "proc-macro2", @@ -6854,19 +6863,20 @@ dependencies = [ [[package]] name = "ratatui" -version = "0.26.3" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f44c9e68fd46eda15c646fbb85e1040b657a58cdc8c98db1d97a55930d991eef" +checksum = "5ba6a365afbe5615999275bea2446b970b10a41102500e27ce7678d50d978303" dependencies = [ "bitflags 2.6.0", "cassowary", "compact_str", - "crossterm", - "itertools 0.12.1", + "crossterm 0.28.1", + "instability", + "itertools 0.13.0", "lru", "paste", - "stability", "strum", + "strum_macros", "unicode-segmentation", "unicode-truncate", "unicode-width", @@ -7385,25 +7395,24 @@ dependencies = [ [[package]] name = "rustyline" -version = "12.0.0" +version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "994eca4bca05c87e86e15d90fc7a91d1be64b4482b38cb2d27474568fe7c9db9" +checksum = "7803e8936da37efd9b6d4478277f4b2b9bb5cdb37a113e8d63222e58da647e63" dependencies = [ "bitflags 2.6.0", "cfg-if", "clipboard-win", - "fd-lock 3.0.13", + "fd-lock", "home", "libc", "log", "memchr", - "nix 0.26.4", + "nix 0.28.0", "radix_trie", - "scopeguard", "unicode-segmentation", "unicode-width", "utf8parse", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -7871,7 +7880,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" dependencies = [ "libc", - "mio 0.8.11", + "mio 1.0.2", "signal-hook", ] @@ -8057,28 +8066,12 @@ dependencies = [ "der", ] -[[package]] -name = "stability" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac" -dependencies = [ - "quote", - "syn 2.0.75", -] - [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "str-buf" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" - [[package]] name = "string_cache" version = "0.8.7" @@ -8342,9 +8335,9 @@ dependencies = [ [[package]] name = "tikv-jemalloc-sys" -version = "0.5.4+5.3.0-patched" +version = "0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9402443cb8fd499b6f327e40565234ff34dbda27460c5b47db0db77443dd85d1" +checksum = "cd3c60906412afa9c2b5b5a48ca6a5abe5736aec9eb48ad05037a677e52e4e2d" dependencies = [ "cc", "libc", @@ -8352,9 +8345,9 @@ dependencies = [ [[package]] name = "tikv-jemallocator" -version = "0.5.4" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "965fe0c26be5c56c94e38ba547249074803efd52adfb66de62107d95aab3eaca" +checksum = "4cec5ff18518d81584f477e9bfdf957f5bb0979b0bac3af4ca30b5b3ae2d2865" dependencies = [ "libc", "tikv-jemalloc-sys", diff --git a/Cargo.toml b/Cargo.toml index 8563d1add..5ac49d2df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -221,7 +221,7 @@ chrono = { version = "0.4", default-features = false, features = [ "std", ] } color-eyre = "0.6" -derive_more = "0.99" +derive_more = { version = "1.0", features = ["full"] } dunce = "1" evm-disassembler = "0.5" eyre = "0.6" @@ -244,7 +244,7 @@ tracing = "0.1" tracing-subscriber = "0.3" vergen = { version = "8", default-features = false } indexmap = "2.2" -tikv-jemallocator = "0.5.4" +tikv-jemallocator = "0.6" url = "2" num-format = "0.4.4" yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } diff --git a/crates/chisel/Cargo.toml b/crates/chisel/Cargo.toml index bbfeb6ee9..5d8ad4d2c 100644 --- a/crates/chisel/Cargo.toml +++ b/crates/chisel/Cargo.toml @@ -53,7 +53,7 @@ eyre.workspace = true regex = "1" reqwest.workspace = true revm.workspace = true -rustyline = "12" +rustyline = "14" semver.workspace = true serde_json.workspace = true serde.workspace = true diff --git a/crates/chisel/src/solidity_helper.rs b/crates/chisel/src/solidity_helper.rs index 84140c820..4707ab37b 100644 --- a/crates/chisel/src/solidity_helper.rs +++ b/crates/chisel/src/solidity_helper.rs @@ -211,7 +211,7 @@ impl Highlighter for SolidityHelper { Self::highlight(line) } - fn highlight_char(&self, line: &str, pos: usize) -> bool { + fn highlight_char(&self, line: &str, pos: usize, _forced: bool) -> bool { pos == line.len() } diff --git a/crates/debugger/Cargo.toml b/crates/debugger/Cargo.toml index 9f96eb7f0..6ccb630ca 100644 --- a/crates/debugger/Cargo.toml +++ b/crates/debugger/Cargo.toml @@ -20,9 +20,11 @@ revm-inspectors.workspace = true alloy-primitives.workspace = true -crossterm = "0.27" +crossterm = "0.28" eyre.workspace = true -ratatui = { version = "0.26", default-features = false, features = ["crossterm"] } +ratatui = { version = "0.28", default-features = false, features = [ + "crossterm", +] } revm.workspace = true tracing.workspace = true serde.workspace = true diff --git a/crates/debugger/src/tui/draw.rs b/crates/debugger/src/tui/draw.rs index 509b986fd..0f2399a20 100644 --- a/crates/debugger/src/tui/draw.rs +++ b/crates/debugger/src/tui/draw.rs @@ -8,9 +8,9 @@ use foundry_evm_traces::debug::SourceData; use ratatui::{ layout::{Alignment, Constraint, Direction, Layout, Rect}, style::{Color, Modifier, Style}, - terminal::Frame, text::{Line, Span, Text}, widgets::{Block, Borders, List, ListItem, ListState, Paragraph, Wrap}, + Frame, }; use revm::interpreter::opcode; use revm_inspectors::tracing::types::CallKind; @@ -25,17 +25,17 @@ impl DebuggerContext<'_> { #[inline] fn draw_layout(&self, f: &mut Frame<'_>) { // We need 100 columns to display a 32 byte word in the memory and stack panes. - let size = f.size(); + let area = f.area(); let min_width = 100; let min_height = 16; - if size.width < min_width || size.height < min_height { + if area.width < min_width || area.height < min_height { self.size_too_small(f, min_width, min_height); return; } // The horizontal layout draws these panes at 50% width. let min_column_width_for_horizontal = 200; - if size.width >= min_column_width_for_horizontal { + if area.width >= min_column_width_for_horizontal { self.horizontal_layout(f); } else { self.vertical_layout(f); @@ -48,14 +48,14 @@ impl DebuggerContext<'_> { let l1 = "Terminal size too small:"; lines.push(Line::from(l1)); - let size = f.size(); - let width_color = if size.width >= min_width { Color::Green } else { Color::Red }; - let height_color = if size.height >= min_height { Color::Green } else { Color::Red }; + let area = f.area(); + let width_color = if area.width >= min_width { Color::Green } else { Color::Red }; + let height_color = if area.height >= min_height { Color::Green } else { Color::Red }; let l2 = vec![ Span::raw("Width = "), - Span::styled(size.width.to_string(), Style::new().fg(width_color)), + Span::styled(area.width.to_string(), Style::new().fg(width_color)), Span::raw(" Height = "), - Span::styled(size.height.to_string(), Style::new().fg(height_color)), + Span::styled(area.height.to_string(), Style::new().fg(height_color)), ]; lines.push(Line::from(l2)); @@ -66,7 +66,7 @@ impl DebuggerContext<'_> { let paragraph = Paragraph::new(lines).alignment(Alignment::Center).wrap(Wrap { trim: true }); - f.render_widget(paragraph, size) + f.render_widget(paragraph, area) } /// Draws the layout in vertical mode. @@ -85,7 +85,7 @@ impl DebuggerContext<'_> { /// |-----------------------------| /// ``` fn vertical_layout(&self, f: &mut Frame<'_>) { - let area = f.size(); + let area = f.area(); let h_height = if self.show_shortcuts { 4 } else { 0 }; // NOTE: `Layout::split` always returns a slice of the same length as the number of @@ -135,7 +135,7 @@ impl DebuggerContext<'_> { /// |-----------------|-----------| /// ``` fn horizontal_layout(&self, f: &mut Frame<'_>) { - let area = f.size(); + let area = f.area(); let h_height = if self.show_shortcuts { 4 } else { 0 }; // Split off footer. @@ -465,7 +465,7 @@ impl DebuggerContext<'_> { // Color memory region based on read/write. let mut offset = None; - let mut size = None; + let mut len = None; let mut write_offset = None; let mut write_size = None; let mut color = None; @@ -475,13 +475,13 @@ impl DebuggerContext<'_> { if let Some(accesses) = get_buffer_accesses(step.op.get(), stack) { if let Some(read_access) = accesses.read { offset = Some(read_access.1.offset); - size = Some(read_access.1.size); + len = Some(read_access.1.len); color = Some(Color::Cyan); } if let Some(write_access) = accesses.write { if self.active_buffer == BufferKind::Memory { write_offset = Some(write_access.offset); - write_size = Some(write_access.size); + write_size = Some(write_access.len); } } } @@ -501,7 +501,7 @@ impl DebuggerContext<'_> { { if self.active_buffer == BufferKind::Memory { offset = Some(write_access.offset); - size = Some(write_access.size); + len = Some(write_access.len); color = Some(Color::Green); } } @@ -530,10 +530,10 @@ impl DebuggerContext<'_> { let mut byte_color = Color::White; let mut end = None; let idx = i * 32 + j; - if let (Some(offset), Some(size), Some(color)) = (offset, size, color) { - end = Some(offset + size); - if (offset..offset + size).contains(&idx) { - // [offset, offset + size] is the memory region to be colored. + if let (Some(offset), Some(len), Some(color)) = (offset, len, color) { + end = Some(offset + len); + if (offset..offset + len).contains(&idx) { + // [offset, offset + len] is the memory region to be colored. // If a byte at row i and column j in the memory panel // falls in this region, set the color. byte_color = color; @@ -627,7 +627,7 @@ impl<'a> SourceLines<'a> { /// Container for buffer access information. struct BufferAccess { offset: usize, - size: usize, + len: usize, } /// Container for read and write buffer access information. @@ -639,15 +639,15 @@ struct BufferAccesses { } /// The memory_access variable stores the index on the stack that indicates the buffer -/// offset/size accessed by the given opcode: -/// (read buffer, buffer read offset, buffer read size, write memory offset, write memory size) +/// offset/len accessed by the given opcode: +/// (read buffer, buffer read offset, buffer read len, write memory offset, write memory len) /// \>= 1: the stack index /// 0: no memory access -/// -1: a fixed size of 32 bytes -/// -2: a fixed size of 1 byte +/// -1: a fixed len of 32 bytes +/// -2: a fixed len of 1 byte /// /// The return value is a tuple about accessed buffer region by the given opcode: -/// (read buffer, buffer read offset, buffer read size, write memory offset, write memory size) +/// (read buffer, buffer read offset, buffer read len, write memory offset, write memory len) fn get_buffer_accesses(op: u8, stack: &[U256]) -> Option { let buffer_access = match op { opcode::KECCAK256 | opcode::RETURN | opcode::REVERT => { @@ -696,12 +696,12 @@ fn get_buffer_accesses(op: u8, stack: &[U256]) -> Option { if buffer_access.0.is_some() || buffer_access.1.is_some() { let (read, write) = buffer_access; let read_access = read.and_then(|b| { - let (buffer, offset, size) = b; - Some((buffer, BufferAccess { offset: get_size(offset)?, size: get_size(size)? })) + let (buffer, offset, len) = b; + Some((buffer, BufferAccess { offset: get_size(offset)?, len: get_size(len)? })) }); let write_access = write.and_then(|b| { - let (offset, size) = b; - Some(BufferAccess { offset: get_size(offset)?, size: get_size(size)? }) + let (offset, len) = b; + Some(BufferAccess { offset: get_size(offset)?, len: get_size(len)? }) }); Some(BufferAccesses { read: read_access, write: write_access }) } else { diff --git a/crates/doc/Cargo.toml b/crates/doc/Cargo.toml index 22abae5be..0dcd40747 100644 --- a/crates/doc/Cargo.toml +++ b/crates/doc/Cargo.toml @@ -21,7 +21,7 @@ foundry-config.workspace = true alloy-primitives.workspace = true auto_impl.workspace = true -derive_more = "0.99" +derive_more.workspace = true eyre.workspace = true itertools.workspace = true mdbook = { version = "0.4", default-features = false, features = ["search"] } diff --git a/crates/evm/abi/src/console/mod.rs b/crates/evm/abi/src/console/mod.rs index 3f10c769e..9ce96e4ce 100644 --- a/crates/evm/abi/src/console/mod.rs +++ b/crates/evm/abi/src/console/mod.rs @@ -12,70 +12,70 @@ sol! { #[sol(abi)] #[derive(Display)] interface Console { - #[display(fmt = "{val}")] + #[display("{val}")] event log(string val); - #[display(fmt = "{}", "hex::encode_prefixed(val)")] + #[display("{}", hex::encode_prefixed(val))] event logs(bytes val); - #[display(fmt = "{val}")] + #[display("{val}")] event log_address(address val); - #[display(fmt = "{val}")] + #[display("{val}")] event log_bytes32(bytes32 val); - #[display(fmt = "{val}")] + #[display("{val}")] event log_int(int val); - #[display(fmt = "{val}")] + #[display("{val}")] event log_uint(uint val); - #[display(fmt = "{}", "hex::encode_prefixed(val)")] + #[display("{}", hex::encode_prefixed(val))] event log_bytes(bytes val); - #[display(fmt = "{val}")] + #[display("{val}")] event log_string(string val); - #[display(fmt = "[{}]", "val.iter().format(\", \")")] + #[display("[{}]", val.iter().format(", "))] event log_array(uint256[] val); - #[display(fmt = "[{}]", "val.iter().format(\", \")")] + #[display("[{}]", val.iter().format(", "))] event log_array(int256[] val); - #[display(fmt = "[{}]", "val.iter().format(\", \")")] + #[display("[{}]", val.iter().format(", "))] event log_array(address[] val); - #[display(fmt = "{key}: {val}")] + #[display("{key}: {val}")] event log_named_address(string key, address val); - #[display(fmt = "{key}: {val}")] + #[display("{key}: {val}")] event log_named_bytes32(string key, bytes32 val); - #[display(fmt = "{key}: {}", "format_units_int(val, decimals)")] + #[display("{key}: {}", format_units_int(val, decimals))] event log_named_decimal_int(string key, int val, uint decimals); - #[display(fmt = "{key}: {}", "format_units_uint(val, decimals)")] + #[display("{key}: {}", format_units_uint(val, decimals))] event log_named_decimal_uint(string key, uint val, uint decimals); - #[display(fmt = "{key}: {val}")] + #[display("{key}: {val}")] event log_named_int(string key, int val); - #[display(fmt = "{key}: {val}")] + #[display("{key}: {val}")] event log_named_uint(string key, uint val); - #[display(fmt = "{key}: {}", "hex::encode_prefixed(val)")] + #[display("{key}: {}", hex::encode_prefixed(val))] event log_named_bytes(string key, bytes val); - #[display(fmt = "{key}: {val}")] + #[display("{key}: {val}")] event log_named_string(string key, string val); - #[display(fmt = "{key}: [{}]", "val.iter().format(\", \")")] + #[display("{key}: [{}]", val.iter().format(", "))] event log_named_array(string key, uint256[] val); - #[display(fmt = "{key}: [{}]", "val.iter().format(\", \")")] + #[display("{key}: [{}]", val.iter().format(", "))] event log_named_array(string key, int256[] val); - #[display(fmt = "{key}: [{}]", "val.iter().format(\", \")")] + #[display("{key}: [{}]", val.iter().format(", "))] event log_named_array(string key, address[] val); } } diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index a4fce5cc3..4f9110ff6 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -114,7 +114,7 @@ tikv-jemallocator = { workspace = true, optional = true } anvil.workspace = true foundry-test-utils.workspace = true -mockall = "0.12" +mockall = "0.13" criterion = "0.5" globset = "0.4" paste = "1.0" diff --git a/crates/forge/bin/cmd/clone.rs b/crates/forge/bin/cmd/clone.rs index f2c07294a..65b3cf01d 100644 --- a/crates/forge/bin/cmd/clone.rs +++ b/crates/forge/bin/cmd/clone.rs @@ -575,11 +575,9 @@ pub fn find_main_contract<'a>( rv.ok_or_else(|| eyre::eyre!("contract not found")) } -#[cfg(test)] -use mockall::automock; /// EtherscanClient is a trait that defines the methods to interact with Etherscan. /// It is defined as a wrapper of the `foundry_block_explorers::Client` to allow mocking. -#[cfg_attr(test, automock)] +#[cfg_attr(test, mockall::automock)] pub(crate) trait EtherscanClient { async fn contract_source_code( &self, From 70ef94a90f4d2adb5dd932b417e7cf37410cebb3 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 22 Aug 2024 17:07:17 +0300 Subject: [PATCH 103/184] fix(forge): reset gas to original after pauseGasMetering (#8717) --- crates/cheatcodes/src/inspector.rs | 8 ++++- crates/forge/tests/cli/test_cmd.rs | 48 ++++++++++++++++++++++++++++++ crates/test-utils/src/util.rs | 16 +++++++++- 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 02c97ac27..65dabcf2d 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -1354,7 +1354,13 @@ impl Cheatcodes { // inside it. self.gas_metering_create = Some(None) } - opcode::STOP | opcode::RETURN | opcode::SELFDESTRUCT | opcode::REVERT => { + opcode::STOP => { + // Reset gas to value recorded when paused. + interpreter.gas = *gas; + self.gas_metering = None; + self.gas_metering_create = None; + } + opcode::RETURN | opcode::SELFDESTRUCT | opcode::REVERT => { match &self.gas_metering_create { None | Some(None) => { // If we are ending current execution frame, we want to reset diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 40890c9db..3b0cbfb7f 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -1238,3 +1238,51 @@ contract DeterministicRandomnessTest is Test { assert_ne!(res4, res1); assert_ne!(res4, res3); }); + +// tests that `pauseGasMetering` used at the end of test does not produce meaningless values +// see https://github.com/foundry-rs/foundry/issues/5491 +forgetest_init!(repro_5491, |prj, cmd| { + prj.wipe_contracts(); + + prj.add_test( + "ATest.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract ATest is Test { + function testWeirdGas1() public { + vm.pauseGasMetering(); + } + + function testWeirdGas2() public { + uint256 a = 1; + uint256 b = a + 1; + require(b == 2, "b is not 2"); + vm.pauseGasMetering(); + } + + function testNormalGas() public { + vm.pauseGasMetering(); + vm.resumeGasMetering(); + } + + function testWithAssembly() public { + vm.pauseGasMetering(); + assembly { + return(0, 0) + } + } +} + "#, + ) + .unwrap(); + + cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" +... +[PASS] testNormalGas() (gas: 3202) +[PASS] testWeirdGas1() (gas: 3040) +[PASS] testWeirdGas2() (gas: 3148) +[PASS] testWithAssembly() (gas: 3083) +... +"#]]); +}); diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 5d06a8c69..593dbdbe1 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -624,6 +624,7 @@ impl TestProject { current_dir_lock: None, saved_cwd: pretty_err("", std::env::current_dir()), stdin_fun: None, + redact_output: true, } } @@ -638,6 +639,7 @@ impl TestProject { current_dir_lock: None, saved_cwd: pretty_err("", std::env::current_dir()), stdin_fun: None, + redact_output: true, } } @@ -735,6 +737,8 @@ pub struct TestCommand { // initial: Command, current_dir_lock: Option>, stdin_fun: Option>, + /// If true, command output is redacted. + redact_output: bool, } impl TestCommand { @@ -825,6 +829,12 @@ impl TestCommand { self } + /// Does not apply [`snapbox`] redactions to the command output. + pub fn with_no_redact(&mut self) -> &mut Self { + self.redact_output = false; + self + } + /// Returns the `Config` as spit out by `forge config` #[track_caller] pub fn config(&mut self) -> Config { @@ -1053,7 +1063,11 @@ stderr: /// Runs the command, returning a [`snapbox`] object to assert the command output. #[track_caller] pub fn assert(&mut self) -> OutputAssert { - OutputAssert::new(self.execute()).with_assert(test_assert()) + let assert = OutputAssert::new(self.execute()); + if self.redact_output { + return assert.with_assert(test_assert()); + }; + assert } /// Runs the command and asserts that it resulted in success. From 1dad817e099e4bb0a672e937b1c8d559702f5112 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 23 Aug 2024 13:53:42 +0800 Subject: [PATCH 104/184] feat(cast): get create2 address (#8724) * feat(cast): get create2 address * add additional documentation for `salt` --- crates/cast/bin/cmd/create2.rs | 59 ++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/crates/cast/bin/cmd/create2.rs b/crates/cast/bin/cmd/create2.rs index 0f751fc89..ebf2b3eca 100644 --- a/crates/cast/bin/cmd/create2.rs +++ b/crates/cast/bin/cmd/create2.rs @@ -22,7 +22,7 @@ pub struct Create2Args { #[arg( long, short, - required_unless_present_any = &["ends_with", "matching"], + required_unless_present_any = &["ends_with", "matching", "salt"], value_name = "HEX" )] starts_with: Option, @@ -48,6 +48,23 @@ pub struct Create2Args { )] deployer: Address, + /// Salt to be used for the contract deployment. This option separate from the default salt + /// mining with filters. + #[arg( + long, + conflicts_with_all = [ + "starts_with", + "ends_with", + "matching", + "case_sensitive", + "caller", + "seed", + "no_random" + ], + value_name = "HEX" + )] + salt: Option, + /// Init code of the contract to be deployed. #[arg(short, long, value_name = "HEX")] init_code: Option, @@ -87,6 +104,7 @@ impl Create2Args { matching, case_sensitive, deployer, + salt, init_code, init_code_hash, jobs, @@ -95,6 +113,21 @@ impl Create2Args { no_random, } = self; + let init_code_hash = if let Some(init_code_hash) = init_code_hash { + hex::FromHex::from_hex(init_code_hash) + } else if let Some(init_code) = init_code { + hex::decode(init_code).map(keccak256) + } else { + unreachable!(); + }?; + + if let Some(salt) = salt { + let salt = hex::FromHex::from_hex(salt)?; + let address = deployer.create2(salt, init_code_hash); + println!("{address}"); + return Ok(Create2Output { address, salt }); + } + let mut regexs = vec![]; if let Some(matches) = matching { @@ -134,14 +167,6 @@ impl Create2Args { let regex = RegexSetBuilder::new(regexs).case_insensitive(!case_sensitive).build()?; - let init_code_hash = if let Some(init_code_hash) = init_code_hash { - hex::FromHex::from_hex(init_code_hash) - } else if let Some(init_code) = init_code { - hex::decode(init_code).map(keccak256) - } else { - unreachable!(); - }?; - let mut n_threads = std::thread::available_parallelism().map_or(1, |n| n.get()); if let Some(jobs) = jobs { n_threads = n_threads.min(jobs.get()); @@ -302,6 +327,22 @@ mod tests { assert!(format!("{address:x}").starts_with("bb")); } + #[test] + fn create2_salt() { + let args = Create2Args::parse_from([ + "foundry-cli", + "--deployer=0x8ba1f109551bD432803012645Ac136ddd64DBA72", + "--salt=0x7c5ea36004851c764c44143b1dcb59679b11c9a68e5f41497f6cf3d480715331", + "--init-code=0x6394198df16000526103ff60206004601c335afa6040516060f3", + ]); + let create2_out = args.run().unwrap(); + let address = create2_out.address; + assert_eq!( + address, + Address::from_str("0x533AE9D683B10C02EBDB05471642F85230071FC3").unwrap() + ); + } + #[test] fn create2_init_code() { let init_code = "00"; From 2b1f8d6dd90f9790faf0528e05e60e573a7569ce Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2024 05:36:38 +0000 Subject: [PATCH 105/184] chore(deps): weekly `cargo update` (#8737) --- Cargo.lock | 301 +++++++++++++++++++++++++++++------------------------ 1 file changed, 166 insertions(+), 135 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 66b4357a4..daa1a091a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,6 +27,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "aes" version = "0.8.4" @@ -316,7 +322,7 @@ checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -551,7 +557,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -568,7 +574,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", "syn-solidity", "tiny-keccak", ] @@ -586,7 +592,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.75", + "syn 2.0.76", "syn-solidity", ] @@ -1094,7 +1100,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -1116,7 +1122,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -1127,7 +1133,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -1180,7 +1186,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -1575,7 +1581,7 @@ dependencies = [ "cc", "cfg-if", "libc", - "miniz_oxide", + "miniz_oxide 0.7.4", "object", "rustc-demangle", ] @@ -1811,9 +1817,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3054fea8a20d8ff3968d5b22cc27501d2b08dc4decdb31b184323f00c5ef23bb" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" dependencies = [ "serde", ] @@ -1923,9 +1929,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.13" +version = "1.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" +checksum = "50d2eb3cd3d1bf4529e31c215ee6f93ec5a3d536d9f578f93d9d33ee19562932" dependencies = [ "jobserver", "libc", @@ -2065,9 +2071,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.18" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee158892bd7ce77aa15c208abbdb73e155d191c287a659b57abd5adb92feb03" +checksum = "531d7959c5bbb6e266cecdd0f20213639c3a5c3e4d615f97db87661745f781ff" dependencies = [ "clap", ] @@ -2091,7 +2097,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -2539,7 +2545,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -2550,7 +2556,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -2623,7 +2629,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -2644,7 +2650,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -2654,7 +2660,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -2667,7 +2673,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.0", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -2688,7 +2694,7 @@ dependencies = [ "convert_case 0.6.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", "unicode-xid", ] @@ -2796,7 +2802,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -2923,7 +2929,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -3069,7 +3075,7 @@ dependencies = [ "regex", "serde", "serde_json", - "syn 2.0.75", + "syn 2.0.76", "toml 0.8.19", "walkdir", ] @@ -3097,7 +3103,7 @@ dependencies = [ "serde", "serde_json", "strum", - "syn 2.0.75", + "syn 2.0.76", "tempfile", "thiserror", "tiny-keccak", @@ -3127,9 +3133,9 @@ dependencies = [ [[package]] name = "evmole" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628b1881ed2b7f83a32418f6de735c49292065dfc225067687ee1d4fc59a134e" +checksum = "0ffb1f458e6901be6a6aaa485ce3a5d81478644edde1ffbe95da114ad9c94467" dependencies = [ "ruint", ] @@ -3152,9 +3158,9 @@ checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183" [[package]] name = "fastrand" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "fastrlp" @@ -3248,12 +3254,12 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.31" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920" +checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.8.0", ] [[package]] @@ -3462,7 +3468,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -4029,7 +4035,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -4093,9 +4099,9 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "fs4" -version = "0.8.4" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e180ac76c23b45e767bd7ae9579bc0bb458618c4bc71835926e098e61d15f8" +checksum = "e8c6b3bd49c37d2aa3f3f2220233b29a7cd23f79d1fe70e5337d25fb390793de" dependencies = [ "rustix", "windows-sys 0.52.0", @@ -4188,7 +4194,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -4335,9 +4341,9 @@ dependencies = [ [[package]] name = "gix-config-value" -version = "0.14.7" +version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b328997d74dd15dc71b2773b162cb4af9a25c424105e4876e6d0686ab41c383e" +checksum = "03f76169faa0dec598eac60f83d7fcdd739ec16596eca8fb144c88973dbe6f8c" dependencies = [ "bitflags 2.6.0", "bstr", @@ -4385,9 +4391,9 @@ dependencies = [ [[package]] name = "gix-glob" -version = "0.16.4" +version = "0.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7df15afa265cc8abe92813cd354d522f1ac06b29ec6dfa163ad320575cb447" +checksum = "74908b4bbc0a0a40852737e5d7889f676f081e340d5451a16e5b4c50d592f111" dependencies = [ "bitflags 2.6.0", "bstr", @@ -4437,9 +4443,9 @@ dependencies = [ [[package]] name = "gix-path" -version = "0.10.9" +version = "0.10.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d23d5bbda31344d8abc8de7c075b3cf26e5873feba7c4a15d916bce67382bd9" +checksum = "38d5b8722112fa2fa87135298780bc833b0e9f6c56cc82795d209804b3a03484" dependencies = [ "bstr", "gix-trace", @@ -4472,9 +4478,9 @@ dependencies = [ [[package]] name = "gix-sec" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1547d26fa5693a7f34f05b4a3b59a90890972922172653bcb891ab3f09f436df" +checksum = "0fe4d52f30a737bbece5276fab5d3a8b276dc2650df963e293d0673be34e7a5f" dependencies = [ "bitflags 2.6.0", "gix-path", @@ -4572,9 +4578,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" dependencies = [ "atomic-waker", "bytes", @@ -4704,7 +4710,7 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -4830,7 +4836,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.5", + "h2 0.4.6", "http 1.1.0", "http-body 1.0.1", "httparse", @@ -4869,7 +4875,7 @@ dependencies = [ "hyper 1.4.1", "hyper-util", "rustls 0.23.12", - "rustls-native-certs 0.7.1", + "rustls-native-certs 0.7.2", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -5119,7 +5125,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c" dependencies = [ "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -5355,9 +5361,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.156" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libdbus-sys" @@ -5571,7 +5577,7 @@ checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -5605,6 +5611,15 @@ dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + [[package]] name = "mio" version = "0.8.11" @@ -5653,7 +5668,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -5903,7 +5918,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -6015,7 +6030,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -6201,7 +6216,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -6260,7 +6275,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -6344,7 +6359,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -6402,7 +6417,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -6516,12 +6531,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +checksum = "a909e6e8053fa1a5ad670f5816c7d93029ee1fa8898718490544a6b0d5d38b3e" dependencies = [ "proc-macro2", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -6597,7 +6612,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", "version_check", "yansi", ] @@ -6684,7 +6699,7 @@ dependencies = [ "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -6799,9 +6814,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -6919,9 +6934,9 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", @@ -6980,9 +6995,9 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reqwest" -version = "0.12.5" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" dependencies = [ "async-compression", "base64 0.22.1", @@ -7008,7 +7023,7 @@ dependencies = [ "pin-project-lite", "quinn", "rustls 0.23.12", - "rustls-native-certs 0.7.1", + "rustls-native-certs 0.7.2", "rustls-pemfile 2.1.3", "rustls-pki-types", "serde", @@ -7018,6 +7033,7 @@ dependencies = [ "tokio", "tokio-native-tls", "tokio-rustls 0.26.0", + "tokio-socks", "tokio-util", "tower-service", "url", @@ -7026,7 +7042,7 @@ dependencies = [ "wasm-streams", "web-sys", "webpki-roots", - "winreg", + "windows-registry", ] [[package]] @@ -7045,9 +7061,9 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43cbb1576a147317c6990cf69d6cd5ef402df99f638616ba911006e9ec45866b" +checksum = "ec16f9b9d3cdaaf2f4b7ceaf004eb2c89df04e7ea29622584c0a6ec676bd0a83" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -7318,9 +7334,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88d6d420651b496bdd98684116959239430022a115c1240e6c3993be0b15fba" +checksum = "04182dffc9091a404e0fc069ea5cd60e5b866c3adf881eff99a32d048242dffa" dependencies = [ "openssl-probe", "rustls-pemfile 2.1.3", @@ -7502,7 +7518,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -7649,22 +7665,22 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.208" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.208" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -7675,14 +7691,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] name = "serde_json" -version = "1.0.125" +version = "1.0.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" +checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" dependencies = [ "indexmap 2.4.0", "itoa", @@ -7719,7 +7735,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -7778,7 +7794,7 @@ checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -8046,7 +8062,7 @@ dependencies = [ "walkdir", "yansi", "yash-fnmatch", - "zip 2.1.6", + "zip 2.2.0", "zip-extract", ] @@ -8123,7 +8139,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -8147,14 +8163,13 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "svm-rs" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af5910befd515534a92e9424f250d952fe6f6dba6a92bd001dfeba1fb4a2f87c" +checksum = "7b638fb4b6a74ef632a18935d240596b2618c8eb5c888e9cbf3c689af9a4aded" dependencies = [ "const-hex", "dirs 5.0.1", "fs4", - "once_cell", "reqwest", "semver 1.0.23", "serde", @@ -8162,14 +8177,14 @@ dependencies = [ "sha2", "thiserror", "url", - "zip 2.1.6", + "zip 2.2.0", ] [[package]] name = "svm-rs-builds" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d5ea000fdbeab0b2739315f9093c75ea63030e5c44f92daa72401d11b48adda" +checksum = "813b21b9858cc493134b899c96e73c60f2e6043f050a17020e98ad8c2d9c9912" dependencies = [ "build_const", "const-hex", @@ -8191,9 +8206,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.75" +version = "2.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" +checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" dependencies = [ "proc-macro2", "quote", @@ -8209,7 +8224,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -8223,6 +8238,9 @@ name = "sync_wrapper" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] [[package]] name = "tap" @@ -8311,7 +8329,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -8446,7 +8464,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -8480,6 +8498,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-socks" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" +dependencies = [ + "either", + "futures-util", + "thiserror", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.15" @@ -8599,7 +8629,7 @@ dependencies = [ "axum", "base64 0.22.1", "bytes", - "h2 0.4.5", + "h2 0.4.6", "http 1.1.0", "http-body 1.0.1", "http-body-util", @@ -8609,7 +8639,7 @@ dependencies = [ "percent-encoding", "pin-project 1.1.5", "prost", - "rustls-native-certs 0.7.1", + "rustls-native-certs 0.7.2", "rustls-pemfile 2.1.3", "socket2", "tokio", @@ -8716,7 +8746,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -8770,9 +8800,9 @@ dependencies = [ [[package]] name = "tracing-tracy" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be7f8874d6438e4263f9874c84eded5095bda795d9c7da6ea0192e1750d3ffe" +checksum = "c6a90519f16f55e5c62ffd5976349f10744435a919ecff83d918300575dfb69b" dependencies = [ "tracing-core", "tracing-subscriber", @@ -8781,9 +8811,9 @@ dependencies = [ [[package]] name = "tracy-client" -version = "0.17.1" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63de1e1d4115534008d8fd5788b39324d6f58fc707849090533828619351d855" +checksum = "373db47331c3407b343538df77eea2516884a0b126cdfb4b135acfd400015dd7" dependencies = [ "loom", "once_cell", @@ -8792,9 +8822,9 @@ dependencies = [ [[package]] name = "tracy-client-sys" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98b98232a2447ce0a58f9a0bfb5f5e39647b5c597c994b63945fcccd1306fafb" +checksum = "49cf0064dcb31c99aa1244c1b93439359e53f72ed217eef5db50abd442241e9a" dependencies = [ "cc", ] @@ -8959,9 +8989,9 @@ checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" [[package]] name = "unsafe-libyaml" @@ -9116,7 +9146,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", "wasm-bindgen-shared", ] @@ -9150,7 +9180,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -9367,7 +9397,7 @@ checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -9378,7 +9408,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -9389,7 +9419,7 @@ checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -9400,7 +9430,18 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result 0.2.0", + "windows-strings", + "windows-targets 0.52.6", ] [[package]] @@ -9597,16 +9638,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "winsafe" version = "0.0.19" @@ -9685,7 +9716,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -9705,7 +9736,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -9729,9 +9760,9 @@ dependencies = [ [[package]] name = "zip" -version = "2.1.6" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40dd8c92efc296286ce1fbd16657c5dbefff44f1b4ca01cc5f517d8b7b3d3e2e" +checksum = "dc5e4288ea4057ae23afc69a4472434a87a2495cafce6632fd1c4ec9f5cf3494" dependencies = [ "arbitrary", "crc32fast", From 47c040baec32378b8fc49573bf088ad3f6e276df Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 26 Aug 2024 10:58:48 +0300 Subject: [PATCH 106/184] fix(cheatcodes): rework gas metering pause/resume (#8736) * fix(cheatcodes): rework gas metering pause/resume * Changes after review: simplify paused gas recording * Move check out of meter_gas * Fix clippy * Add unit test for 4523 --- crates/anvil/src/eth/backend/mem/mod.rs | 4 +- crates/anvil/src/eth/otterscan/api.rs | 5 +- crates/cheatcodes/src/evm.rs | 7 +- crates/cheatcodes/src/inspector.rs | 102 +++++------------- crates/common/src/contracts.rs | 2 + crates/common/src/evm.rs | 1 + .../common/src/provider/runtime_transport.rs | 2 + crates/doc/src/parser/mod.rs | 7 +- .../src/preprocessor/contract_inheritance.rs | 1 + crates/evm/core/src/backend/mod.rs | 1 + .../evm/evm/src/executors/invariant/shrink.rs | 1 + crates/evm/fuzz/src/lib.rs | 1 + crates/evm/fuzz/src/strategies/param.rs | 5 +- crates/fmt/src/visit.rs | 2 + crates/forge/tests/cli/test_cmd.rs | 80 ++++++++++++++ 15 files changed, 132 insertions(+), 89 deletions(-) diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index a089a9d2b..12ae96dab 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -2676,8 +2676,8 @@ pub fn transaction_build( WithOtherFields::new(transaction) } -/// Prove a storage key's existence or nonexistence in the account's storage -/// trie. +/// Prove a storage key's existence or nonexistence in the account's storage trie. +/// /// `storage_key` is the hash of the desired storage key, meaning /// this will only work correctly under a secure trie. /// `storage_key` == keccak(key) diff --git a/crates/anvil/src/eth/otterscan/api.rs b/crates/anvil/src/eth/otterscan/api.rs index 9b6b54a80..0195cee69 100644 --- a/crates/anvil/src/eth/otterscan/api.rs +++ b/crates/anvil/src/eth/otterscan/api.rs @@ -39,8 +39,9 @@ pub fn mentions_address(trace: LocalizedTransactionTrace, address: Address) -> O } } -/// Converts the list of traces for a transaction into the expected Otterscan format, as -/// specified in the [`ots_traceTransaction`](https://github.com/otterscan/otterscan/blob/develop/docs/custom-jsonrpc.md#ots_tracetransaction) spec +/// Converts the list of traces for a transaction into the expected Otterscan format. +/// +/// Follows format specified in the [`ots_traceTransaction`](https://github.com/otterscan/otterscan/blob/develop/docs/custom-jsonrpc.md#ots_tracetransaction) spec. pub fn batch_build_ots_traces(traces: Vec) -> Vec { traces .into_iter() diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 5cb05ec2b..1e2359425 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -209,9 +209,7 @@ impl Cheatcode for getRecordedLogsCall { impl Cheatcode for pauseGasMeteringCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; - if state.gas_metering.is_none() { - state.gas_metering = Some(None); - } + state.pause_gas_metering = true; Ok(Default::default()) } } @@ -219,7 +217,8 @@ impl Cheatcode for pauseGasMeteringCall { impl Cheatcode for resumeGasMeteringCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; - state.gas_metering = None; + state.pause_gas_metering = false; + state.paused_frame_gas = vec![]; Ok(Default::default()) } } diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 65dabcf2d..e7d354059 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -293,18 +293,11 @@ pub struct Cheatcodes { /// All recorded ETH `deal`s. pub eth_deals: Vec, - /// Holds the stored gas info for when we pause gas metering. It is an `Option>` - /// because the `call` callback in an `Inspector` doesn't get access to - /// the `revm::Interpreter` which holds the `revm::Gas` struct that - /// we need to copy. So we convert it to a `Some(None)` in `apply_cheatcode`, and once we have - /// the interpreter, we copy the gas struct. Then each time there is an execution of an - /// operation, we reset the gas. - pub gas_metering: Option>, - - /// Holds stored gas info for when we pause gas metering, and we're entering/inside - /// CREATE / CREATE2 frames. This is needed to make gas meter pausing work correctly when - /// paused and creating new contracts. - pub gas_metering_create: Option>, + /// If true then gas metering is paused. + pub pause_gas_metering: bool, + + /// Stores frames paused gas. + pub paused_frame_gas: Vec, /// Mapping slots. pub mapping_slots: Option>, @@ -353,8 +346,8 @@ impl Cheatcodes { context: Default::default(), serialized_jsons: Default::default(), eth_deals: Default::default(), - gas_metering: Default::default(), - gas_metering_create: Default::default(), + pause_gas_metering: false, + paused_frame_gas: Default::default(), mapping_slots: Default::default(), pc: Default::default(), breakpoints: Default::default(), @@ -940,7 +933,7 @@ impl Cheatcodes { impl Inspector for Cheatcodes { #[inline] - fn initialize_interp(&mut self, _interpreter: &mut Interpreter, ecx: &mut EvmContext) { + fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { // When the first interpreter is initialized we've circumvented the balance and gas checks, // so we apply our actual block data with the correct fees and all. if let Some(block) = self.block.take() { @@ -949,6 +942,11 @@ impl Inspector for Cheatcodes { if let Some(gas_price) = self.gas_price.take() { ecx.env.tx.gas_price = gas_price; } + + // Record gas for current frame if gas metering is paused. + if self.pause_gas_metering { + self.paused_frame_gas.push(interpreter.gas); + } } #[inline] @@ -956,7 +954,7 @@ impl Inspector for Cheatcodes { self.pc = interpreter.program_counter(); // `pauseGasMetering`: reset interpreter gas. - if self.gas_metering.is_some() { + if self.pause_gas_metering { self.meter_gas(interpreter); } @@ -1343,68 +1341,20 @@ impl InspectorExt for Cheatcodes { impl Cheatcodes { #[cold] fn meter_gas(&mut self, interpreter: &mut Interpreter) { - match &self.gas_metering { - None => {} - // Need to store gas metering. - Some(None) => self.gas_metering = Some(Some(interpreter.gas)), - Some(Some(gas)) => { - match interpreter.current_opcode() { - opcode::CREATE | opcode::CREATE2 => { - // Set we're about to enter CREATE frame to meter its gas on first opcode - // inside it. - self.gas_metering_create = Some(None) - } - opcode::STOP => { - // Reset gas to value recorded when paused. - interpreter.gas = *gas; - self.gas_metering = None; - self.gas_metering_create = None; - } - opcode::RETURN | opcode::SELFDESTRUCT | opcode::REVERT => { - match &self.gas_metering_create { - None | Some(None) => { - // If we are ending current execution frame, we want to reset - // interpreter gas to the value of gas spent during frame, so only - // the consumed gas is erased. - // ref: https://github.com/bluealloy/revm/blob/2cb991091d32330cfe085320891737186947ce5a/crates/revm/src/evm_impl.rs#L190 - // - // It would be nice if we had access to the interpreter in - // `call_end`, as we could just do this there instead. - interpreter.gas = Gas::new(interpreter.gas.spent()); - - // Make sure CREATE gas metering is resetted. - self.gas_metering_create = None - } - Some(Some(gas)) => { - // If this was CREATE frame, set correct gas limit. This is needed - // because CREATE opcodes deduct additional gas for code storage, - // and deducted amount is compared to gas limit. If we set this to - // 0, the CREATE would fail with out of gas. - // - // If we however set gas limit to the limit of outer frame, it would - // cause a panic after erasing gas cost post-create. Reason for this - // is pre-create REVM records `gas_limit - (gas_limit / 64)` as gas - // used, and erases costs by `remaining` gas post-create. - // gas used ref: https://github.com/bluealloy/revm/blob/2cb991091d32330cfe085320891737186947ce5a/crates/revm/src/instructions/host.rs#L254-L258 - // post-create erase ref: https://github.com/bluealloy/revm/blob/2cb991091d32330cfe085320891737186947ce5a/crates/revm/src/instructions/host.rs#L279 - interpreter.gas = Gas::new(gas.limit()); - - // Reset CREATE gas metering because we're about to exit its frame. - self.gas_metering_create = None - } - } - } - _ => { - // If just starting with CREATE opcodes, record its inner frame gas. - if self.gas_metering_create == Some(None) { - self.gas_metering_create = Some(Some(interpreter.gas)) - } + if let Some(paused_gas) = self.paused_frame_gas.last() { + // Keep gas constant if paused. + interpreter.gas = *paused_gas; + } else { + // Record frame paused gas. + self.paused_frame_gas.push(interpreter.gas); + } - // Don't monitor gas changes, keep it constant. - interpreter.gas = *gas; - } - } + // Remove recorded gas if we exit frame. + match interpreter.current_opcode() { + opcode::STOP | opcode::RETURN | opcode::REVERT | opcode::SELFDESTRUCT => { + self.paused_frame_gas.pop(); } + _ => {} } } diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index 38f73a713..bccac2f78 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -341,6 +341,8 @@ unsafe fn count_different_bytes(a: &[u8], b: &[u8]) -> usize { sum } +/// Returns contract name for a given contract identifier. +/// /// Artifact/Contract identifier can take the following form: /// `:`, the `artifact file name` is the name of the json file of /// the contract's artifact and the contract name is the name of the solidity contract, like diff --git a/crates/common/src/evm.rs b/crates/common/src/evm.rs index d7f026552..d281d5652 100644 --- a/crates/common/src/evm.rs +++ b/crates/common/src/evm.rs @@ -18,6 +18,7 @@ use serde::Serialize; 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`]) /// diff --git a/crates/common/src/provider/runtime_transport.rs b/crates/common/src/provider/runtime_transport.rs index 8ca90ca80..8362b46d6 100644 --- a/crates/common/src/provider/runtime_transport.rs +++ b/crates/common/src/provider/runtime_transport.rs @@ -62,6 +62,8 @@ pub enum RuntimeTransportError { InvalidJwt(String), } +/// Runtime transport that only connects on first request. +/// /// A runtime transport is a custom [alloy_transport::Transport] that only connects when the *first* /// request is made. When the first request is made, it will connect to the runtime using either an /// HTTP WebSocket, or IPC transport depending on the URL used. diff --git a/crates/doc/src/parser/mod.rs b/crates/doc/src/parser/mod.rs index aa493c5c6..9757fec1a 100644 --- a/crates/doc/src/parser/mod.rs +++ b/crates/doc/src/parser/mod.rs @@ -23,9 +23,10 @@ pub use item::{ParseItem, ParseSource}; mod comment; pub use comment::{Comment, CommentTag, Comments, CommentsRef}; -/// The documentation parser. This type implements a [Visitor] trait. While walking the parse tree, -/// [Parser] will collect relevant source items and corresponding doc comments. The resulting -/// [ParseItem]s can be accessed by calling [Parser::items]. +/// The documentation parser. This type implements a [Visitor] trait. +/// +/// While walking the parse tree, [Parser] will collect relevant source items and corresponding +/// doc comments. The resulting [ParseItem]s can be accessed by calling [Parser::items]. #[derive(Debug, Default)] pub struct Parser { /// Initial comments from solang parser. diff --git a/crates/doc/src/preprocessor/contract_inheritance.rs b/crates/doc/src/preprocessor/contract_inheritance.rs index 894f373e7..7d74589bd 100644 --- a/crates/doc/src/preprocessor/contract_inheritance.rs +++ b/crates/doc/src/preprocessor/contract_inheritance.rs @@ -7,6 +7,7 @@ use std::{collections::HashMap, path::PathBuf}; pub const CONTRACT_INHERITANCE_ID: PreprocessorId = PreprocessorId("contract_inheritance"); /// The contract inheritance preprocessor. +/// /// It matches the documents with inner [`ParseSource::Contract`](crate::ParseSource) elements, /// iterates over their [Base](solang_parser::pt::Base)s and attempts /// to link them with the paths of the other contract documents. diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index f33b772af..be3b0135e 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -50,6 +50,7 @@ pub use snapshot::{BackendSnapshot, RevertSnapshotAction, StateSnapshot}; type ForkDB = CacheDB; /// Represents a numeric `ForkId` valid only for the existence of the `Backend`. +/// /// The difference between `ForkId` and `LocalForkId` is that `ForkId` tracks pairs of `endpoint + /// block` which can be reused by multiple tests, whereas the `LocalForkId` is unique within a test pub type LocalForkId = U256; diff --git a/crates/evm/evm/src/executors/invariant/shrink.rs b/crates/evm/evm/src/executors/invariant/shrink.rs index 5559ec821..318163534 100644 --- a/crates/evm/evm/src/executors/invariant/shrink.rs +++ b/crates/evm/evm/src/executors/invariant/shrink.rs @@ -137,6 +137,7 @@ pub(crate) fn shrink_sequence( } /// Checks if the given call sequence breaks the invariant. +/// /// Used in shrinking phase for checking candidate sequences and in replay failures phase to test /// persisted failures. /// Returns the result of invariant check (and afterInvariant call if needed) and if sequence was diff --git a/crates/evm/fuzz/src/lib.rs b/crates/evm/fuzz/src/lib.rs index 8f24cba30..bb5bdde37 100644 --- a/crates/evm/fuzz/src/lib.rs +++ b/crates/evm/fuzz/src/lib.rs @@ -299,6 +299,7 @@ impl FuzzedCases { } /// Fixtures to be used for fuzz tests. +/// /// The key represents name of the fuzzed parameter, value holds possible fuzzed values. /// For example, for a fixture function declared as /// `function fixture_sender() external returns (address[] memory senders)` diff --git a/crates/evm/fuzz/src/strategies/param.rs b/crates/evm/fuzz/src/strategies/param.rs index 65229d686..10b166956 100644 --- a/crates/evm/fuzz/src/strategies/param.rs +++ b/crates/evm/fuzz/src/strategies/param.rs @@ -14,9 +14,10 @@ pub fn fuzz_param(param: &DynSolType) -> BoxedStrategy { } /// Given a parameter type and configured fixtures for param name, returns a strategy for generating -/// values for that type. Fixtures can be currently generated for uint, int, address, bytes and -/// string types and are defined for parameter name. +/// values for that type. /// +/// Fixtures can be currently generated for uint, int, address, bytes and +/// string types and are defined for parameter name. /// For example, fixtures for parameter `owner` of type `address` can be defined in a function with /// a `function fixture_owner() public returns (address[] memory)` signature. /// diff --git a/crates/fmt/src/visit.rs b/crates/fmt/src/visit.rs index ef9273a30..b977ba5b3 100644 --- a/crates/fmt/src/visit.rs +++ b/crates/fmt/src/visit.rs @@ -383,6 +383,8 @@ pub trait Visitor { } } +/// Visitable trait for [`solang_parser::pt`] types. +/// /// All [`solang_parser::pt`] types, such as [Statement], should implement the [Visitable] trait /// that accepts a trait [Visitor] implementation, which has various callback handles for Solidity /// Parse Tree nodes. diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 3b0cbfb7f..83869fa5b 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -1286,3 +1286,83 @@ contract ATest is Test { ... "#]]); }); + +// see https://github.com/foundry-rs/foundry/issues/5564 +forgetest_init!(repro_5564, |prj, cmd| { + prj.wipe_contracts(); + + prj.add_test( + "ATest.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; +contract ATest is Test { + error MyError(); + function testSelfMeteringRevert() public { + vm.pauseGasMetering(); + vm.expectRevert(MyError.selector); + this.selfReverts(); + } + function selfReverts() external { + vm.resumeGasMetering(); + revert MyError(); + } +} + "#, + ) + .unwrap(); + + cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" +... +[PASS] testSelfMeteringRevert() (gas: 3299) +... +"#]]); +}); + +// see https://github.com/foundry-rs/foundry/issues/4523 +forgetest_init!(repro_4523, |prj, cmd| { + prj.wipe_contracts(); + + prj.add_test( + "ATest.t.sol", + r#" +import "forge-std/Test.sol"; +contract ATest is Test { + mapping(uint => bytes32) map; + + function test_GasMeter () public { + vm.pauseGasMetering(); + for (uint i = 0; i < 10000; i++) { + map[i] = keccak256(abi.encode(i)); + } + vm.resumeGasMetering(); + + for (uint i = 0; i < 10000; i++) { + map[i] = keccak256(abi.encode(i)); + } + } + + function test_GasLeft () public { + for (uint i = 0; i < 10000; i++) { + map[i] = keccak256(abi.encode(i)); + } + + uint start = gasleft(); + for (uint i = 0; i < 10000; i++) { + map[i] = keccak256(abi.encode(i)); + } + console2.log("Gas cost:", start - gasleft()); + } +} + "#, + ) + .unwrap(); + + cmd.args(["test", "-vvvv"]).with_no_redact().assert_success().stdout_eq(str![[r#" +... +Logs: + Gas cost: 5754479 +... +[PASS] test_GasMeter() (gas: 5757137) +... +"#]]); +}); From 44cceb4a3d0a64faf818f16ce7c6ab7dc3a27400 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Mon, 26 Aug 2024 10:17:31 +0200 Subject: [PATCH 107/184] chore: add security policy document (#8741) add security policy document --- SECURITY.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..5260d529f --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,5 @@ +# Security Policy + +## Reporting a Vulnerability + +Contact georgios at paradigm.xyz. From bdf48aa6f04f69f40c8eb3623cf32f7413a82ea4 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 26 Aug 2024 19:57:47 +0800 Subject: [PATCH 108/184] feat: `vm.pauseTracing` + `vm.resumeTracing` (#8696) * feat: vm.pauseTracing + vm.resumeTracing * clippy * fixes * fix --decode-internal edge case * fmt * clippy + change tracing_inspector return type * update fixture * fix fixture --- Cargo.lock | 1 + crates/cheatcodes/Cargo.toml | 1 + crates/cheatcodes/assets/cheatcodes.json | 40 ++++++ crates/cheatcodes/spec/src/vm.rs | 9 ++ crates/cheatcodes/src/fs.rs | 18 +-- crates/cheatcodes/src/inspector.rs | 34 ++++- crates/cheatcodes/src/utils.rs | 63 ++++++++- crates/debugger/src/tui/builder.rs | 2 +- crates/evm/evm/src/executors/fuzz/mod.rs | 6 +- crates/evm/evm/src/executors/fuzz/types.rs | 4 +- crates/evm/evm/src/executors/invariant/mod.rs | 8 +- .../evm/evm/src/executors/invariant/replay.rs | 3 +- crates/evm/evm/src/executors/mod.rs | 12 +- crates/evm/evm/src/inspectors/stack.rs | 30 +++- crates/evm/fuzz/src/lib.rs | 10 +- crates/evm/traces/src/lib.rs | 133 +++++++++++++++++- crates/forge/bin/cmd/test/mod.rs | 4 +- crates/forge/src/runner.rs | 2 +- crates/forge/tests/cli/test_cmd.rs | 87 ++++++++++++ crates/forge/tests/it/repros.rs | 2 +- crates/test-utils/src/util.rs | 6 + testdata/cheats/Vm.sol | 2 + 22 files changed, 430 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index daa1a091a..f9a01ff96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3557,6 +3557,7 @@ dependencies = [ "foundry-compilers", "foundry-config", "foundry-evm-core", + "foundry-evm-traces", "foundry-wallets", "itertools 0.13.0", "jsonpath_lib", diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index 8af927f34..412c5b898 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -27,6 +27,7 @@ foundry-common.workspace = true foundry-compilers.workspace = true foundry-config.workspace = true foundry-evm-core.workspace = true +foundry-evm-traces.workspace = true foundry-wallets.workspace = true alloy-dyn-abi.workspace = true diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 80c8516f6..4f41af83e 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -6431,6 +6431,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "pauseTracing", + "description": "Pauses collection of call traces. Useful in cases when you want to skip tracing of\ncomplex calls which are not useful for debugging.", + "declaration": "function pauseTracing() external view;", + "visibility": "external", + "mutability": "view", + "signature": "pauseTracing()", + "selector": "0xc94d1f90", + "selectorBytes": [ + 201, + 77, + 31, + 144 + ] + }, + "group": "utilities", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "prank_0", @@ -7031,6 +7051,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "resumeTracing", + "description": "Unpauses collection of call traces.", + "declaration": "function resumeTracing() external view;", + "visibility": "external", + "mutability": "view", + "signature": "resumeTracing()", + "selector": "0x72a09ccb", + "selectorBytes": [ + 114, + 160, + 156, + 203 + ] + }, + "group": "utilities", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "revertTo", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 8e0b51ec3..4149b56b4 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -2282,6 +2282,15 @@ interface Vm { /// Returns a random `address`. #[cheatcode(group = Utilities)] function randomAddress() external returns (address); + + /// Pauses collection of call traces. Useful in cases when you want to skip tracing of + /// complex calls which are not useful for debugging. + #[cheatcode(group = Utilities)] + function pauseTracing() external view; + + /// Unpauses collection of call traces. + #[cheatcode(group = Utilities)] + function resumeTracing() external view; } } diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 7f07674d0..da6909401 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -272,7 +272,7 @@ impl Cheatcode for deployCode_0Call { ) -> Result { let Self { artifactPath: path } = self; let bytecode = get_artifact_code(ccx.state, path, false)?; - let output = executor + let address = executor .exec_create( CreateInputs { caller: ccx.caller, @@ -282,10 +282,11 @@ impl Cheatcode for deployCode_0Call { gas_limit: ccx.gas_limit, }, ccx, - ) - .unwrap(); + )? + .address + .ok_or_else(|| fmt_err!("contract creation failed"))?; - Ok(output.address.unwrap().abi_encode()) + Ok(address.abi_encode()) } } @@ -298,7 +299,7 @@ impl Cheatcode for deployCode_1Call { let Self { artifactPath: path, constructorArgs } = self; let mut bytecode = get_artifact_code(ccx.state, path, false)?.to_vec(); bytecode.extend_from_slice(constructorArgs); - let output = executor + let address = executor .exec_create( CreateInputs { caller: ccx.caller, @@ -308,10 +309,11 @@ impl Cheatcode for deployCode_1Call { gas_limit: ccx.gas_limit, }, ccx, - ) - .unwrap(); + )? + .address + .ok_or_else(|| fmt_err!("contract creation failed"))?; - Ok(output.address.unwrap().abi_encode()) + Ok(address.abi_encode()) } } diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index e7d354059..41b1aa562 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -13,6 +13,7 @@ use crate::{ self, ExpectedCallData, ExpectedCallTracker, ExpectedCallType, ExpectedEmit, ExpectedRevert, ExpectedRevertKind, }, + utils::IgnoredTraces, CheatsConfig, CheatsCtxt, DynCheatcode, Error, Result, Vm, Vm::AccountAccess, }; @@ -28,14 +29,15 @@ use foundry_evm_core::{ utils::new_evm_with_existing_context, InspectorExt, }; +use foundry_evm_traces::TracingInspector; use itertools::Itertools; use rand::{rngs::StdRng, Rng, SeedableRng}; use revm::{ interpreter::{ opcode, CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, EOFCreateInputs, - Gas, InstructionResult, Interpreter, InterpreterAction, InterpreterResult, + EOFCreateKind, Gas, InstructionResult, Interpreter, InterpreterAction, InterpreterResult, }, - primitives::{BlockEnv, CreateScheme, EVMError}, + primitives::{BlockEnv, CreateScheme, EVMError, SpecId, EOF_MAGIC_BYTES}, EvmContext, InnerEvmContext, Inspector, }; use rustc_hash::FxHashMap; @@ -115,8 +117,19 @@ pub trait CheatcodesExecutor { self.with_evm(ccx, |evm| { evm.context.evm.inner.journaled_state.depth += 1; - let first_frame_or_result = - evm.handler.execution().create(&mut evm.context, Box::new(inputs))?; + // Handle EOF bytecode + let first_frame_or_result = if evm.handler.cfg.spec_id.is_enabled_in(SpecId::PRAGUE_EOF) + && inputs.scheme == CreateScheme::Create && inputs.init_code.starts_with(&EOF_MAGIC_BYTES) + { + evm.handler.execution().eofcreate( + &mut evm.context, + Box::new(EOFCreateInputs::new(inputs.caller, inputs.value, inputs.gas_limit, EOFCreateKind::Tx { + initdata: inputs.init_code, + })), + )? + } else { + evm.handler.execution().create(&mut evm.context, Box::new(inputs))? + }; let mut result = match first_frame_or_result { revm::FrameOrResult::Frame(first_frame) => evm.run_the_loop(first_frame)?, @@ -126,8 +139,8 @@ pub trait CheatcodesExecutor { evm.handler.execution().last_frame_return(&mut evm.context, &mut result)?; let outcome = match result { - revm::FrameResult::Call(_) | revm::FrameResult::EOFCreate(_) => unreachable!(), - revm::FrameResult::Create(create) => create, + revm::FrameResult::Call(_) => unreachable!(), + revm::FrameResult::Create(create) | revm::FrameResult::EOFCreate(create) => create, }; evm.context.evm.inner.journaled_state.depth -= 1; @@ -139,6 +152,11 @@ pub trait CheatcodesExecutor { fn console_log(&mut self, ccx: &mut CheatsCtxt, message: String) { self.get_inspector::(ccx.state).console_log(message); } + + /// Returns a mutable reference to the tracing inspector if it is available. + fn tracing_inspector(&mut self) -> Option<&mut Option> { + None + } } /// Basic implementation of [CheatcodesExecutor] that simply returns the [Cheatcodes] instance as an @@ -310,6 +328,9 @@ pub struct Cheatcodes { /// Optional RNG algorithm. rng: Option, + + /// Ignored traces. + pub ignored_traces: IgnoredTraces, } // This is not derived because calling this in `fn new` with `..Default::default()` creates a second @@ -352,6 +373,7 @@ impl Cheatcodes { pc: Default::default(), breakpoints: Default::default(), rng: Default::default(), + ignored_traces: Default::default(), } } diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index 5fa55ba8f..642cf83ab 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -4,8 +4,23 @@ use crate::{Cheatcode, Cheatcodes, Result, Vm::*}; use alloy_primitives::{Address, U256}; use alloy_sol_types::SolValue; use foundry_common::ens::namehash; -use foundry_evm_core::constants::DEFAULT_CREATE2_DEPLOYER; +use foundry_evm_core::{backend::DatabaseExt, constants::DEFAULT_CREATE2_DEPLOYER}; use rand::Rng; +use std::collections::HashMap; + +/// Contains locations of traces ignored via cheatcodes. +/// +/// The way we identify location in traces is by (node_idx, item_idx) tuple where node_idx is an +/// index of a call trace node, and item_idx is a value between 0 and `node.ordering.len()` where i +/// represents point after ith item, and 0 represents the beginning of the node trace. +#[derive(Debug, Default, Clone)] +pub struct IgnoredTraces { + /// Mapping from (start_node_idx, start_item_idx) to (end_node_idx, end_item_idx) representing + /// ranges of trace nodes to ignore. + pub ignored: HashMap<(usize, usize), (usize, usize)>, + /// Keeps track of (start_node_idx, start_item_idx) of the last `vm.pauseTracing` call. + pub last_pause_call: Option<(usize, usize)>, +} impl Cheatcode for labelCall { fn apply(&self, state: &mut Cheatcodes) -> Result { @@ -88,3 +103,49 @@ impl Cheatcode for randomAddressCall { Ok(addr.abi_encode()) } } + +impl Cheatcode for pauseTracingCall { + fn apply_full( + &self, + ccx: &mut crate::CheatsCtxt, + executor: &mut E, + ) -> Result { + let Some(tracer) = executor.tracing_inspector().and_then(|t| t.as_ref()) else { + // No tracer -> nothing to pause + return Ok(Default::default()) + }; + + // If paused earlier, ignore the call + if ccx.state.ignored_traces.last_pause_call.is_some() { + return Ok(Default::default()) + } + + let cur_node = &tracer.traces().nodes().last().expect("no trace nodes"); + ccx.state.ignored_traces.last_pause_call = Some((cur_node.idx, cur_node.ordering.len())); + + Ok(Default::default()) + } +} + +impl Cheatcode for resumeTracingCall { + fn apply_full( + &self, + ccx: &mut crate::CheatsCtxt, + executor: &mut E, + ) -> Result { + let Some(tracer) = executor.tracing_inspector().and_then(|t| t.as_ref()) else { + // No tracer -> nothing to unpause + return Ok(Default::default()) + }; + + let Some(start) = ccx.state.ignored_traces.last_pause_call.take() else { + // Nothing to unpause + return Ok(Default::default()) + }; + + let node = &tracer.traces().nodes().last().expect("no trace nodes"); + ccx.state.ignored_traces.ignored.insert(start, (node.idx, node.ordering.len())); + + Ok(Default::default()) + } +} diff --git a/crates/debugger/src/tui/builder.rs b/crates/debugger/src/tui/builder.rs index 81632dcca..fd952def5 100644 --- a/crates/debugger/src/tui/builder.rs +++ b/crates/debugger/src/tui/builder.rs @@ -31,7 +31,7 @@ impl DebuggerBuilder { #[inline] pub fn traces(mut self, traces: Traces) -> Self { for (_, arena) in traces { - self = self.trace_arena(arena); + self = self.trace_arena(arena.arena); } self } diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index 76f01541d..7fa5498bf 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -11,7 +11,7 @@ use foundry_evm_fuzz::{ strategies::{fuzz_calldata, fuzz_calldata_from_state, EvmFuzzState}, BaseCounterExample, CounterExample, FuzzCase, FuzzError, FuzzFixtures, FuzzTestResult, }; -use foundry_evm_traces::CallTraceArena; +use foundry_evm_traces::SparsedTraceArena; use indicatif::ProgressBar; use proptest::test_runner::{TestCaseError, TestError, TestRunner}; use std::cell::RefCell; @@ -29,7 +29,7 @@ pub struct FuzzTestData { // Stores the result and calldata of the last failed call, if any. pub counterexample: (Bytes, RawCallResult), // Stores up to `max_traces_to_collect` traces. - pub traces: Vec, + pub traces: Vec, // Stores breakpoints for the last fuzz case. pub breakpoints: Option, // Stores coverage information for all fuzz cases. @@ -163,7 +163,7 @@ impl FuzzedExecutor { labeled_addresses: call.labels, traces: last_run_traces, breakpoints: last_run_breakpoints, - gas_report_traces: traces, + gas_report_traces: traces.into_iter().map(|a| a.arena).collect(), coverage: fuzz_result.coverage, }; diff --git a/crates/evm/evm/src/executors/fuzz/types.rs b/crates/evm/evm/src/executors/fuzz/types.rs index 7ec707eff..ac7c14373 100644 --- a/crates/evm/evm/src/executors/fuzz/types.rs +++ b/crates/evm/evm/src/executors/fuzz/types.rs @@ -3,7 +3,7 @@ use alloy_primitives::{Bytes, Log}; use foundry_common::evm::Breakpoints; use foundry_evm_coverage::HitMaps; use foundry_evm_fuzz::FuzzCase; -use foundry_evm_traces::CallTraceArena; +use foundry_evm_traces::SparsedTraceArena; use revm::interpreter::InstructionResult; /// Returned by a single fuzz in the case of a successful run @@ -12,7 +12,7 @@ pub struct CaseOutcome { /// Data of a single fuzz test case pub case: FuzzCase, /// The traces of the call - pub traces: Option, + pub traces: Option, /// The coverage info collected during the call pub coverage: Option, /// Breakpoints char pc map diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 91143f4b2..b5854d1d7 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -21,7 +21,7 @@ use foundry_evm_fuzz::{ strategies::{invariant_strat, override_call_strat, EvmFuzzState}, FuzzCase, FuzzFixtures, FuzzedCases, }; -use foundry_evm_traces::CallTraceArena; +use foundry_evm_traces::{CallTraceArena, SparsedTraceArena}; use indicatif::ProgressBar; use parking_lot::RwLock; use proptest::{ @@ -199,7 +199,9 @@ impl InvariantTest { let mut invariant_data = self.execution_data.borrow_mut(); if invariant_data.gas_report_traces.len() < gas_samples { - invariant_data.gas_report_traces.push(run.run_traces); + invariant_data + .gas_report_traces + .push(run.run_traces.into_iter().map(|arena| arena.arena).collect()); } invariant_data.fuzz_cases.push(FuzzedCases::new(run.fuzz_runs)); @@ -219,7 +221,7 @@ pub struct InvariantTestRun { // Contracts created during current invariant run. pub created_contracts: Vec
, // Traces of each call of the invariant run call sequence. - pub run_traces: Vec, + pub run_traces: Vec, // Current depth of invariant run. pub depth: u32, // Current assume rejects of the invariant run. diff --git a/crates/evm/evm/src/executors/invariant/replay.rs b/crates/evm/evm/src/executors/invariant/replay.rs index 9de5bf531..c10d31560 100644 --- a/crates/evm/evm/src/executors/invariant/replay.rs +++ b/crates/evm/evm/src/executors/invariant/replay.rs @@ -59,7 +59,8 @@ pub fn replay_run( } // Identify newly generated contracts, if they exist. - ided_contracts.extend(load_contracts(call_result.traces.as_slice(), known_contracts)); + ided_contracts + .extend(load_contracts(call_result.traces.iter().map(|a| &a.arena), known_contracts)); // Create counter example to be used in failed case. counterexample_sequence.push(BaseCounterExample::from_invariant_call( diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 2f08b2f42..44bc33e37 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -23,7 +23,7 @@ use foundry_evm_core::{ utils::StateChangeset, }; use foundry_evm_coverage::HitMaps; -use foundry_evm_traces::{CallTraceArena, TraceMode}; +use foundry_evm_traces::{SparsedTraceArena, TraceMode}; use revm::{ db::{DatabaseCommit, DatabaseRef}, interpreter::{return_ok, InstructionResult}, @@ -424,9 +424,13 @@ impl Executor { if let Some(cheats) = self.inspector_mut().cheatcodes.as_mut() { // Clear broadcastable transactions cheats.broadcastable_transactions.clear(); + cheats.ignored_traces.ignored.clear(); - // corrected_nonce value is needed outside of this context (setUp), so we don't - // reset it. + // if tracing was paused but never unpaused, we should begin next frame with tracing + // still paused + if let Some(last_pause_call) = cheats.ignored_traces.last_pause_call.as_mut() { + *last_pause_call = (0, 0); + } } // Persist the changed environment. @@ -721,7 +725,7 @@ pub struct RawCallResult { /// The labels assigned to addresses during the call pub labels: HashMap, /// The traces of the call - pub traces: Option, + pub traces: Option, /// The coverage info collected during the call pub coverage: Option, /// Scripted transactions generated from this call diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 719998365..f72c62ff3 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -9,7 +9,7 @@ use foundry_evm_core::{ InspectorExt, }; use foundry_evm_coverage::HitMaps; -use foundry_evm_traces::{CallTraceArena, TraceMode}; +use foundry_evm_traces::{SparsedTraceArena, TraceMode}; use revm::{ inspectors::CustomPrintTracer, interpreter::{ @@ -244,7 +244,7 @@ macro_rules! call_inspectors_adjust_depth { pub struct InspectorData { pub logs: Vec, pub labels: HashMap, - pub traces: Option, + pub traces: Option, pub coverage: Option, pub cheatcodes: Option, pub chisel_state: Option<(Vec, Vec, InstructionResult)>, @@ -318,6 +318,10 @@ impl CheatcodesExecutor for InspectorStackInner { ) -> impl InspectorExt + 'a { InspectorStackRefMut { cheatcodes: Some(cheats), inner: self } } + + fn tracing_inspector(&mut self) -> Option<&mut Option> { + Some(&mut self.tracer) + } } impl InspectorStack { @@ -437,17 +441,35 @@ impl InspectorStack { #[inline] pub fn collect(self) -> InspectorData { let Self { - cheatcodes, + mut cheatcodes, inner: InspectorStackInner { chisel_state, coverage, log_collector, tracer, .. }, } = self; + let traces = tracer.map(|tracer| tracer.into_traces()).map(|arena| { + let ignored = cheatcodes + .as_mut() + .map(|cheatcodes| { + let mut ignored = std::mem::take(&mut cheatcodes.ignored_traces.ignored); + + // If the last pause call was not resumed, ignore the rest of the trace + if let Some(last_pause_call) = cheatcodes.ignored_traces.last_pause_call { + ignored.insert(last_pause_call, (arena.nodes().len(), 0)); + } + + ignored + }) + .unwrap_or_default(); + + SparsedTraceArena { arena, ignored } + }); + InspectorData { logs: log_collector.map(|logs| logs.logs).unwrap_or_default(), labels: cheatcodes .as_ref() .map(|cheatcodes| cheatcodes.labels.clone()) .unwrap_or_default(), - traces: tracer.map(|tracer| tracer.into_traces()), + traces, coverage: coverage.map(|coverage| coverage.maps), cheatcodes, chisel_state: chisel_state.and_then(|state| state.state), diff --git a/crates/evm/fuzz/src/lib.rs b/crates/evm/fuzz/src/lib.rs index bb5bdde37..99232443b 100644 --- a/crates/evm/fuzz/src/lib.rs +++ b/crates/evm/fuzz/src/lib.rs @@ -12,7 +12,7 @@ use alloy_dyn_abi::{DynSolValue, JsonAbiExt}; use alloy_primitives::{Address, Bytes, Log}; use foundry_common::{calc, contracts::ContractsByAddress, evm::Breakpoints}; use foundry_evm_coverage::HitMaps; -use foundry_evm_traces::CallTraceArena; +use foundry_evm_traces::{CallTraceArena, SparsedTraceArena}; use itertools::Itertools; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, fmt, sync::Arc}; @@ -52,7 +52,7 @@ pub struct BaseCounterExample { pub args: Option, /// Traces #[serde(skip)] - pub traces: Option, + pub traces: Option, } impl BaseCounterExample { @@ -62,7 +62,7 @@ impl BaseCounterExample { addr: Address, bytes: &Bytes, contracts: &ContractsByAddress, - traces: Option, + traces: Option, ) -> Self { if let Some((name, abi)) = &contracts.get(&addr) { if let Some(func) = abi.functions().find(|f| f.selector() == bytes[..4]) { @@ -98,7 +98,7 @@ impl BaseCounterExample { pub fn from_fuzz_call( bytes: Bytes, args: Vec, - traces: Option, + traces: Option, ) -> Self { Self { sender: None, @@ -170,7 +170,7 @@ pub struct FuzzTestResult { /// /// **Note** We only store a single trace of a successful fuzz call, otherwise we would get /// `num(fuzz_cases)` traces, one for each run, which is neither helpful nor performant. - pub traces: Option, + pub traces: Option, /// Additional traces used for gas report construction. /// Those traces should not be displayed. diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index 4677a02bc..4a80b164a 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -10,8 +10,16 @@ extern crate tracing; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; use revm::interpreter::OpCode; -use revm_inspectors::tracing::OpcodeFilter; +use revm_inspectors::tracing::{ + types::{DecodedTraceStep, TraceMemberOrder}, + OpcodeFilter, +}; use serde::{Deserialize, Serialize}; +use std::{ + borrow::Cow, + collections::{BTreeSet, HashMap}, + ops::{Deref, DerefMut}, +}; pub use revm_inspectors::tracing::{ types::{ @@ -34,7 +42,124 @@ pub use decoder::{CallTraceDecoder, CallTraceDecoderBuilder}; pub mod debug; pub use debug::DebugTraceIdentifier; -pub type Traces = Vec<(TraceKind, CallTraceArena)>; +pub type Traces = Vec<(TraceKind, SparsedTraceArena)>; + +/// Trace arena keeping track of ignored trace items. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SparsedTraceArena { + /// Full trace arena. + #[serde(flatten)] + pub arena: CallTraceArena, + /// Ranges of trace steps to ignore in format (start_node, start_step) -> (end_node, end_step). + /// See `foundry_cheatcodes::utils::IgnoredTraces` for more information. + #[serde(default, skip_serializing_if = "HashMap::is_empty")] + pub ignored: HashMap<(usize, usize), (usize, usize)>, +} + +impl SparsedTraceArena { + /// Goes over entire trace arena and removes ignored trace items. + fn resolve_arena(&self) -> Cow<'_, CallTraceArena> { + if self.ignored.is_empty() { + Cow::Borrowed(&self.arena) + } else { + let mut arena = self.arena.clone(); + + fn clear_node( + nodes: &mut [CallTraceNode], + node_idx: usize, + ignored: &HashMap<(usize, usize), (usize, usize)>, + cur_ignore_end: &mut Option<(usize, usize)>, + ) { + // Prepend an additional None item to the ordering to handle the beginning of the + // trace. + let items = std::iter::once(None) + .chain(nodes[node_idx].ordering.clone().into_iter().map(Some)) + .enumerate(); + + let mut iternal_calls = Vec::new(); + let mut items_to_remove = BTreeSet::new(); + for (item_idx, item) in items { + if let Some(end_node) = ignored.get(&(node_idx, item_idx)) { + *cur_ignore_end = Some(*end_node); + } + + let mut remove = cur_ignore_end.is_some() & item.is_some(); + + match item { + // we only remove calls if they did not start/pause tracing + Some(TraceMemberOrder::Call(child_idx)) => { + clear_node( + nodes, + nodes[node_idx].children[child_idx], + ignored, + cur_ignore_end, + ); + remove &= cur_ignore_end.is_some(); + } + // we only remove decoded internal calls if they did not start/pause tracing + Some(TraceMemberOrder::Step(step_idx)) => { + // If this is an internal call beginning, track it in `iternal_calls` + if let Some(DecodedTraceStep::InternalCall(_, end_step_idx)) = + &nodes[node_idx].trace.steps[step_idx].decoded + { + iternal_calls.push((item_idx, remove, *end_step_idx)); + // we decide if we should remove it later + remove = false; + } + // Handle ends of internal calls + iternal_calls.retain(|(start_item_idx, remove_start, end_step_idx)| { + if *end_step_idx != step_idx { + return true; + } + // only remove start if end should be removed as well + if *remove_start && remove { + items_to_remove.insert(*start_item_idx); + } else { + remove = false; + } + + false + }); + } + _ => {} + } + + if remove { + items_to_remove.insert(item_idx); + } + + if let Some((end_node, end_step_idx)) = cur_ignore_end { + if node_idx == *end_node && item_idx == *end_step_idx { + *cur_ignore_end = None; + } + } + } + + for (offset, item_idx) in items_to_remove.into_iter().enumerate() { + nodes[node_idx].ordering.remove(item_idx - offset - 1); + } + } + + clear_node(arena.nodes_mut(), 0, &self.ignored, &mut None); + + Cow::Owned(arena) + } + } +} + +impl Deref for SparsedTraceArena { + type Target = CallTraceArena; + + fn deref(&self) -> &Self::Target { + &self.arena + } +} + +impl DerefMut for SparsedTraceArena { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.arena + } +} /// Decode a collection of call traces. /// @@ -50,9 +175,9 @@ pub async fn decode_trace_arena( } /// Render a collection of call traces to a string. -pub fn render_trace_arena(arena: &CallTraceArena) -> String { +pub fn render_trace_arena(arena: &SparsedTraceArena) -> String { let mut w = TraceWriter::new(Vec::::new()); - w.write_arena(arena).expect("Failed to write traces"); + w.write_arena(&arena.resolve_arena()).expect("Failed to write traces"); String::from_utf8(w.into_writer()).expect("trace writer wrote invalid UTF-8") } diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index ab868f5bc..ec582f298 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -560,9 +560,7 @@ impl TestArgs { } if let Some(gas_report) = &mut gas_report { - gas_report - .analyze(result.traces.iter().map(|(_, arena)| arena), &decoder) - .await; + gas_report.analyze(result.traces.iter().map(|(_, a)| &a.arena), &decoder).await; for trace in result.gas_report_traces.iter() { decoder.clear_addresses(); diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 6ed06e525..3cfbd76c9 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -350,7 +350,7 @@ impl<'a> ContractRunner<'a> { ); let identified_contracts = has_invariants - .then(|| load_contracts(setup.traces.iter().map(|(_, t)| t), &known_contracts)); + .then(|| load_contracts(setup.traces.iter().map(|(_, t)| &t.arena), &known_contracts)); let test_results = functions .par_iter() .map(|&func| { diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 83869fa5b..eae1514cf 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -1366,3 +1366,90 @@ Logs: ... "#]]); }); + +// tests `pauseTracing` and `resumeTracing` functions +#[cfg(not(feature = "isolate-by-default"))] +forgetest_init!(pause_tracing, |prj, cmd| { + prj.wipe_contracts(); + prj.insert_ds_test(); + prj.insert_vm(); + prj.clear(); + + prj.add_source( + "Pause.t.sol", + r#"pragma solidity 0.8.24; +import {Vm} from "./Vm.sol"; +import {DSTest} from "./test.sol"; +contract TraceGenerator is DSTest { + Vm vm = Vm(HEVM_ADDRESS); + event DummyEvent(uint256 i); + function call(uint256 i) public { + emit DummyEvent(i); + } + function generate() public { + for (uint256 i = 0; i < 10; i++) { + if (i == 3) { + vm.pauseTracing(); + } + this.call(i); + if (i == 7) { + vm.resumeTracing(); + } + } + } +} +contract PauseTracingTest is DSTest { + Vm vm = Vm(HEVM_ADDRESS); + event DummyEvent(uint256 i); + function setUp() public { + emit DummyEvent(1); + vm.pauseTracing(); + emit DummyEvent(2); + } + function test() public { + emit DummyEvent(3); + TraceGenerator t = new TraceGenerator(); + vm.resumeTracing(); + t.generate(); + } +} + "#, + ) + .unwrap(); + cmd.args(["test", "-vvvvv"]).assert_success().stdout_eq(str![[r#" +... +Traces: + [7285] PauseTracingTest::setUp() + ├─ emit DummyEvent(i: 1) + ├─ [0] VM::pauseTracing() [staticcall] + │ └─ ← [Return] + └─ ← [Stop] + + [294725] PauseTracingTest::test() + ├─ [0] VM::resumeTracing() [staticcall] + │ └─ ← [Return] + ├─ [18373] TraceGenerator::generate() + │ ├─ [1280] TraceGenerator::call(0) + │ │ ├─ emit DummyEvent(i: 0) + │ │ └─ ← [Stop] + │ ├─ [1280] TraceGenerator::call(1) + │ │ ├─ emit DummyEvent(i: 1) + │ │ └─ ← [Stop] + │ ├─ [1280] TraceGenerator::call(2) + │ │ ├─ emit DummyEvent(i: 2) + │ │ └─ ← [Stop] + │ ├─ [0] VM::pauseTracing() [staticcall] + │ │ └─ ← [Return] + │ ├─ [0] VM::resumeTracing() [staticcall] + │ │ └─ ← [Return] + │ ├─ [1280] TraceGenerator::call(8) + │ │ ├─ emit DummyEvent(i: 8) + │ │ └─ ← [Stop] + │ ├─ [1280] TraceGenerator::call(9) + │ │ ├─ emit DummyEvent(i: 9) + │ │ └─ ← [Stop] + │ └─ ← [Stop] + └─ ← [Stop] +... +"#]]); +}); diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index a74933f6f..6c0708e20 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -261,7 +261,7 @@ test_repro!(6501, false, None, |res| { ); let (kind, traces) = test.traces.last().unwrap().clone(); - let nodes = traces.into_nodes(); + let nodes = traces.arena.into_nodes(); assert_eq!(kind, TraceKind::Execution); let test_call = nodes.first().unwrap(); diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 593dbdbe1..ac5674d9a 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -596,6 +596,12 @@ impl TestProject { self.add_source("console.sol", s).unwrap() } + /// Adds `Vm.sol` as a source under "Vm.sol" + pub fn insert_vm(&self) -> PathBuf { + let s = include_str!("../../../testdata/cheats/Vm.sol"); + self.add_source("Vm.sol", s).unwrap() + } + /// Asserts all project paths exist. These are: /// - sources /// - artifacts diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 8004efb1d..8ce2ce5fe 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -317,6 +317,7 @@ interface Vm { function parseToml(string calldata toml, string calldata key) external pure returns (bytes memory abiEncodedData); function parseUint(string calldata stringifiedValue) external pure returns (uint256 parsedValue); function pauseGasMetering() external; + function pauseTracing() external view; function prank(address msgSender) external; function prank(address msgSender, address txOrigin) external; function prevrandao(bytes32 newPrevrandao) external; @@ -347,6 +348,7 @@ interface Vm { function replace(string calldata input, string calldata from, string calldata to) external pure returns (string memory output); function resetNonce(address account) external; function resumeGasMetering() external; + function resumeTracing() external view; function revertTo(uint256 snapshotId) external returns (bool success); function revertToAndDelete(uint256 snapshotId) external returns (bool success); function revokePersistent(address account) external; From 88a4920420fe2e7f82efb997f614e9ed7ca40da5 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 26 Aug 2024 16:58:49 +0200 Subject: [PATCH 109/184] chore(cheatcodes): make pauseGasMetering more robust (#8743) * chore: make pauseGasMetering more robust * test * test * simpler * fix(cheatcodes): reconcile gas when exiting frame (#8744) * Fix for 4370 + tests * assert on test kind gas --------- Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> --- crates/cheatcodes/src/evm.rs | 5 +- crates/cheatcodes/src/inspector.rs | 126 +++++++++++++++------- crates/evm/evm/src/inspectors/stack.rs | 2 +- crates/forge/tests/cli/test_cmd.rs | 84 ++++++++++----- crates/forge/tests/it/repros.rs | 15 ++- testdata/default/cheats/GasMetering.t.sol | 12 +-- 6 files changed, 162 insertions(+), 82 deletions(-) diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 1e2359425..6d8bdfb37 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -209,7 +209,7 @@ impl Cheatcode for getRecordedLogsCall { impl Cheatcode for pauseGasMeteringCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; - state.pause_gas_metering = true; + state.gas_metering.paused = true; Ok(Default::default()) } } @@ -217,8 +217,7 @@ impl Cheatcode for pauseGasMeteringCall { impl Cheatcode for resumeGasMeteringCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; - state.pause_gas_metering = false; - state.paused_frame_gas = vec![]; + state.gas_metering.resume(); Ok(Default::default()) } } diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 41b1aa562..09ced9294 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -34,8 +34,9 @@ use itertools::Itertools; use rand::{rngs::StdRng, Rng, SeedableRng}; use revm::{ interpreter::{ - opcode, CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, EOFCreateInputs, - EOFCreateKind, Gas, InstructionResult, Interpreter, InterpreterAction, InterpreterResult, + opcode as op, CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, + EOFCreateInputs, EOFCreateKind, Gas, InstructionResult, Interpreter, InterpreterAction, + InterpreterResult, }, primitives::{BlockEnv, CreateScheme, EVMError, SpecId, EOF_MAGIC_BYTES}, EvmContext, InnerEvmContext, Inspector, @@ -213,6 +214,28 @@ pub struct BroadcastableTransaction { pub transaction: TransactionMaybeSigned, } +/// Holds gas metering state. +#[derive(Clone, Debug, Default)] +pub struct GasMetering { + /// True if gas metering is paused. + pub paused: bool, + /// True if gas metering was resumed during the test. + pub resumed: bool, + /// Stores frames paused gas. + pub paused_frames: Vec, +} + +impl GasMetering { + /// Resume paused gas metering. + pub fn resume(&mut self) { + if self.paused { + self.paused = false; + self.resumed = true; + } + self.paused_frames.clear(); + } +} + /// List of transactions that can be broadcasted. pub type BroadcastableTransactions = VecDeque; @@ -311,11 +334,8 @@ pub struct Cheatcodes { /// All recorded ETH `deal`s. pub eth_deals: Vec, - /// If true then gas metering is paused. - pub pause_gas_metering: bool, - - /// Stores frames paused gas. - pub paused_frame_gas: Vec, + /// Gas metering state. + pub gas_metering: GasMetering, /// Mapping slots. pub mapping_slots: Option>, @@ -367,8 +387,7 @@ impl Cheatcodes { context: Default::default(), serialized_jsons: Default::default(), eth_deals: Default::default(), - pause_gas_metering: false, - paused_frame_gas: Default::default(), + gas_metering: Default::default(), mapping_slots: Default::default(), pc: Default::default(), breakpoints: Default::default(), @@ -965,9 +984,9 @@ impl Inspector for Cheatcodes { ecx.env.tx.gas_price = gas_price; } - // Record gas for current frame if gas metering is paused. - if self.pause_gas_metering { - self.paused_frame_gas.push(interpreter.gas); + // Record gas for current frame. + if self.gas_metering.paused { + self.gas_metering.paused_frames.push(interpreter.gas); } } @@ -976,7 +995,7 @@ impl Inspector for Cheatcodes { self.pc = interpreter.program_counter(); // `pauseGasMetering`: reset interpreter gas. - if self.pause_gas_metering { + if self.gas_metering.paused { self.meter_gas(interpreter); } @@ -1001,7 +1020,18 @@ impl Inspector for Cheatcodes { } } - fn log(&mut self, _interpreter: &mut Interpreter, _context: &mut EvmContext, log: &Log) { + #[inline] + fn step_end(&mut self, interpreter: &mut Interpreter, _ecx: &mut EvmContext) { + if self.gas_metering.paused { + self.meter_gas_end(interpreter); + } + + if self.gas_metering.resumed { + self.meter_gas_check(interpreter); + } + } + + fn log(&mut self, _interpreter: &mut Interpreter, _ecx: &mut EvmContext, log: &Log) { if !self.expected_emits.is_empty() { expect::handle_expect_emit(self, log); } @@ -1016,12 +1046,8 @@ impl Inspector for Cheatcodes { } } - fn call( - &mut self, - context: &mut EvmContext, - inputs: &mut CallInputs, - ) -> Option { - Self::call_with_executor(self, context, inputs, &mut TransparentCheatcodesExecutor) + fn call(&mut self, ecx: &mut EvmContext, inputs: &mut CallInputs) -> Option { + Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor) } fn call_end( @@ -1363,20 +1389,33 @@ impl InspectorExt for Cheatcodes { impl Cheatcodes { #[cold] fn meter_gas(&mut self, interpreter: &mut Interpreter) { - if let Some(paused_gas) = self.paused_frame_gas.last() { + if let Some(paused_gas) = self.gas_metering.paused_frames.last() { // Keep gas constant if paused. interpreter.gas = *paused_gas; } else { // Record frame paused gas. - self.paused_frame_gas.push(interpreter.gas); + self.gas_metering.paused_frames.push(interpreter.gas); } + } + #[cold] + fn meter_gas_end(&mut self, interpreter: &mut Interpreter) { // Remove recorded gas if we exit frame. - match interpreter.current_opcode() { - opcode::STOP | opcode::RETURN | opcode::REVERT | opcode::SELFDESTRUCT => { - self.paused_frame_gas.pop(); + if will_exit(interpreter.instruction_result) { + self.gas_metering.paused_frames.pop(); + } + } + + #[cold] + fn meter_gas_check(&mut self, interpreter: &mut Interpreter) { + if will_exit(interpreter.instruction_result) { + // Reconcile gas if spent is less than refunded. + // This can happen if gas was paused / resumed (https://github.com/foundry-rs/foundry/issues/4370). + if interpreter.gas.spent() < + u64::try_from(interpreter.gas.refunded()).unwrap_or_default() + { + interpreter.gas = Gas::new(interpreter.gas.remaining()); } - _ => {} } } @@ -1385,11 +1424,11 @@ impl Cheatcodes { fn record_accesses(&mut self, interpreter: &mut Interpreter) { let Some(access) = &mut self.accesses else { return }; match interpreter.current_opcode() { - opcode::SLOAD => { + op::SLOAD => { let key = try_or_return!(interpreter.stack().peek(0)); access.record_read(interpreter.contract().target_address, key); } - opcode::SSTORE => { + op::SSTORE => { let key = try_or_return!(interpreter.stack().peek(0)); access.record_write(interpreter.contract().target_address, key); } @@ -1405,7 +1444,7 @@ impl Cheatcodes { ) { let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return }; match interpreter.current_opcode() { - opcode::SELFDESTRUCT => { + op::SELFDESTRUCT => { // Ensure that we're not selfdestructing a context recording was initiated on let Some(last) = account_accesses.last_mut() else { return }; @@ -1444,7 +1483,7 @@ impl Cheatcodes { }); } - opcode::SLOAD => { + op::SLOAD => { let Some(last) = account_accesses.last_mut() else { return }; let key = try_or_return!(interpreter.stack().peek(0)); @@ -1469,7 +1508,7 @@ impl Cheatcodes { }; append_storage_access(last, access, ecx.journaled_state.depth()); } - opcode::SSTORE => { + op::SSTORE => { let Some(last) = account_accesses.last_mut() else { return }; let key = try_or_return!(interpreter.stack().peek(0)); @@ -1496,12 +1535,12 @@ impl Cheatcodes { } // Record account accesses via the EXT family of opcodes - opcode::EXTCODECOPY | opcode::EXTCODESIZE | opcode::EXTCODEHASH | opcode::BALANCE => { + op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => { let kind = match interpreter.current_opcode() { - opcode::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy, - opcode::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize, - opcode::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash, - opcode::BALANCE => crate::Vm::AccountAccessKind::Balance, + op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy, + op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize, + op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash, + op::BALANCE => crate::Vm::AccountAccessKind::Balance, _ => unreachable!(), }; let address = @@ -1570,7 +1609,7 @@ impl Cheatcodes { // OPERATIONS THAT CAN EXPAND/MUTATE MEMORY BY WRITING // //////////////////////////////////////////////////////////////// - opcode::MSTORE => { + op::MSTORE => { // The offset of the mstore operation is at the top of the stack. let offset = try_or_return!(interpreter.stack().peek(0)).saturating_to::(); @@ -1592,7 +1631,7 @@ impl Cheatcodes { return } } - opcode::MSTORE8 => { + op::MSTORE8 => { // The offset of the mstore8 operation is at the top of the stack. let offset = try_or_return!(interpreter.stack().peek(0)).saturating_to::(); @@ -1608,7 +1647,7 @@ impl Cheatcodes { // OPERATIONS THAT CAN EXPAND MEMORY BY READING // //////////////////////////////////////////////////////////////// - opcode::MLOAD => { + op::MLOAD => { // The offset of the mload operation is at the top of the stack let offset = try_or_return!(interpreter.stack().peek(0)).saturating_to::(); @@ -1627,7 +1666,7 @@ impl Cheatcodes { // OPERATIONS WITH OFFSET AND SIZE ON STACK // //////////////////////////////////////////////////////////////// - opcode::CALL => { + op::CALL => { // The destination offset of the operation is the fifth element on the stack. let dest_offset = try_or_return!(interpreter.stack().peek(5)).saturating_to::(); @@ -1663,7 +1702,7 @@ impl Cheatcodes { } } - $(opcode::$opcode => { + $(op::$opcode => { // The destination offset of the operation. let dest_offset = try_or_return!(interpreter.stack().peek($offset_depth)).saturating_to::(); @@ -1842,6 +1881,11 @@ fn apply_dispatch( result } +/// Helper function to check if frame execution will exit. +fn will_exit(ir: InstructionResult) -> bool { + !matches!(ir, InstructionResult::Continue | InstructionResult::CallOrCreate) +} + // Caches the result of `calls_as_dyn_cheatcode`. // TODO: Remove this once Cheatcode is object-safe, as caching would not be necessary anymore. struct DynCheatCache<'a> { diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index f72c62ff3..e44d499ea 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -686,7 +686,7 @@ impl<'a, DB: DatabaseExt> Inspector for InspectorStackRefMut<'a> { fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { call_inspectors_adjust_depth!( - [&mut self.tracer, &mut self.chisel_state, &mut self.printer], + [&mut self.tracer, &mut self.cheatcodes, &mut self.chisel_state, &mut self.printer], |inspector| inspector.step_end(interpreter, ecx), self, ecx diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index eae1514cf..37e1895f6 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -462,8 +462,8 @@ forgetest_init!(exit_code_error_on_fail_fast_with_json, |prj, cmd| { cmd.assert_err(); }); -// -forgetest_init!(repro_6531, |prj, cmd| { +// https://github.com/foundry-rs/foundry/pull/6531 +forgetest_init!(fork_traces, |prj, cmd| { prj.wipe_contracts(); let endpoint = rpc::next_http_archive_rpc_endpoint(); @@ -510,7 +510,7 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) "#]]); }); -// +// https://github.com/foundry-rs/foundry/issues/6579 forgetest_init!(include_custom_types_in_traces, |prj, cmd| { prj.wipe_contracts(); @@ -862,7 +862,7 @@ Encountered a total of 2 failing tests, 0 tests succeeded "#]]); }); -// +// https://github.com/foundry-rs/foundry/issues/7530 forgetest_init!(should_show_precompile_labels, |prj, cmd| { prj.wipe_contracts(); @@ -1239,9 +1239,9 @@ contract DeterministicRandomnessTest is Test { assert_ne!(res4, res3); }); -// tests that `pauseGasMetering` used at the end of test does not produce meaningless values -// see https://github.com/foundry-rs/foundry/issues/5491 -forgetest_init!(repro_5491, |prj, cmd| { +// Tests that `pauseGasMetering` used at the end of test does not produce meaningless values. +// https://github.com/foundry-rs/foundry/issues/5491 +forgetest_init!(gas_metering_pause_last_call, |prj, cmd| { prj.wipe_contracts(); prj.add_test( @@ -1287,8 +1287,8 @@ contract ATest is Test { "#]]); }); -// see https://github.com/foundry-rs/foundry/issues/5564 -forgetest_init!(repro_5564, |prj, cmd| { +// https://github.com/foundry-rs/foundry/issues/5564 +forgetest_init!(gas_metering_expect_revert, |prj, cmd| { prj.wipe_contracts(); prj.add_test( @@ -1318,51 +1318,79 @@ contract ATest is Test { "#]]); }); -// see https://github.com/foundry-rs/foundry/issues/4523 -forgetest_init!(repro_4523, |prj, cmd| { +// https://github.com/foundry-rs/foundry/issues/4523 +forgetest_init!(gas_metering_gasleft, |prj, cmd| { prj.wipe_contracts(); prj.add_test( "ATest.t.sol", r#" import "forge-std/Test.sol"; + contract ATest is Test { - mapping(uint => bytes32) map; + mapping(uint256 => bytes32) map; - function test_GasMeter () public { + function test_GasMeter() public { vm.pauseGasMetering(); - for (uint i = 0; i < 10000; i++) { - map[i] = keccak256(abi.encode(i)); - } + consumeGas(); vm.resumeGasMetering(); - for (uint i = 0; i < 10000; i++) { - map[i] = keccak256(abi.encode(i)); - } + consumeGas(); } - function test_GasLeft () public { - for (uint i = 0; i < 10000; i++) { - map[i] = keccak256(abi.encode(i)); - } + function test_GasLeft() public { + consumeGas(); + + uint256 start = gasleft(); + consumeGas(); + console.log("Gas cost:", start - gasleft()); + } - uint start = gasleft(); - for (uint i = 0; i < 10000; i++) { + function consumeGas() private { + for (uint256 i = 0; i < 100; i++) { map[i] = keccak256(abi.encode(i)); } - console2.log("Gas cost:", start - gasleft()); } } "#, ) .unwrap(); + // Log and test gas cost should be similar. cmd.args(["test", "-vvvv"]).with_no_redact().assert_success().stdout_eq(str![[r#" ... Logs: - Gas cost: 5754479 + Gas cost: 34468 +... +[PASS] test_GasMeter() (gas: 37512) +... +"#]]); +}); + +// https://github.com/foundry-rs/foundry/issues/4370 +forgetest_init!(repro_4370, |prj, cmd| { + prj.wipe_contracts(); + + prj.add_test( + "ATest.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; +contract ATest is Test { + uint a; + function test_negativeGas () public { + vm.pauseGasMetering(); + a = 100; + vm.resumeGasMetering(); + delete a; + } +} + "#, + ) + .unwrap(); + + cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" ... -[PASS] test_GasMeter() (gas: 5757137) +[PASS] test_negativeGas() (gas: 3252) ... "#]]); }); diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index 6c0708e20..3cce4bed2 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -9,7 +9,10 @@ use crate::{ use alloy_dyn_abi::{DecodedEvent, DynSolValue, EventExt}; use alloy_json_abi::Event; use alloy_primitives::{address, b256, Address, U256}; -use forge::{decode::decode_console_logs, result::TestStatus}; +use forge::{ + decode::decode_console_logs, + result::{TestKind, TestStatus}, +}; use foundry_config::{fs_permissions::PathPermission, Config, FsPermissions}; use foundry_evm::{ constants::HARDHAT_CONSOLE_ADDRESS, @@ -363,7 +366,15 @@ test_repro!(8287); test_repro!(8168); // https://github.com/foundry-rs/foundry/issues/8383 -test_repro!(8383); +test_repro!(8383, false, None, |res| { + let mut res = res.remove("default/repros/Issue8383.t.sol:Issue8383Test").unwrap(); + let test = res.test_results.remove("testP256VerifyOutOfBounds()").unwrap(); + assert_eq!(test.status, TestStatus::Success); + match test.kind { + TestKind::Unit { gas } => assert_eq!(gas, 3103), + _ => panic!("not a unit test kind"), + } +}); // https://github.com/foundry-rs/foundry/issues/1543 test_repro!(1543); diff --git a/testdata/default/cheats/GasMetering.t.sol b/testdata/default/cheats/GasMetering.t.sol index 54d0a7422..e439e05be 100644 --- a/testdata/default/cheats/GasMetering.t.sol +++ b/testdata/default/cheats/GasMetering.t.sol @@ -16,21 +16,21 @@ contract GasMeteringTest is DSTest { function testGasMetering() public { uint256 gas_start = gasleft(); - addInLoop(); + consumeGas(); uint256 gas_end_normal = gas_start - gasleft(); vm.pauseGasMetering(); uint256 gas_start_not_metered = gasleft(); - addInLoop(); + consumeGas(); uint256 gas_end_not_metered = gas_start_not_metered - gasleft(); vm.resumeGasMetering(); uint256 gas_start_metered = gasleft(); - addInLoop(); + consumeGas(); uint256 gas_end_resume_metered = gas_start_metered - gasleft(); @@ -76,11 +76,9 @@ contract GasMeteringTest is DSTest { assertEq(gas_end_not_metered, 0); } - function addInLoop() internal returns (uint256) { - uint256 b; + function consumeGas() internal returns (uint256 x) { for (uint256 i; i < 10000; i++) { - b + i; + x += i; } - return b; } } From d28a3377e52e6a4114a8cea2903c115b023279e8 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Tue, 27 Aug 2024 03:44:18 +0800 Subject: [PATCH 110/184] chore(deps): bump foundry-compilers (#8746) * chore(deps): bump compilers * add doc * fix --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 2 +- crates/config/src/lib.rs | 7 +++++-- crates/config/src/vyper.rs | 3 +++ crates/verify/src/provider.rs | 2 +- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f9a01ff96..fdb4214e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3691,9 +3691,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b8ffe1d5a00cd78a9461262377270d88b8d6a8a5f51b402996242bccef3994" +checksum = "7eaa24a47bb84e1db38c84f03e8c90ca81050bd20beac8bdc99aae8afd0b8784" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3729,9 +3729,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdb80803e20447fc8c3f4ec97d47ad5fa37286648bb8224edbbc553ebe1a0f4" +checksum = "3588ee6a986f89040d1158fb90459731580b404fb72b8c6c832c0ddbc95fed58" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3739,9 +3739,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3280cf657d802358856a397cb8465b18a0a6c09b1fa6422842e422a9aa21276d" +checksum = "a149c5e8c326c7bae8f73cacb28c637f4bc2e535f950eec10348494990e9636f" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3763,9 +3763,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ecc61aa540bff773d4441a94e0f158769fcedd61f61d3e91608a76d6bcd7aa" +checksum = "8645c9e7c070c81bf8c90f456416953234334f097b67445c773af98df74e27b0" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3778,9 +3778,9 @@ dependencies = [ [[package]] name = "foundry-compilers-core" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a14603a33a217e64cc38977c215b01b37b48a0cae0a739a9f9b3555f16938704" +checksum = "66492aeb708f3d142c078457dba5f52b04ca5031012d48903a0bcb37d205d595" dependencies = [ "alloy-primitives", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index 5ac49d2df..7816eb95b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -158,7 +158,7 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.5.1", default-features = false } -foundry-compilers = { version = "0.10.2", default-features = false } +foundry-compilers = { version = "0.10.3", default-features = false } foundry-fork-db = "0.2" solang-parser = "=0.3.3" diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index cedf96e6e..715a83a19 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -997,7 +997,7 @@ impl Config { /// Returns configuration for a compiler to use when setting up a [Project]. pub fn compiler(&self) -> Result { - Ok(MultiCompiler { solc: self.solc_compiler()?, vyper: self.vyper_compiler()? }) + Ok(MultiCompiler { solc: Some(self.solc_compiler()?), vyper: self.vyper_compiler()? }) } /// Returns configured [MultiCompilerSettings]. @@ -1347,6 +1347,7 @@ impl Config { "evm.deployedBytecode".to_string(), ]), search_paths: None, + experimental_codegen: self.vyper.experimental_codegen, }) } @@ -5023,6 +5024,7 @@ mod tests { [vyper] optimize = "codesize" path = "/path/to/vyper" + experimental_codegen = true "#, )?; @@ -5031,7 +5033,8 @@ mod tests { config.vyper, VyperConfig { optimize: Some(VyperOptimizationMode::Codesize), - path: Some("/path/to/vyper".into()) + path: Some("/path/to/vyper".into()), + experimental_codegen: Some(true), } ); diff --git a/crates/config/src/vyper.rs b/crates/config/src/vyper.rs index 7b2f0a54d..dbd47faec 100644 --- a/crates/config/src/vyper.rs +++ b/crates/config/src/vyper.rs @@ -12,4 +12,7 @@ pub struct VyperConfig { /// The Vyper instance to use if any. #[serde(default, skip_serializing_if = "Option::is_none")] pub path: Option, + /// Optionally enables experimental Venom pipeline + #[serde(default, skip_serializing_if = "Option::is_none")] + pub experimental_codegen: Option, } diff --git a/crates/verify/src/provider.rs b/crates/verify/src/provider.rs index 1506585ab..6e5b29d5e 100644 --- a/crates/verify/src/provider.rs +++ b/crates/verify/src/provider.rs @@ -38,7 +38,7 @@ impl VerificationContext { project.no_artifacts = true; let solc = Solc::find_or_install(&compiler_version)?; - project.compiler.solc = SolcCompiler::Specific(solc); + project.compiler.solc = Some(SolcCompiler::Specific(solc)); Ok(Self { config, project, target_name, target_path, compiler_version }) } From 26f0ab4af02804e0c09d97d75099059ce0b32e11 Mon Sep 17 00:00:00 2001 From: Priyank Makwana <117025290+PriyankMkwna@users.noreply.github.com> Date: Tue, 27 Aug 2024 14:44:54 +0530 Subject: [PATCH 111/184] Provide a shields.io badge (#8738) * Provide a Foundry shields.io badge * Updated foundry badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bdb5ad8a0..684b33556 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ ## Foundry ![Github Actions][gha-badge] [![Telegram Chat][tg-badge]][tg-url] [![Telegram Support][tg-support-badge]][tg-support-url] +![Foundry](https://img.shields.io/badge/Foundry-grey?style=flat&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAElElEQVR4nH1VUUhUaRg9984YdzBpkqR0Z210rIESIXSabEbcHgydrpNRRj00kWaztj0U1MOW0MOIbD300IvLMqBpMTGYxdoqyoRNDUESBDWwUuPugCSSsTM7u0Oj1/+efdiMcmnP2/fDd77D4f/OB6xCa2urQZbllVICYGtqanK1tLS4AdgAyAAgyzJaW1sNq/ulT4twOGw4fPiwAGDp7Ow8VV1d7bVarRWxWCw/k8mgsbExm0wmZ+Lx+M/Xr1//CcAsSVmSJH01McLhsAEAnE5nx+Tk5B/xeJxOp5N9fX2sqqqixWLhnTt36HA4GIvFGI1GU3V1df5Pe/9D1t7eHkgkEuzo6GBPT49WWloq7Ha7fujQITocDu7atUs3m83i6tWr2okTJ/jixQuePn265zPScDhskGUZe/fubXv8+DFv3rypbdiwQaxbt46RSIT79u3j0NAQb926RVVVOT4+TqvVyvz8fD0YDC5NTk6ysbHxlCRJ/5KSlAAURyKRTFNTkwAg7t69S5/Px76+Pq7GyMgI9+/fz9HRUQIQO3bsEKOjo38DsJCUJADw+/0BVVW7otHo8ps3b4yvXr3CxMQETCYTTCYTNE0DAOTl5SGXy0FRFOzZswdmsxkVFRXLNTU1xmg0+kNvb+/3AGAcGBiI7969Wwcg6urq+OTJE967d49btmzh9PT0R3WJRIKBQIDBYJBTU1NsaGggAGGz2fTe3t5fAeQZAWwuLi4uP3nypOT1emEwGFBeXo7a2losLCygoaEB/f39MJlMCIVCkCQJBw8ehNVqhcfjQXNzs1RSUiKtX7++DEAZqqqq3KFQiABYUFDAM2fOkCQXFxdJkvfv32dhYSG9Xi+vXbvG2dnZj4oDgQCLioqoKAqHhobodDq/Mc7NzUklJSUIBoOw2WzYtm0blpeXsWbNGkxMTODp06doa2vD4OAgNm7cCIvFApLQdR3nzp3Dzp078fLlSxQVFeHdu3cAgIpHjx69/zBUX5k+MDBAt9vNY8eOsbu7m6lUigcOHKDL5WImkyHJz9TGYrEcALsMIPn69esZTdMIgM+ePUNXVxdu376NsrIyuN1uXLp0CWazGcPDw3C5XFBVFWfPnkVNTQ18Pp+ezWY5MzPzO4DfAABHjhzpJslUKqVdvHiR4+PjbG9vZy6XI0kuLS0xmUxSCEGS9Pv9LC0tpdFoZGVlpSaEoM/nuwIAKx/7q5GRkb9CoZBQVVWcP3+ez58/J0mm02kODg7ywoULjMViTKfTtNvtXLt2LTdt2qTncrnlsbGxLICvSUqfrl5HJBLh1NTUkhBCJ8mFhQX29/dTVVUWFBTwwYMH1HWdly9fpqIoeiKRWJqfn2d1dXWnLMuf7zMAHD16tGd+fn7FZy2bzYrKykodAAFQVVV9cXFRkNTevn3Lubk5trS0XPnfxHE4HN8ODw+nV/yanp6mx+Ohx+P5aIMQgmNjY3/W1tZ+t5rsSwG7+fjx4/76+vrm7du32woLC00AkE6n38fj8ZmHDx/+cuPGjR8BJL8YsCtYdQIMALYqilKvKEo9APuHty+egH8A3GfFDJXmxmMAAAAASUVORK5CYII%3D&link=https%3A%2F%2Fbook.getfoundry.sh%2F) [gha-badge]: https://img.shields.io/github/actions/workflow/status/foundry-rs/foundry/test.yml?branch=master [tg-badge]: https://img.shields.io/endpoint?color=neon&logo=telegram&label=chat&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Ffoundry_rs From fa11be9f16e4ff5d969f61ffa9f57aa08679e175 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Tue, 27 Aug 2024 13:15:49 +0400 Subject: [PATCH 112/184] fix: throw error if `vm.expectEmit` is used on anonymous event (#8748) * fix: throw error on vm.expectEmit on anonymous event * add test --- crates/cheatcodes/src/inspector.rs | 4 ++-- crates/cheatcodes/src/test/expect.rs | 24 +++++++++++++++++++----- testdata/default/repros/Issue7457.t.sol | 4 ++-- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 09ced9294..a11d24bb9 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -1031,9 +1031,9 @@ impl Inspector for Cheatcodes { } } - fn log(&mut self, _interpreter: &mut Interpreter, _ecx: &mut EvmContext, log: &Log) { + fn log(&mut self, interpreter: &mut Interpreter, _ecx: &mut EvmContext, log: &Log) { if !self.expected_emits.is_empty() { - expect::handle_expect_emit(self, log); + expect::handle_expect_emit(self, log, interpreter); } // `recordLogs` diff --git a/crates/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs index b68ae700a..4592f6367 100644 --- a/crates/cheatcodes/src/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -1,7 +1,9 @@ -use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*}; +use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Error, Result, Vm::*}; use alloy_primitives::{address, hex, Address, Bytes, LogData as RawLog, U256}; use alloy_sol_types::{SolError, SolValue}; -use revm::interpreter::{return_ok, InstructionResult}; +use revm::interpreter::{ + return_ok, InstructionResult, Interpreter, InterpreterAction, InterpreterResult, +}; use spec::Vm; use std::collections::{hash_map::Entry, HashMap}; @@ -444,7 +446,11 @@ fn expect_emit( Ok(Default::default()) } -pub(crate) fn handle_expect_emit(state: &mut Cheatcodes, log: &alloy_primitives::Log) { +pub(crate) fn handle_expect_emit( + state: &mut Cheatcodes, + log: &alloy_primitives::Log, + interpreter: &mut Interpreter, +) { // Fill or check the expected emits. // We expect for emit checks to be filled as they're declared (from oldest to newest), // so we fill them and push them to the back of the queue. @@ -473,11 +479,19 @@ pub(crate) fn handle_expect_emit(state: &mut Cheatcodes, log: &alloy_primitives: let Some(expected) = &event_to_fill_or_check.log else { // Unless the caller is trying to match an anonymous event, the first topic must be // filled. - // TODO: failing this check should probably cause a warning if event_to_fill_or_check.anonymous || log.topics().first().is_some() { event_to_fill_or_check.log = Some(log.data.clone()); + state.expected_emits.push_back(event_to_fill_or_check); + } else { + interpreter.instruction_result = InstructionResult::Revert; + interpreter.next_action = InterpreterAction::Return { + result: InterpreterResult { + output: Error::encode("use vm.expectEmitAnonymous to match anonymous events"), + gas: interpreter.gas, + result: InstructionResult::Revert, + }, + }; } - state.expected_emits.push_back(event_to_fill_or_check); return }; diff --git a/testdata/default/repros/Issue7457.t.sol b/testdata/default/repros/Issue7457.t.sol index 8d9d6f075..3c4080df2 100644 --- a/testdata/default/repros/Issue7457.t.sol +++ b/testdata/default/repros/Issue7457.t.sol @@ -61,10 +61,10 @@ contract Issue7457Test is DSTest, ITarget { target.emitAnonymousEventEmpty(); } - function testFailEmitEventNonIndexed() public { + function testEmitEventNonIndexedReverts() public { vm.expectEmit(false, false, false, true); + vm.expectRevert("use vm.expectEmitAnonymous to match anonymous events"); emit AnonymousEventNonIndexed(1); - target.emitAnonymousEventNonIndexed(1); } function testEmitEventNonIndexed() public { From b97c67339cb8153dd9fa9207000e2b8561fe729d Mon Sep 17 00:00:00 2001 From: m4rio <92288535+mario-eth@users.noreply.github.com> Date: Tue, 27 Aug 2024 13:35:12 +0300 Subject: [PATCH 113/184] Update to Soldeer 0.3.0 (#8648) * Update to Soldeer 0.3.0 * added config tests and descriptive comments, refactored the soldeer config to look more explicit as well * fmt * removed soldeer config value variable * fixed tests * modified cargo lock * clipy and fmt * fmt * fmt * bumped to 0.3.1 with the hotfix for os --- Cargo.lock | 60 +++------- Cargo.toml | 3 +- crates/config/src/lib.rs | 92 ++++++++++----- crates/config/src/soldeer.rs | 57 ++++++++- crates/forge/bin/cmd/soldeer.rs | 30 +++-- crates/forge/tests/cli/config.rs | 1 + crates/forge/tests/cli/soldeer.rs | 185 ++++++++++++++++++++++-------- 7 files changed, 295 insertions(+), 133 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fdb4214e2..9278cf751 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7456,6 +7456,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "sanitize-filename" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ed72fbaf78e6f2d41744923916966c4fbe3d7c74e3037a8ee482f1115572603" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "scale-info" version = "2.11.3" @@ -7826,19 +7836,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "sha256" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18278f6a914fa3070aa316493f7d2ddfb9ac86ebc06fa3b83bffda487e9065b0" -dependencies = [ - "async-trait", - "bytes", - "hex", - "sha2", - "tokio", -] - [[package]] name = "sha3" version = "0.10.8" @@ -7946,15 +7943,6 @@ dependencies = [ "similar", ] -[[package]] -name = "simple-home-dir" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c221cbc8c1ff6bdf949b12cc011456c510ec6840654b444c7374c78e928ce344" -dependencies = [ - "windows-sys 0.52.0", -] - [[package]] name = "simple_asn1" version = "0.6.2" @@ -8039,30 +8027,29 @@ dependencies = [ [[package]] name = "soldeer" -version = "0.2.19" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d584c27ebf7ad3e2557d029a07b19fa0d192d67bd73eea1e1695936eb5666e34" +checksum = "6191e16cbc3b4ed14cafc642bc2b7f966eddb7c1f3a9f60549f6a1e526016127" dependencies = [ "chrono", "clap", + "const-hex", "email-address-parser", "futures", - "once_cell", + "home", + "ignore", "regex", "reqwest", "rpassword", + "sanitize-filename", "serde", - "serde_derive", "serde_json", - "sha256", - "simple-home-dir", + "sha2", + "thiserror", "tokio", - "toml 0.8.19", "toml_edit 0.22.20", "uuid 1.10.0", - "walkdir", "yansi", - "yash-fnmatch", "zip 2.2.0", "zip-extract", ] @@ -9688,17 +9675,6 @@ dependencies = [ "is-terminal", ] -[[package]] -name = "yash-fnmatch" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697c20b479d2e6419e9a073bfdd20e90cbd8540d6c683ee46712e13de650e54f" -dependencies = [ - "regex", - "regex-syntax 0.8.4", - "thiserror", -] - [[package]] name = "zerocopy" version = "0.7.35" diff --git a/Cargo.toml b/Cargo.toml index 7816eb95b..c6c8aee07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -258,8 +258,7 @@ reqwest = { version = "0.12", default-features = false } tower = "0.4" tower-http = "0.5" # soldeer -soldeer = "0.2.19" - +soldeer = "0.3.1" proptest = "1" comfy-table = "7" diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 715a83a19..13dfc0474 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -107,7 +107,7 @@ mod inline; pub use inline::{validate_profiles, InlineConfig, InlineConfigError, InlineConfigParser, NatSpec}; pub mod soldeer; -use soldeer::SoldeerConfig; +use soldeer::{SoldeerConfig, SoldeerDependencyConfig}; mod vyper; use vyper::VyperConfig; @@ -422,7 +422,10 @@ pub struct Config { pub vyper: VyperConfig, /// Soldeer dependencies - pub dependencies: Option, + pub dependencies: Option, + + /// Soldeer custom configs + pub soldeer: Option, /// The root path where the config detection started from, [`Config::with_root`]. // We're skipping serialization here, so it won't be included in the [`Config::to_string()`] @@ -498,6 +501,7 @@ impl Config { "invariant", "labels", "dependencies", + "soldeer", "vyper", "bind_json", ]; @@ -892,7 +896,7 @@ impl Config { if self.offline { return Err(SolcError::msg(format!( "can't install missing solc {version} in offline mode" - ))) + ))); } Solc::blocking_install(version)? } @@ -902,12 +906,12 @@ impl Config { return Err(SolcError::msg(format!( "`solc` {} does not exist", solc.display() - ))) + ))); } Solc::new(solc)? } }; - return Ok(Some(solc)) + return Ok(Some(solc)); } Ok(None) @@ -925,7 +929,7 @@ impl Config { /// `auto_detect_solc` pub fn is_auto_detect(&self) -> bool { if self.solc.is_some() { - return false + return false; } self.auto_detect_solc } @@ -984,7 +988,7 @@ impl Config { pub fn vyper_compiler(&self) -> Result, SolcError> { // Only instantiate Vyper if there are any Vyper files in the project. if self.project_paths::().input_files_iter().next().is_none() { - return Ok(None) + return Ok(None); } let vyper = if let Some(path) = &self.vyper.path { Some(Vyper::new(path)?) @@ -1166,7 +1170,7 @@ impl Config { ) -> Result, EtherscanConfigError> { if let Some(maybe_alias) = self.etherscan_api_key.as_ref().or(self.eth_rpc_url.as_ref()) { if self.etherscan.contains_key(maybe_alias) { - return self.etherscan.clone().resolved().remove(maybe_alias).transpose() + return self.etherscan.clone().resolved().remove(maybe_alias).transpose(); } } @@ -1180,7 +1184,7 @@ impl Config { // we update the key, because if an etherscan_api_key is set, it should take // precedence over the entry, since this is usually set via env var or CLI args. config.key.clone_from(key); - return Ok(Some(config)) + return Ok(Some(config)); } (Ok(config), None) => return Ok(Some(config)), (Err(err), None) => return Err(err), @@ -1193,7 +1197,7 @@ impl Config { // etherscan fallback via API key if let Some(key) = self.etherscan_api_key.as_ref() { let chain = chain.or(self.chain).unwrap_or_default(); - return Ok(ResolvedEtherscanConfig::create(key, chain)) + return Ok(ResolvedEtherscanConfig::create(key, chain)); } Ok(None) @@ -1478,7 +1482,7 @@ impl Config { { let file_path = self.get_config_path(); if !file_path.exists() { - return Ok(()) + return Ok(()); } let contents = fs::read_to_string(&file_path)?; let mut doc = contents.parse::()?; @@ -1640,14 +1644,14 @@ impl Config { return match path.is_file() { true => Some(path.to_path_buf()), false => None, - } + }; } let cwd = std::env::current_dir().ok()?; let mut cwd = cwd.as_path(); loop { let file_path = cwd.join(path); if file_path.is_file() { - return Some(file_path) + return Some(file_path); } cwd = cwd.parent()?; } @@ -1721,7 +1725,7 @@ impl Config { if let Some(cache_dir) = Self::foundry_rpc_cache_dir() { let mut cache = Cache { chains: vec![] }; if !cache_dir.exists() { - return Ok(cache) + return Ok(cache); } if let Ok(entries) = cache_dir.as_path().read_dir() { for entry in entries.flatten().filter(|x| x.path().is_dir()) { @@ -1765,7 +1769,7 @@ impl Config { fn get_cached_blocks(chain_path: &Path) -> eyre::Result> { let mut blocks = vec![]; if !chain_path.exists() { - return Ok(blocks) + return Ok(blocks); } for block in chain_path.read_dir()?.flatten() { let file_type = block.file_type()?; @@ -1777,7 +1781,7 @@ impl Config { { block.path() } else { - continue + continue; }; blocks.push((file_name.to_string_lossy().into_owned(), fs::metadata(filepath)?.len())); } @@ -1787,7 +1791,7 @@ impl Config { /// The path provided to this function should point to the etherscan cache for a chain. fn get_cached_block_explorer_data(chain_path: &Path) -> eyre::Result { if !chain_path.exists() { - return Ok(0) + return Ok(0); } fn dir_size_recursive(mut dir: fs::ReadDir) -> eyre::Result { @@ -1963,7 +1967,7 @@ pub(crate) mod from_opt_glob { { let s: Option = Option::deserialize(deserializer)?; if let Some(s) = s { - return Ok(Some(globset::Glob::new(&s).map_err(serde::de::Error::custom)?)) + return Ok(Some(globset::Glob::new(&s).map_err(serde::de::Error::custom)?)); } Ok(None) } @@ -2141,6 +2145,7 @@ impl Default for Config { create2_library_salt: Self::DEFAULT_CREATE2_LIBRARY_SALT, skip: vec![], dependencies: Default::default(), + soldeer: Default::default(), assertions_revert: true, legacy_assertions: false, warnings: vec![], @@ -2247,7 +2252,7 @@ impl TomlFileProvider { if let Some(file) = self.env_val() { let path = Path::new(&file); if !path.exists() { - return true + return true; } } false @@ -2267,7 +2272,7 @@ impl TomlFileProvider { "Config file `{}` set in env var `{}` does not exist", file, self.env_var.unwrap() - ))) + ))); } Toml::file(file) } else { @@ -2311,7 +2316,7 @@ impl Provider for ForcedSnakeCaseData

{ if Config::STANDALONE_SECTIONS.contains(&profile.as_ref()) { // don't force snake case for keys in standalone sections map.insert(profile, dict); - continue + continue; } map.insert(profile, dict.into_iter().map(|(k, v)| (k.to_snake_case(), v)).collect()); } @@ -2451,7 +2456,7 @@ impl Provider for DappEnvCompatProvider { if val > 1 { return Err( format!("Invalid $DAPP_BUILD_OPTIMIZE value `{val}`, expected 0 or 1").into() - ) + ); } dict.insert("optimizer".to_string(), (val == 1).into()); } @@ -2517,7 +2522,7 @@ impl Provider for RenameProfileProvider

{ fn data(&self) -> Result, Error> { let mut data = self.provider.data()?; if let Some(data) = data.remove(&self.from) { - return Ok(Map::from([(self.to.clone(), data)])) + return Ok(Map::from([(self.to.clone(), data)])); } Ok(Default::default()) } @@ -2563,7 +2568,7 @@ impl Provider for UnwrapProfileProvider

{ for (profile_str, profile_val) in profiles { let profile = Profile::new(&profile_str); if profile != self.profile { - continue + continue; } match profile_val { Value::Dict(_, dict) => return Ok(profile.collect(dict)), @@ -2574,7 +2579,7 @@ impl Provider for UnwrapProfileProvider

{ )); err.metadata = Some(self.provider.metadata()); err.profile = Some(self.profile.clone()); - return Err(err) + return Err(err); } } } @@ -2686,7 +2691,7 @@ impl Provider for OptionalStrictProfileProvider

{ // provider and can't map the metadata to the error. Therefor we return the root error // if this error originated in the provider's data. if let Err(root_err) = self.provider.data() { - return root_err + return root_err; } err }) @@ -2814,6 +2819,7 @@ mod tests { vyper::VyperOptimizationMode, ModelCheckerEngine, YulDetails, }; use similar_asserts::assert_eq; + use soldeer::RemappingsLocation; use std::{collections::BTreeMap, fs::File, io::Write}; use tempfile::tempdir; use NamedChain::Moonbeam; @@ -5041,4 +5047,38 @@ mod tests { Ok(()) }); } + + #[test] + fn test_parse_soldeer() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [soldeer] + remappings_generate = true + remappings_regenerate = false + remappings_version = true + remappings_prefix = "@" + remappings_location = "txt" + recursive_deps = true + "#, + )?; + + let config = Config::load(); + + assert_eq!( + config.soldeer, + Some(SoldeerConfig { + remappings_generate: true, + remappings_regenerate: false, + remappings_version: true, + remappings_prefix: "@".to_string(), + remappings_location: RemappingsLocation::Txt, + recursive_deps: true, + }) + ); + + Ok(()) + }); + } } diff --git a/crates/config/src/soldeer.rs b/crates/config/src/soldeer.rs index 3bb8e9a3b..75ee96c2c 100644 --- a/crates/config/src/soldeer.rs +++ b/crates/config/src/soldeer.rs @@ -21,9 +21,9 @@ pub struct MapDependency { /// Type for Soldeer configs, under dependencies tag in the foundry.toml #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct SoldeerConfig(BTreeMap); +pub struct SoldeerDependencyConfig(BTreeMap); -impl AsRef for SoldeerConfig { +impl AsRef for SoldeerDependencyConfig { fn as_ref(&self) -> &Self { self } @@ -39,3 +39,56 @@ pub enum SoldeerDependencyValue { Map(MapDependency), Str(String), } + +/// Location where to store the remappings, either in `remappings.txt` or in the `foundry.toml` +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Default)] +#[serde(rename_all = "lowercase")] +pub enum RemappingsLocation { + #[default] + Txt, + Config, +} + +fn default_true() -> bool { + true +} + +/// Type for Soldeer configs, under soldeer tag in the foundry.toml +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct SoldeerConfig { + #[serde(default = "default_true")] + pub remappings_generate: bool, + + #[serde(default)] + pub remappings_regenerate: bool, + + #[serde(default = "default_true")] + pub remappings_version: bool, + + #[serde(default)] + pub remappings_prefix: String, + + #[serde(default)] + pub remappings_location: RemappingsLocation, + + #[serde(default)] + pub recursive_deps: bool, +} + +impl AsRef for SoldeerConfig { + fn as_ref(&self) -> &Self { + self + } +} +impl Default for SoldeerConfig { + fn default() -> Self { + Self { + remappings_generate: true, + remappings_regenerate: false, + remappings_version: true, + remappings_prefix: String::new(), + remappings_location: Default::default(), + recursive_deps: false, + } + } +} diff --git a/crates/forge/bin/cmd/soldeer.rs b/crates/forge/bin/cmd/soldeer.rs index 9c8579fe7..5482bdc6f 100644 --- a/crates/forge/bin/cmd/soldeer.rs +++ b/crates/forge/bin/cmd/soldeer.rs @@ -4,15 +4,23 @@ use eyre::Result; use soldeer::commands::Subcommands; // CLI arguments for `forge soldeer`. +// The following list of commands and their actions: +// +// forge soldeer install: looks up the config file and install all the dependencies that are present +// there forge soldeer install package~version: looks up on https://soldeer.xyz and if the package>version is there then add to config+lockfile and install new dependency. Replaces existing entry if version is different. +// forge soldeer install package~version url: same behavior as install but instead of looking at https://soldeer.xyz it choses the URL, which can be git or custom zip url +// forge soldeer update: same behavior as install looks up the config file and install all the +// dependencies that are present there. This will change in the future forge soldeer login: logs in into https://soldeer.xyz account +// forge soldeer push package~version: pushes files to the central repository +// forge soldeer version: checks soldeer version +// forge soldeer init: initializes a new project with minimal dependency for foundry setup, install +// latest forge-std version forge soldeer uninstall dependency: uninstalls a dependency, removes +// artifacts and configs + #[derive(Clone, Debug, Parser)] -#[clap(override_usage = "forge soldeer install [DEPENDENCY]~[VERSION] - forge soldeer install [DEPENDENCY]~[VERSION] - forge soldeer install [DEPENDENCY]~[VERSION] --rev - forge soldeer install [DEPENDENCY]~[VERSION] --rev - forge soldeer push [DEPENDENCY]~[VERSION] - forge soldeer login - forge soldeer update - forge soldeer version")] +#[clap( + override_usage = "Native Solidity Package Manager, `run forge soldeer [COMMAND] --help` for more details" +)] pub struct SoldeerArgs { /// Command must be one of the following install/push/login/update/version. #[command(subcommand)] @@ -23,7 +31,7 @@ impl SoldeerArgs { pub fn run(self) -> Result<()> { match soldeer::run(self.command) { Ok(_) => Ok(()), - Err(err) => Err(eyre::eyre!("Failed to run soldeer {}", err.message)), + Err(err) => Err(eyre::eyre!("Failed to run soldeer {}", err)), } } } @@ -31,10 +39,10 @@ impl SoldeerArgs { #[cfg(test)] mod tests { use super::*; - use soldeer::commands::VersionDryRun; + use soldeer::commands::Version; #[test] fn test_soldeer_version() { - assert!(soldeer::run(Subcommands::VersionDryRun(VersionDryRun {})).is_ok()); + assert!(soldeer::run(Subcommands::Version(Version {})).is_ok()); } } diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index e943001af..88ac186b2 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -143,6 +143,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { vyper: Default::default(), skip: vec![], dependencies: Default::default(), + soldeer: Default::default(), warnings: vec![], assertions_revert: true, legacy_assertions: false, diff --git a/crates/forge/tests/cli/soldeer.rs b/crates/forge/tests/cli/soldeer.rs index 1a62578d1..bacf47230 100644 --- a/crates/forge/tests/cli/soldeer.rs +++ b/crates/forge/tests/cli/soldeer.rs @@ -24,16 +24,12 @@ forgesoldeer!(install_dependency, |prj, cmd| { // Making sure the lock contents are the right ones let path_lock_file = prj.root().join("soldeer.lock"); - let lock_contents = r#" -[[dependencies]] -name = "forge-std" -version = "1.8.1" -source = "https://soldeer-revisions.s3.amazonaws.com/forge-std/v1_8_1_23-03-2024_00:05:44_forge-std-v1.8.1.zip" -checksum = "0f7cd44f5670c31a9646d4031e70c66321cd3ed6ebac3c7278e4e57e4e5c5bd0" -"#; let actual_lock_contents = read_file_to_string(&path_lock_file); - assert_eq!(lock_contents, actual_lock_contents); + assert!(actual_lock_contents.contains("forge-std")); + assert!(actual_lock_contents + .contains("0f7cd44f5670c31a9646d4031e70c66321cd3ed6ebac3c7278e4e57e4e5c5bd0")); + assert!(actual_lock_contents.contains("1.8.1")); // Making sure the foundry contents are the right ones let foundry_contents = r#"[profile.default] @@ -41,10 +37,10 @@ src = "src" out = "out" libs = ["lib"] -# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options - [dependencies] forge-std = "1.8.1" + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options "#; assert_data_eq!(read_file_to_string(&foundry_file), foundry_contents); @@ -53,7 +49,7 @@ forge-std = "1.8.1" forgesoldeer!(install_dependency_git, |prj, cmd| { let command = "install"; let dependency = "forge-std~1.8.1"; - let git = "git@gitlab.com:mario4582928/Mario.git"; + let git = "https://gitlab.com/mario4582928/Mario.git"; let foundry_file = prj.root().join("foundry.toml"); @@ -67,16 +63,11 @@ forgesoldeer!(install_dependency_git, |prj, cmd| { // Making sure the lock contents are the right ones let path_lock_file = prj.root().join("soldeer.lock"); - let lock_contents = r#" -[[dependencies]] -name = "forge-std" -version = "1.8.1" -source = "git@gitlab.com:mario4582928/Mario.git" -checksum = "22868f426bd4dd0e682b5ec5f9bd55507664240c" -"#; let actual_lock_contents = read_file_to_string(&path_lock_file); - assert_eq!(lock_contents, actual_lock_contents); + assert!(actual_lock_contents.contains("forge-std")); + assert!(actual_lock_contents.contains("22868f426bd4dd0e682b5ec5f9bd55507664240c")); + assert!(actual_lock_contents.contains("1.8.1")); // Making sure the foundry contents are the right ones let foundry_contents = r#"[profile.default] @@ -84,10 +75,10 @@ src = "src" out = "out" libs = ["lib"] -# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options - [dependencies] -forge-std = { version = "1.8.1", git = "git@gitlab.com:mario4582928/Mario.git", rev = "22868f426bd4dd0e682b5ec5f9bd55507664240c" } +forge-std = { version = "1.8.1", git = "https://gitlab.com/mario4582928/Mario.git", rev = "22868f426bd4dd0e682b5ec5f9bd55507664240c" } + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options "#; assert_data_eq!(read_file_to_string(&foundry_file), foundry_contents); @@ -96,7 +87,7 @@ forge-std = { version = "1.8.1", git = "git@gitlab.com:mario4582928/Mario.git", forgesoldeer!(install_dependency_git_commit, |prj, cmd| { let command = "install"; let dependency = "forge-std~1.8.1"; - let git = "git@gitlab.com:mario4582928/Mario.git"; + let git = "https://gitlab.com/mario4582928/Mario.git"; let rev_flag = "--rev"; let commit = "7a0663eaf7488732f39550be655bad6694974cb3"; @@ -113,16 +104,12 @@ forgesoldeer!(install_dependency_git_commit, |prj, cmd| { // Making sure the lock contents are the right ones let path_lock_file = prj.root().join("soldeer.lock"); - let lock_contents = r#" -[[dependencies]] -name = "forge-std" -version = "1.8.1" -source = "git@gitlab.com:mario4582928/Mario.git" -checksum = "7a0663eaf7488732f39550be655bad6694974cb3" -"#; let actual_lock_contents = read_file_to_string(&path_lock_file); - assert_eq!(lock_contents, actual_lock_contents); + assert!(actual_lock_contents.contains("forge-std")); + assert!(actual_lock_contents.contains("7a0663eaf7488732f39550be655bad6694974cb3")); + assert!(actual_lock_contents.contains("https://gitlab.com/mario4582928/Mario.git")); + assert!(actual_lock_contents.contains("1.8.1")); // Making sure the foundry contents are the right ones let foundry_contents = r#"[profile.default] @@ -130,10 +117,10 @@ src = "src" out = "out" libs = ["lib"] -# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options - [dependencies] -forge-std = { version = "1.8.1", git = "git@gitlab.com:mario4582928/Mario.git", rev = "7a0663eaf7488732f39550be655bad6694974cb3" } +forge-std = { version = "1.8.1", git = "https://gitlab.com/mario4582928/Mario.git", rev = "7a0663eaf7488732f39550be655bad6694974cb3" } + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options "#; assert_data_eq!(read_file_to_string(&foundry_file), foundry_contents); @@ -145,7 +132,12 @@ forgesoldeer!(update_dependencies, |prj, cmd| { // We need to write this into the foundry.toml to make the update install the dependency let foundry_updates = r#" [dependencies] +"@tt" = {version = "1.6.1", url = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts/3_3_0-rc_2_22-01-2024_13:12:57_contracts.zip"} forge-std = { version = "1.8.1" } +solmate = "6.7.0" +mario = { version = "1.0", git = "https://gitlab.com/mario4582928/Mario.git", rev = "22868f426bd4dd0e682b5ec5f9bd55507664240c" } +mario-custom-tag = { version = "1.0", git = "https://gitlab.com/mario4582928/Mario.git", tag = "custom-tag" } +mario-custom-branch = { version = "1.0", git = "https://gitlab.com/mario4582928/Mario.git", tag = "custom-branch" } "#; let foundry_file = prj.root().join("foundry.toml"); @@ -166,16 +158,20 @@ forge-std = { version = "1.8.1" } // Making sure the lock contents are the right ones let path_lock_file = prj.root().join("soldeer.lock"); - let lock_contents = r#" -[[dependencies]] -name = "forge-std" -version = "1.8.1" -source = "https://soldeer-revisions.s3.amazonaws.com/forge-std/v1_8_1_23-03-2024_00:05:44_forge-std-v1.8.1.zip" -checksum = "0f7cd44f5670c31a9646d4031e70c66321cd3ed6ebac3c7278e4e57e4e5c5bd0" -"#; + let dep1 = prj.root().join("dependencies").join("@tt-1.6.1"); + let dep2 = prj.root().join("dependencies").join("forge-std-1.8.1"); + let dep3 = prj.root().join("dependencies").join("mario-1.0"); + let dep4 = prj.root().join("dependencies").join("solmate-6.7.0"); + let dep5 = prj.root().join("dependencies").join("mario-custom-tag-1.0"); + let dep6 = prj.root().join("dependencies").join("mario-custom-branch-1.0"); let actual_lock_contents = read_file_to_string(&path_lock_file); - assert_eq!(lock_contents, actual_lock_contents); + assert!(actual_lock_contents.contains("@tt")); + assert!(actual_lock_contents.contains("forge-std")); + assert!(actual_lock_contents.contains("mario")); + assert!(actual_lock_contents.contains("solmate")); + assert!(actual_lock_contents.contains("mario-custom-tag")); + assert!(actual_lock_contents.contains("mario-custom-branch")); // Making sure the foundry contents are the right ones let foundry_contents = r#"[profile.default] @@ -186,10 +182,21 @@ libs = ["lib"] # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options [dependencies] +"@tt" = {version = "1.6.1", url = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts/3_3_0-rc_2_22-01-2024_13:12:57_contracts.zip"} forge-std = { version = "1.8.1" } +solmate = "6.7.0" +mario = { version = "1.0", git = "https://gitlab.com/mario4582928/Mario.git", rev = "22868f426bd4dd0e682b5ec5f9bd55507664240c" } +mario-custom-tag = { version = "1.0", git = "https://gitlab.com/mario4582928/Mario.git", tag = "custom-tag" } +mario-custom-branch = { version = "1.0", git = "https://gitlab.com/mario4582928/Mario.git", tag = "custom-branch" } "#; assert_data_eq!(read_file_to_string(&foundry_file), foundry_contents); + assert!(dep1.exists()); + assert!(dep2.exists()); + assert!(dep3.exists()); + assert!(dep4.exists()); + assert!(dep5.exists()); + assert!(dep6.exists()); }); forgesoldeer!(update_dependencies_simple_version, |prj, cmd| { @@ -220,16 +227,12 @@ forge-std = "1.8.1" // Making sure the lock contents are the right ones let path_lock_file = prj.root().join("soldeer.lock"); - let lock_contents = r#" -[[dependencies]] -name = "forge-std" -version = "1.8.1" -source = "https://soldeer-revisions.s3.amazonaws.com/forge-std/v1_8_1_23-03-2024_00:05:44_forge-std-v1.8.1.zip" -checksum = "0f7cd44f5670c31a9646d4031e70c66321cd3ed6ebac3c7278e4e57e4e5c5bd0" -"#; let actual_lock_contents = read_file_to_string(&path_lock_file); - assert_eq!(lock_contents, actual_lock_contents); + assert!(actual_lock_contents.contains("forge-std")); + assert!(actual_lock_contents + .contains("0f7cd44f5670c31a9646d4031e70c66321cd3ed6ebac3c7278e4e57e4e5c5bd0")); + assert!(actual_lock_contents.contains("1.8.1")); // Making sure the foundry contents are the right ones let foundry_contents = r#"[profile.default] @@ -257,6 +260,88 @@ forgesoldeer!(login, |prj, cmd| { assert!(stdout.contains("Please enter your email")); }); +forgesoldeer!(install_dependency_with_remappings_config, |prj, cmd| { + let command = "install"; + let dependency = "forge-std~1.8.1"; + let foundry_updates = r#" +[soldeer] +remappings_generate = true +remappings_prefix = "@custom-f@" +remappings_location = "config" +remappings_regenerate = true +"#; + let foundry_file = prj.root().join("foundry.toml"); + let mut file = OpenOptions::new().append(true).open(&foundry_file).unwrap(); + + if let Err(e) = write!(file, "{foundry_updates}") { + eprintln!("Couldn't write to file: {e}"); + } + + cmd.arg("soldeer").args([command, dependency]); + cmd.execute(); + + // Making sure the path was created to the dependency and that foundry.toml exists + // meaning that the dependencies were installed correctly + let path_dep_forge = + prj.root().join("dependencies").join("forge-std-1.8.1").join("foundry.toml"); + assert!(path_dep_forge.exists()); + + // Making sure the foundry contents are the right ones + let foundry_contents = r#"[profile.default] +src = "src" +out = "out" +libs = ["lib"] +remappings = ["@custom-f@forge-std-1.8.1/=dependencies/forge-std-1.8.1/"] + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options + +[soldeer] +remappings_generate = true +remappings_prefix = "@custom-f@" +remappings_location = "config" +remappings_regenerate = true + +[dependencies] +forge-std = "1.8.1" +"#; + + assert_data_eq!(read_file_to_string(&foundry_file), foundry_contents); +}); + +forgesoldeer!(install_dependency_with_remappings_txt, |prj, cmd| { + let command = "install"; + let dependency = "forge-std~1.8.1"; + let foundry_updates = r#" +[soldeer] +remappings_generate = true +remappings_prefix = "@custom-f@" +remappings_location = "txt" +remappings_regenerate = true +"#; + let foundry_file = prj.root().join("foundry.toml"); + let mut file = OpenOptions::new().append(true).open(&foundry_file).unwrap(); + + if let Err(e) = write!(file, "{foundry_updates}") { + eprintln!("Couldn't write to file: {e}"); + } + + cmd.arg("soldeer").args([command, dependency]); + cmd.execute(); + + // Making sure the path was created to the dependency and that foundry.toml exists + // meaning that the dependencies were installed correctly + let path_dep_forge = + prj.root().join("dependencies").join("forge-std-1.8.1").join("foundry.toml"); + assert!(path_dep_forge.exists()); + + // Making sure the foundry contents are the right ones + let remappings_content = "@custom-f@forge-std-1.8.1/=dependencies/forge-std-1.8.1/\n"; + let remappings_file = prj.root().join("remappings.txt"); + println!("ddd {:?}", read_file_to_string(&remappings_file)); + + assert_data_eq!(read_file_to_string(&remappings_file), remappings_content); +}); + fn read_file_to_string(path: &Path) -> String { let contents: String = fs::read_to_string(path).unwrap_or_default(); contents From 5d2ac1ad0682f8172fec7348802d62344cb562bd Mon Sep 17 00:00:00 2001 From: bernard-wagner Date: Tue, 27 Aug 2024 15:46:33 +0200 Subject: [PATCH 114/184] fix(anvil): backwards compatible dumps (#8752) * fix(anvil): backwards compatible state * regression tests * fmt --------- Co-authored-by: Matthias Seitz --- crates/anvil/src/eth/backend/db.rs | 33 +++++++++++++++++-- crates/anvil/test-data/state-dump-legacy.json | 1 + crates/anvil/test-data/state-dump.json | 1 + crates/anvil/tests/it/state.rs | 21 ++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 crates/anvil/test-data/state-dump-legacy.json create mode 100644 crates/anvil/test-data/state-dump.json diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index e6d9541f3..cf8f1f5c9 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -6,7 +6,7 @@ use alloy_primitives::{keccak256, Address, Bytes, B256, U256, U64}; use alloy_rpc_types::BlockId; use anvil_core::eth::{ block::Block, - transaction::{MaybeImpersonatedTransaction, TransactionInfo, TypedReceipt}, + transaction::{MaybeImpersonatedTransaction, TransactionInfo, TypedReceipt, TypedTransaction}, }; use foundry_common::errors::FsPathError; use foundry_evm::{ @@ -359,10 +359,24 @@ pub struct SerializableAccountRecord { pub storage: BTreeMap, } +/// Defines a backwards-compatible enum for transactions. +/// This is essential for maintaining compatibility with state dumps +/// created before the changes introduced in PR #8411. +/// +/// The enum can represent either a `TypedTransaction` or a `MaybeImpersonatedTransaction`, +/// depending on the data being deserialized. This flexibility ensures that older state +/// dumps can still be loaded correctly, even after the changes in #8411. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum SerializableTransactionType { + TypedTransaction(TypedTransaction), + MaybeImpersonatedTransaction(MaybeImpersonatedTransaction), +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct SerializableBlock { pub header: Header, - pub transactions: Vec, + pub transactions: Vec, pub ommers: Vec

, } @@ -386,6 +400,21 @@ impl From for Block { } } +impl From for SerializableTransactionType { + fn from(transaction: MaybeImpersonatedTransaction) -> Self { + Self::MaybeImpersonatedTransaction(transaction) + } +} + +impl From for MaybeImpersonatedTransaction { + fn from(transaction: SerializableTransactionType) -> Self { + match transaction { + SerializableTransactionType::TypedTransaction(tx) => Self::new(tx), + SerializableTransactionType::MaybeImpersonatedTransaction(tx) => tx, + } + } +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct SerializableTransaction { pub info: TransactionInfo, diff --git a/crates/anvil/test-data/state-dump-legacy.json b/crates/anvil/test-data/state-dump-legacy.json new file mode 100644 index 000000000..45906791d --- /dev/null +++ b/crates/anvil/test-data/state-dump-legacy.json @@ -0,0 +1 @@ +{"block":{"number":"0x2","coinbase":"0x0000000000000000000000000000000000000000","timestamp":"0x66cdc823","gas_limit":"0x1c9c380","basefee":"0x342a1c58","difficulty":"0x0","prevrandao":"0xb92480171c0235f8c6710a4047d7ee14a3be58c630839fb4422826ff3a013e44","blob_excess_gas_and_price":{"excess_blob_gas":0,"blob_gasprice":1}},"accounts":{"0x0000000000000000000000000000000000000000":{"nonce":0,"balance":"0xa410","code":"0x","storage":{}},"0x14dc79964da2c08b23698b3d3cc7ca32193d9955":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x15d34aaf54267db7d7c367839aaf71a00a2c6a65":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x4e59b44847b379578588920ca78fbf26c0b4956c":{"nonce":0,"balance":"0x0","code":"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3","storage":{}},"0x70997970c51812dc3a010c7d01b50e0d17dc79c8":{"nonce":1,"balance":"0x21e19e0b90393da9b38","code":"0x","storage":{}},"0x90f79bf6eb2c4f870365e785982e1f101e93b906":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x976ea74026e726554db657fa54763abd0c3a0aa9":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xa0ee7a142d267c1f36714e4a8f75612f20a79720":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266":{"nonce":1,"balance":"0x21e19e0b6a140b55df8","code":"0x","storage":{}}},"best_block_number":"0x2","blocks":[{"header":{"parentHash":"0xceb0fe420d6f14a8eeec4319515b89acbb0bb4861cad9983d529ab4b1e4af929","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0xe1423fd180478ab4fd05a7103277d64496b15eb914ecafe71eeec871b552efd1","transactionsRoot":"0x2b5598ef261e5f88e4303bb2b3986b3d5c0ebf4cd9977daebccae82a6469b988","receiptsRoot":"0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x2","gasLimit":"0x1c9c380","gasUsed":"0x5208","timestamp":"0x66cdc823","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x342a1c58","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"EIP1559":{"chainId":"0x7a69","nonce":"0x0","gasLimit":"0x5209","maxFeePerGas":"0x77359401","maxPriorityFeePerGas":"0x1","to":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","value":"0x0","accessList":[],"input":"0x","r":"0x85c2794a580da137e24ccc823b45ae5cea99371ae23ee13860fcc6935f8305b0","s":"0x41de7fa4121dab284af4453d30928241208bafa90cdb701fe9bc7054759fe3cd","yParity":"0x0","hash":"0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515"}}],"ommers":[]},{"header":{"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x0","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66cdc80e","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x3b9aca00","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0xa00dc0c9ee9a888e67ea32d8772f8cc28eff62448c9ec985ee941fcbc921ba59","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x6e5f60b37eeaece7dedfc42cc394731a0ae3ed3d3be93c402780b2e23e141175","transactionsRoot":"0x9ceaeb1b16b924afbf4bf4df4c2c49dc9cfbe23ac7a40bf26a704158ea2d352f","receiptsRoot":"0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x1","gasLimit":"0x1c9c380","gasUsed":"0x5208","timestamp":"0x66cdc814","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x3b9aca00","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"EIP1559":{"chainId":"0x7a69","nonce":"0x0","gasLimit":"0x5209","maxFeePerGas":"0x77359401","maxPriorityFeePerGas":"0x1","to":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","value":"0x0","accessList":[],"input":"0x","r":"0x703a4b4d6dbff2fa2345df73263df2098faa7214863b5ec82c4c07162d87b853","s":"0x17dea762c4ce600ad1d9d2c9ae6dd35b9e526d03c875f868ad0792fd4fad72e0","yParity":"0x0","hash":"0xf8d5fb22350f52ae8c30cd7f6969eb73de849c8dc010f4215d4c5c24824fe2b3"}}],"ommers":[]}]} \ No newline at end of file diff --git a/crates/anvil/test-data/state-dump.json b/crates/anvil/test-data/state-dump.json new file mode 100644 index 000000000..159f30d21 --- /dev/null +++ b/crates/anvil/test-data/state-dump.json @@ -0,0 +1 @@ +{"block":{"number":"0x2","coinbase":"0x0000000000000000000000000000000000000000","timestamp":"0x66cdcc2b","gas_limit":"0x1c9c380","basefee":"0x342a1c58","difficulty":"0x0","prevrandao":"0xdb639d7f8af4f0ff2aa9cc49861820e72f5f8bfeeed677d1e3569f6b1625df4a","blob_excess_gas_and_price":{"excess_blob_gas":0,"blob_gasprice":1}},"accounts":{"0x0000000000000000000000000000000000000000":{"nonce":0,"balance":"0xa410","code":"0x","storage":{}},"0x14dc79964da2c08b23698b3d3cc7ca32193d9955":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x15d34aaf54267db7d7c367839aaf71a00a2c6a65":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x4e59b44847b379578588920ca78fbf26c0b4956c":{"nonce":0,"balance":"0x0","code":"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3","storage":{}},"0x70997970c51812dc3a010c7d01b50e0d17dc79c8":{"nonce":1,"balance":"0x21e19e0b90393da9b38","code":"0x","storage":{}},"0x90f79bf6eb2c4f870365e785982e1f101e93b906":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x976ea74026e726554db657fa54763abd0c3a0aa9":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xa0ee7a142d267c1f36714e4a8f75612f20a79720":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266":{"nonce":1,"balance":"0x21e19e0b6a140b55df8","code":"0x","storage":{}}},"best_block_number":"0x2","blocks":[{"header":{"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x0","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66cdcc25","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x3b9aca00","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0x3a52101c98a4319c419681131d3585d70a6cf13a9af25136be20d451eed5480a","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x6e5f60b37eeaece7dedfc42cc394731a0ae3ed3d3be93c402780b2e23e141175","transactionsRoot":"0x9ceaeb1b16b924afbf4bf4df4c2c49dc9cfbe23ac7a40bf26a704158ea2d352f","receiptsRoot":"0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x1","gasLimit":"0x1c9c380","gasUsed":"0x5208","timestamp":"0x66cdcc29","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x3b9aca00","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"transaction":{"EIP1559":{"chainId":"0x7a69","nonce":"0x0","gasLimit":"0x5209","maxFeePerGas":"0x77359401","maxPriorityFeePerGas":"0x1","to":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","value":"0x0","accessList":[],"input":"0x","r":"0x703a4b4d6dbff2fa2345df73263df2098faa7214863b5ec82c4c07162d87b853","s":"0x17dea762c4ce600ad1d9d2c9ae6dd35b9e526d03c875f868ad0792fd4fad72e0","yParity":"0x0","hash":"0xf8d5fb22350f52ae8c30cd7f6969eb73de849c8dc010f4215d4c5c24824fe2b3"}},"impersonated_sender":null}],"ommers":[]},{"header":{"parentHash":"0x0d575f9ca968cd483549172245483a12343afc3cabef80f0fa39855b10b98c70","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0xe1423fd180478ab4fd05a7103277d64496b15eb914ecafe71eeec871b552efd1","transactionsRoot":"0x2b5598ef261e5f88e4303bb2b3986b3d5c0ebf4cd9977daebccae82a6469b988","receiptsRoot":"0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x2","gasLimit":"0x1c9c380","gasUsed":"0x5208","timestamp":"0x66cdcc2b","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x342a1c58","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"transaction":{"EIP1559":{"chainId":"0x7a69","nonce":"0x0","gasLimit":"0x5209","maxFeePerGas":"0x77359401","maxPriorityFeePerGas":"0x1","to":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","value":"0x0","accessList":[],"input":"0x","r":"0x85c2794a580da137e24ccc823b45ae5cea99371ae23ee13860fcc6935f8305b0","s":"0x41de7fa4121dab284af4453d30928241208bafa90cdb701fe9bc7054759fe3cd","yParity":"0x0","hash":"0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515"}},"impersonated_sender":null}],"ommers":[]}],"transactions":[{"info":{"transaction_hash":"0xf8d5fb22350f52ae8c30cd7f6969eb73de849c8dc010f4215d4c5c24824fe2b3","transaction_index":0,"from":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","to":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","contract_address":null,"traces":[{"parent":null,"children":[],"idx":0,"trace":{"depth":0,"success":true,"caller":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","address":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","maybe_precompile":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x","output":"0x","gas_used":0,"gas_limit":1,"status":"Stop","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}],"exit":"Stop","out":"0x","nonce":0,"gas_used":21000},"receipt":{"type":"0x2","status":"0x1","cumulativeGasUsed":"0x5208","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},"block_hash":"0x0d575f9ca968cd483549172245483a12343afc3cabef80f0fa39855b10b98c70","block_number":1},{"info":{"transaction_hash":"0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515","transaction_index":0,"from":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","to":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","contract_address":null,"traces":[{"parent":null,"children":[],"idx":0,"trace":{"depth":0,"success":true,"caller":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","address":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","maybe_precompile":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x","output":"0x","gas_used":0,"gas_limit":1,"status":"Stop","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}],"exit":"Stop","out":"0x","nonce":0,"gas_used":21000},"receipt":{"type":"0x2","status":"0x1","cumulativeGasUsed":"0x5208","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},"block_hash":"0x1f435a603c1bf6d544a90156b572b96d7a1730028422d800839bae78bb3506d0","block_number":2}]} \ No newline at end of file diff --git a/crates/anvil/tests/it/state.rs b/crates/anvil/tests/it/state.rs index 7e822ee23..4e96ab0f1 100644 --- a/crates/anvil/tests/it/state.rs +++ b/crates/anvil/tests/it/state.rs @@ -1,5 +1,6 @@ //! general eth api tests +use alloy_primitives::Uint; use anvil::{spawn, NodeConfig}; #[tokio::test(flavor = "multi_thread")] @@ -21,3 +22,23 @@ async fn can_load_state() { let num2 = api.block_number().unwrap(); assert_eq!(num, num2); } + +#[tokio::test(flavor = "multi_thread")] +async fn can_load_existing_state_legacy() { + let state_file = "test-data/state-dump-legacy.json"; + + let (api, _handle) = spawn(NodeConfig::test().with_init_state_path(state_file)).await; + + let block_number = api.block_number().unwrap(); + assert_eq!(block_number, Uint::from(2)); +} + +#[tokio::test(flavor = "multi_thread")] +async fn can_load_existing_state() { + let state_file = "test-data/state-dump.json"; + + let (api, _handle) = spawn(NodeConfig::test().with_init_state_path(state_file)).await; + + let block_number = api.block_number().unwrap(); + assert_eq!(block_number, Uint::from(2)); +} From 187cbb5797cc57b8d5261d77e93e8f330f53a912 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 27 Aug 2024 17:49:33 +0300 Subject: [PATCH 115/184] feat(cheatcodes): add resetGasMetering cheatcode (#8750) * feat(cheatcodes): add resetGasMetering cheatcode * Changes after review: nit, add test for negative gas * Consistent gas reset if touched --- crates/cheatcodes/assets/cheatcodes.json | 20 ++++ crates/cheatcodes/spec/src/vm.rs | 4 + crates/cheatcodes/src/evm.rs | 10 +- crates/cheatcodes/src/inspector.rs | 52 +++++++--- crates/forge/tests/cli/test_cmd.rs | 122 ++++++++++++++++++++++- testdata/cheats/Vm.sol | 1 + 6 files changed, 192 insertions(+), 17 deletions(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 4f41af83e..03755f4e6 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -7011,6 +7011,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "resetGasMetering", + "description": "Reset gas metering (i.e. gas usage is set to gas limit).", + "declaration": "function resetGasMetering() external;", + "visibility": "external", + "mutability": "", + "signature": "resetGasMetering()", + "selector": "0xbe367dd3", + "selectorBytes": [ + 190, + 54, + 125, + 211 + ] + }, + "group": "evm", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "resetNonce", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 4149b56b4..1a092176d 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -662,6 +662,10 @@ interface Vm { #[cheatcode(group = Evm, safety = Safe)] function resumeGasMetering() external; + /// Reset gas metering (i.e. gas usage is set to gas limit). + #[cheatcode(group = Evm, safety = Safe)] + function resetGasMetering() external; + // -------- Gas Measurement -------- /// Gets the gas used in the last call. diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 6d8bdfb37..a3d517387 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -222,10 +222,18 @@ impl Cheatcode for resumeGasMeteringCall { } } +impl Cheatcode for resetGasMeteringCall { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self {} = self; + state.gas_metering.reset(); + Ok(Default::default()) + } +} + impl Cheatcode for lastCallGasCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; - let Some(last_call_gas) = &state.last_call_gas else { + let Some(last_call_gas) = &state.gas_metering.last_call_gas else { bail!("no external call was made yet"); }; Ok(last_call_gas.abi_encode()) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index a11d24bb9..8e2331cf1 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -219,10 +219,17 @@ pub struct BroadcastableTransaction { pub struct GasMetering { /// True if gas metering is paused. pub paused: bool, - /// True if gas metering was resumed during the test. - pub resumed: bool, + /// True if gas metering was resumed or reseted during the test. + /// Used to reconcile gas when frame ends (if spent less than refunded). + pub touched: bool, + /// True if gas metering should be reset to frame limit. + pub reset: bool, /// Stores frames paused gas. pub paused_frames: Vec, + + /// Cache of the amount of gas used in previous call. + /// This is used by the `lastCallGas` cheatcode. + pub last_call_gas: Option, } impl GasMetering { @@ -230,10 +237,18 @@ impl GasMetering { pub fn resume(&mut self) { if self.paused { self.paused = false; - self.resumed = true; + self.touched = true; } self.paused_frames.clear(); } + + /// Reset gas to limit. + pub fn reset(&mut self) { + self.paused = false; + self.touched = true; + self.reset = true; + self.paused_frames.clear(); + } } /// List of transactions that can be broadcasted. @@ -264,7 +279,7 @@ pub struct Cheatcodes { /// execution block environment. pub block: Option, - /// The gas price + /// The gas price. /// /// Used in the cheatcode handler to overwrite the gas price separately from the gas price /// in the execution environment. @@ -295,10 +310,6 @@ pub struct Cheatcodes { /// Recorded logs pub recorded_logs: Option>, - /// Cache of the amount of gas used in previous call. - /// This is used by the `lastCallGas` cheatcode. - pub last_call_gas: Option, - /// Mocked calls // **Note**: inner must a BTreeMap because of special `Ord` impl for `MockCallDataContext` pub mocked_calls: HashMap>, @@ -377,7 +388,6 @@ impl Cheatcodes { accesses: Default::default(), recorded_account_diffs_stack: Default::default(), recorded_logs: Default::default(), - last_call_gas: Default::default(), mocked_calls: Default::default(), expected_calls: Default::default(), expected_emits: Default::default(), @@ -994,11 +1004,16 @@ impl Inspector for Cheatcodes { fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { self.pc = interpreter.program_counter(); - // `pauseGasMetering`: reset interpreter gas. + // `pauseGasMetering`: pause / resume interpreter gas. if self.gas_metering.paused { self.meter_gas(interpreter); } + // `resetGasMetering`: reset interpreter gas. + if self.gas_metering.reset { + self.meter_gas_reset(interpreter); + } + // `record`: record storage reads and writes. if self.accesses.is_some() { self.record_accesses(interpreter); @@ -1026,7 +1041,7 @@ impl Inspector for Cheatcodes { self.meter_gas_end(interpreter); } - if self.gas_metering.resumed { + if self.gas_metering.touched { self.meter_gas_check(interpreter); } } @@ -1143,7 +1158,7 @@ impl Inspector for Cheatcodes { // Record the gas usage of the call, this allows the `lastCallGas` cheatcode to // retrieve the gas usage of the last call. let gas = outcome.result.gas; - self.last_call_gas = Some(crate::Vm::Gas { + self.gas_metering.last_call_gas = Some(crate::Vm::Gas { gasLimit: gas.limit(), gasTotalUsed: gas.spent(), gasMemoryUsed: 0, @@ -1406,15 +1421,22 @@ impl Cheatcodes { } } + #[cold] + fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) { + interpreter.gas = Gas::new(interpreter.gas().limit()); + self.gas_metering.reset = false; + } + #[cold] fn meter_gas_check(&mut self, interpreter: &mut Interpreter) { if will_exit(interpreter.instruction_result) { - // Reconcile gas if spent is less than refunded. - // This can happen if gas was paused / resumed (https://github.com/foundry-rs/foundry/issues/4370). + // Reset gas if spent is less than refunded. + // This can happen if gas was paused / resumed or reset. + // https://github.com/foundry-rs/foundry/issues/4370 if interpreter.gas.spent() < u64::try_from(interpreter.gas.refunded()).unwrap_or_default() { - interpreter.gas = Gas::new(interpreter.gas.remaining()); + interpreter.gas = Gas::new(interpreter.gas.limit()); } } } diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 37e1895f6..c2cf46f8f 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -1390,7 +1390,7 @@ contract ATest is Test { cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" ... -[PASS] test_negativeGas() (gas: 3252) +[PASS] test_negativeGas() (gas: 0) ... "#]]); }); @@ -1481,3 +1481,123 @@ Traces: ... "#]]); }); + +forgetest_init!(gas_metering_reset, |prj, cmd| { + prj.wipe_contracts(); + prj.insert_ds_test(); + prj.insert_vm(); + prj.clear(); + + prj.add_source( + "ATest.t.sol", + r#"pragma solidity 0.8.24; +import {Vm} from "./Vm.sol"; +import {DSTest} from "./test.sol"; +contract B { + function a() public returns (uint256) { + return 100; + } +} +contract ATest is DSTest { + Vm vm = Vm(HEVM_ADDRESS); + B b; + uint256 a; + + function testResetGas() public { + vm.resetGasMetering(); + } + + function testResetGas1() public { + vm.resetGasMetering(); + b = new B(); + vm.resetGasMetering(); + } + + function testResetGas2() public { + b = new B(); + b = new B(); + vm.resetGasMetering(); + } + + function testResetGas3() public { + vm.resetGasMetering(); + b = new B(); + b = new B(); + } + + function testResetGas4() public { + vm.resetGasMetering(); + b = new B(); + vm.resetGasMetering(); + b = new B(); + } + + function testResetGas5() public { + vm.resetGasMetering(); + b = new B(); + vm.resetGasMetering(); + b = new B(); + vm.resetGasMetering(); + } + + function testResetGas6() public { + vm.resetGasMetering(); + b = new B(); + b = new B(); + _reset(); + vm.resetGasMetering(); + } + + function testResetGas7() public { + vm.resetGasMetering(); + b = new B(); + b = new B(); + _reset(); + } + + function testResetGas8() public { + this.resetExternal(); + } + + function testResetGas9() public { + this.resetExternal(); + vm.resetGasMetering(); + } + + function testResetNegativeGas() public { + a = 100; + vm.resetGasMetering(); + + delete a; + } + + function _reset() internal { + vm.resetGasMetering(); + } + + function resetExternal() external { + b = new B(); + b = new B(); + vm.resetGasMetering(); + } +} + "#, + ) + .unwrap(); + + cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" +... +[PASS] testResetGas() (gas: 40) +[PASS] testResetGas1() (gas: 40) +[PASS] testResetGas2() (gas: 40) +[PASS] testResetGas3() (gas: 134476) +[PASS] testResetGas4() (gas: 56302) +[PASS] testResetGas5() (gas: 40) +[PASS] testResetGas6() (gas: 40) +[PASS] testResetGas7() (gas: 49) +[PASS] testResetGas8() (gas: 622) +[PASS] testResetGas9() (gas: 40) +[PASS] testResetNegativeGas() (gas: 0) +... +"#]]); +}); diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 8ce2ce5fe..44caa5933 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -346,6 +346,7 @@ interface Vm { function removeDir(string calldata path, bool recursive) external; function removeFile(string calldata path) external; function replace(string calldata input, string calldata from, string calldata to) external pure returns (string memory output); + function resetGasMetering() external; function resetNonce(address account) external; function resumeGasMetering() external; function resumeTracing() external view; From 995a4d089581c489f9e616b133b7a140f55fd6b4 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 27 Aug 2024 19:44:07 +0200 Subject: [PATCH 116/184] chore(deps): midweek bumps (#8757) --- Cargo.lock | 102 ++++++++++++++++++++++------------------------------- 1 file changed, 42 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9278cf751..4dce9a826 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,9 +74,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-chains" -version = "0.1.27" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b515e82c8468ddb6ff8db21c78a5997442f113fd8471fd5b2261b2602dd0c67" +checksum = "bb07629a5d0645d29f68d2fb6f4d0cf15c89ec0965be915f303967180929743f" dependencies = [ "num_enum", "serde", @@ -134,7 +134,7 @@ dependencies = [ "proptest", "serde", "serde_json", - "winnow 0.6.18", + "winnow", ] [[package]] @@ -603,7 +603,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbcba3ca07cf7975f15d871b721fb18031eec8bce51103907f6dcce00b255d98" dependencies = [ "serde", - "winnow 0.6.18", + "winnow", ] [[package]] @@ -1751,9 +1751,9 @@ checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "bytemuck" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fd4c6dcc3b0aea2f5c0b4b82c2b15fe39ddbc76041a310848f4706edf76bb31" +checksum = "773d90827bc3feecfb67fab12e24de0749aad83c74b9504ecde46237b5cd24e2" [[package]] name = "byteorder" @@ -1803,15 +1803,16 @@ dependencies = [ [[package]] name = "c-kzg" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdf100c4cea8f207e883ff91ca886d621d8a166cb04971dfaa9bb8fd99ed95df" +checksum = "f0307f72feab3300336fb803a57134159f6e20139af1357f36c54cb90d8e8928" dependencies = [ "blst", "cc", "glob", "hex", "libc", + "once_cell", "serde", ] @@ -1929,9 +1930,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.14" +version = "1.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d2eb3cd3d1bf4529e31c215ee6f93ec5a3d536d9f578f93d9d33ee19562932" +checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" dependencies = [ "jobserver", "libc", @@ -2071,9 +2072,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.23" +version = "4.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531d7959c5bbb6e266cecdd0f20213639c3a5c3e4d615f97db87661745f781ff" +checksum = "6d7db6eca8c205649e8d3ccd05aa5042b1800a784e56bc7c43524fde8abbfa9b" dependencies = [ "clap", ] @@ -3361,7 +3362,7 @@ dependencies = [ "tikv-jemallocator", "tokio", "toml 0.8.19", - "toml_edit 0.22.20", + "toml_edit", "tower-http", "tracing", "tracing-subscriber", @@ -3516,9 +3517,9 @@ dependencies = [ [[package]] name = "foundry-block-explorers" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3306c1dfb236a3f7c86f7f6c9a88843d621cea96add97fdefbdc53ef3ecf6dfe" +checksum = "c1580bdb99a6a531b44ac5cda229069cacc11ae7d54faa45676e1bee9ee7da1c" dependencies = [ "alloy-chains", "alloy-json-abi", @@ -3723,7 +3724,7 @@ dependencies = [ "thiserror", "tokio", "tracing", - "winnow 0.6.18", + "winnow", "yansi", ] @@ -3829,7 +3830,7 @@ dependencies = [ "tempfile", "thiserror", "toml 0.8.19", - "toml_edit 0.22.20", + "toml_edit", "tracing", "walkdir", ] @@ -4316,7 +4317,7 @@ dependencies = [ "gix-utils", "itoa", "thiserror", - "winnow 0.6.18", + "winnow", ] [[package]] @@ -4337,7 +4338,7 @@ dependencies = [ "smallvec", "thiserror", "unicode-bom", - "winnow 0.6.18", + "winnow", ] [[package]] @@ -4439,7 +4440,7 @@ dependencies = [ "itoa", "smallvec", "thiserror", - "winnow 0.6.18", + "winnow", ] [[package]] @@ -4474,7 +4475,7 @@ dependencies = [ "gix-validate", "memmap2", "thiserror", - "winnow 0.6.18", + "winnow", ] [[package]] @@ -6532,9 +6533,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.21" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a909e6e8053fa1a5ad670f5816c7d93029ee1fa8898718490544a6b0d5d38b3e" +checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", "syn 2.0.76", @@ -6565,11 +6566,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "toml_edit 0.21.1", + "toml_edit", ] [[package]] @@ -6879,9 +6880,9 @@ dependencies = [ [[package]] name = "ratatui" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ba6a365afbe5615999275bea2446b970b10a41102500e27ce7678d50d978303" +checksum = "fdef7f9be5c0122f890d58bdf4d964349ba6a6161f705907526d891efabba57d" dependencies = [ "bitflags 2.6.0", "cassowary", @@ -7316,7 +7317,7 @@ dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.102.6", + "rustls-webpki 0.102.7", "subtle", "zeroize", ] @@ -7383,9 +7384,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.6" +version = "0.102.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +checksum = "84678086bd54edf2b415183ed7a94d0efb049f1b646a33e22a36f3794be6ae56" dependencies = [ "ring", "rustls-pki-types", @@ -8047,7 +8048,7 @@ dependencies = [ "sha2", "thiserror", "tokio", - "toml_edit 0.22.20", + "toml_edit", "uuid 1.10.0", "yansi", "zip 2.2.0", @@ -8151,9 +8152,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "svm-rs" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b638fb4b6a74ef632a18935d240596b2618c8eb5c888e9cbf3c689af9a4aded" +checksum = "00d3230221bec82c4a79cd7af637ac29f04f369e95e476bc492f22882bb83c91" dependencies = [ "const-hex", "dirs 5.0.1", @@ -8163,6 +8164,7 @@ dependencies = [ "serde", "serde_json", "sha2", + "tempfile", "thiserror", "url", "zip 2.2.0", @@ -8170,9 +8172,9 @@ dependencies = [ [[package]] name = "svm-rs-builds" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "813b21b9858cc493134b899c96e73c60f2e6043f050a17020e98ad8c2d9c9912" +checksum = "83fe4ebbe1038a5a517a07948c2487b3ccf79a4908953cc8f0047cf652233546" dependencies = [ "build_const", "const-hex", @@ -8570,7 +8572,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.20", + "toml_edit", ] [[package]] @@ -8582,17 +8584,6 @@ dependencies = [ "serde", ] -[[package]] -name = "toml_edit" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" -dependencies = [ - "indexmap 2.4.0", - "toml_datetime", - "winnow 0.5.40", -] - [[package]] name = "toml_edit" version = "0.22.20" @@ -8603,14 +8594,14 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.18", + "winnow", ] [[package]] name = "tonic" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38659f4a91aba8598d27821589f5db7dddd94601e7a01b1e485a50e5484c7401" +checksum = "c6f6ba989e4b2c58ae83d862d3a3e27690b6e3ae630d0deb59f3697f32aa88ad" dependencies = [ "async-stream", "async-trait", @@ -9608,15 +9599,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - [[package]] name = "winnow" version = "0.6.18" From 2442e7a5fc165d7d0b022aa8b9f09dcdf675157b Mon Sep 17 00:00:00 2001 From: Pranesh A S <42379522+PraneshASP@users.noreply.github.com> Date: Tue, 27 Aug 2024 23:18:20 +0530 Subject: [PATCH 117/184] feat(`forge selectors`): add `find` command (#8754) * feat: add find selectors cmd * chore: throw error --- crates/forge/bin/cmd/selectors.rs | 88 +++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/crates/forge/bin/cmd/selectors.rs b/crates/forge/bin/cmd/selectors.rs index c1626e99f..04d19295c 100644 --- a/crates/forge/bin/cmd/selectors.rs +++ b/crates/forge/bin/cmd/selectors.rs @@ -56,6 +56,17 @@ pub enum SelectorsSubcommands { #[command(flatten)] project_paths: ProjectPathsArgs, }, + + /// Find if a selector is present in the project + #[command(visible_alias = "f")] + Find { + /// The selector to search for + #[arg(help = "The selector to search for (with or without 0x prefix)")] + selector: String, + + #[command(flatten)] + project_paths: ProjectPathsArgs, + }, } impl SelectorsSubcommands { @@ -260,6 +271,83 @@ impl SelectorsSubcommands { } } } + + Self::Find { selector, project_paths } => { + println!("Searching for selector {selector:?} in the project..."); + + let build_args = CoreBuildArgs { + project_paths, + compiler: CompilerArgs { + extra_output: vec![ContractOutputSelection::Abi], + ..Default::default() + }, + ..Default::default() + }; + + let project = build_args.project()?; + let outcome = ProjectCompiler::new().quiet(true).compile(&project)?; + let artifacts = outcome + .into_artifacts_with_files() + .filter(|(file, _, _)| { + let is_sources_path = file.starts_with(&project.paths.sources); + let is_test = file.is_sol_test(); + is_sources_path && !is_test + }) + .collect::>(); + + let mut table = Table::new(); + + table.set_header(["Type", "Signature", "Selector", "Contract"]); + + for (_file, contract, artifact) in artifacts { + let abi = artifact.abi.ok_or_else(|| eyre::eyre!("Unable to fetch abi"))?; + + let selector_bytes = + hex::decode(selector.strip_prefix("0x").unwrap_or(&selector))?; + + for func in abi.functions() { + if func.selector().as_slice().starts_with(selector_bytes.as_slice()) { + table.add_row([ + "Function", + &func.signature(), + &hex::encode_prefixed(func.selector()), + contract.as_str(), + ]); + } + } + + for event in abi.events() { + if event.selector().as_slice().starts_with(selector_bytes.as_slice()) { + table.add_row([ + "Event", + &event.signature(), + &hex::encode_prefixed(event.selector()), + contract.as_str(), + ]); + } + } + + for error in abi.errors() { + if error.selector().as_slice().starts_with(selector_bytes.as_slice()) { + table.add_row([ + "Event", + &error.signature(), + &hex::encode_prefixed(error.selector()), + contract.as_str(), + ]); + } + } + } + + if table.row_count() > 0 { + println!(); + println!("Found {} instance(s)...", table.row_count()); + println!("{table}"); + } else { + println!(); + return Err(eyre::eyre!("Selector not found in the project.")); + } + } } Ok(()) } From e0aeef918e03fcfa3a06e5bd4739a371856d4ee1 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 28 Aug 2024 10:54:45 +0300 Subject: [PATCH 118/184] fix(fmt): `params_first_multi` split if more than one param (#8762) feat(fmt): params_first_multi split if more than one param --- crates/config/src/fmt.rs | 8 +- crates/fmt/src/formatter.rs | 13 +- .../testdata/FunctionDefinition/all.fmt.sol | 4 +- .../FunctionDefinition/params-first.fmt.sol | 8 +- .../FunctionDefinition/params-multi.fmt.sol | 710 ++++++++++++++++++ 5 files changed, 732 insertions(+), 11 deletions(-) create mode 100644 crates/fmt/testdata/FunctionDefinition/params-multi.fmt.sol diff --git a/crates/config/src/fmt.rs b/crates/config/src/fmt.rs index e1ebf7207..44fefe7d4 100644 --- a/crates/config/src/fmt.rs +++ b/crates/config/src/fmt.rs @@ -153,11 +153,13 @@ pub enum SingleLineBlockStyle { #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum MultilineFuncHeaderStyle { - /// Write function parameters multiline first + /// Write function parameters multiline first. ParamsFirst, - /// Write function attributes multiline first + /// Write function parameters multiline first when there is more than one param. + ParamsFirstMulti, + /// Write function attributes multiline first. AttributesFirst, - /// If function params or attrs are multiline + /// If function params or attrs are multiline. /// split the rest All, } diff --git a/crates/fmt/src/formatter.rs b/crates/fmt/src/formatter.rs index 396348201..1c5000330 100644 --- a/crates/fmt/src/formatter.rs +++ b/crates/fmt/src/formatter.rs @@ -1617,7 +1617,9 @@ impl<'a, W: Write> Formatter<'a, W> { let should_multiline = header_multiline && matches!( fmt.config.multiline_func_header, - MultilineFuncHeaderStyle::ParamsFirst | MultilineFuncHeaderStyle::All + MultilineFuncHeaderStyle::ParamsFirst | + MultilineFuncHeaderStyle::ParamsFirstMulti | + MultilineFuncHeaderStyle::All ); params_multiline = should_multiline || multiline || @@ -1626,8 +1628,13 @@ impl<'a, W: Write> Formatter<'a, W> { ¶ms, ",", )?; - // Write new line if we have only one parameter and params on multiline set. - if params.len() == 1 && params_multiline { + // Write new line if we have only one parameter and params first set. + if params.len() == 1 && + matches!( + fmt.config.multiline_func_header, + MultilineFuncHeaderStyle::ParamsFirst + ) + { writeln!(fmt.buf())?; } fmt.write_chunks_separated(¶ms, ",", params_multiline)?; diff --git a/crates/fmt/testdata/FunctionDefinition/all.fmt.sol b/crates/fmt/testdata/FunctionDefinition/all.fmt.sol index 86577d44b..6d9088067 100644 --- a/crates/fmt/testdata/FunctionDefinition/all.fmt.sol +++ b/crates/fmt/testdata/FunctionDefinition/all.fmt.sol @@ -717,9 +717,7 @@ contract FunctionOverrides is a = 1; } - function oneParam( - uint256 x - ) + function oneParam(uint256 x) override( FunctionInterfaces, FunctionDefinitions, diff --git a/crates/fmt/testdata/FunctionDefinition/params-first.fmt.sol b/crates/fmt/testdata/FunctionDefinition/params-first.fmt.sol index 8208c15d2..3e7ebfff6 100644 --- a/crates/fmt/testdata/FunctionDefinition/params-first.fmt.sol +++ b/crates/fmt/testdata/FunctionDefinition/params-first.fmt.sol @@ -3,7 +3,9 @@ interface FunctionInterfaces { function noParamsNoModifiersNoReturns(); - function oneParam(uint256 x); + function oneParam( + uint256 x + ); function oneModifier() modifier1; @@ -335,7 +337,9 @@ contract FunctionDefinitions { a = 1; } - function oneParam(uint256 x) { + function oneParam( + uint256 x + ) { a = 1; } diff --git a/crates/fmt/testdata/FunctionDefinition/params-multi.fmt.sol b/crates/fmt/testdata/FunctionDefinition/params-multi.fmt.sol new file mode 100644 index 000000000..cd2015c9e --- /dev/null +++ b/crates/fmt/testdata/FunctionDefinition/params-multi.fmt.sol @@ -0,0 +1,710 @@ +// config: line_length = 60 +// config: multiline_func_header = "params_first_multi" +interface FunctionInterfaces { + function noParamsNoModifiersNoReturns(); + + function oneParam(uint256 x); + + function oneModifier() modifier1; + + function oneReturn() returns (uint256 y1); + + // function prefix + function withComments( // function name postfix + // x1 prefix + uint256 x1, // x1 postfix + // x2 prefix + uint256 x2, // x2 postfix + // x2 postfix2 + /* + multi-line x3 prefix + */ + uint256 x3 // x3 postfix + ) + // public prefix + public // public postfix + // pure prefix + pure // pure postfix + // modifier1 prefix + modifier1 // modifier1 postfix + // modifier2 prefix + modifier2 /* + mutliline modifier2 postfix + */ + // modifier3 prefix + modifier3 // modifier3 postfix + returns ( + // y1 prefix + uint256 y1, // y1 postfix + // y2 prefix + uint256 y2, // y2 postfix + // y3 prefix + uint256 y3 + ); // y3 postfix + // function postfix + + /*////////////////////////////////////////////////////////////////////////// + TEST + //////////////////////////////////////////////////////////////////////////*/ + function manyParams( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ); + + function manyModifiers() + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10; + + function manyReturns() + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function someParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) modifier1 modifier2 modifier3; + + function someParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) returns (uint256 y1, uint256 y2, uint256 y3); + + function someModifiersSomeReturns() + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3); + + function someParamSomeModifiersSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3); + + function someParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10; + + function someParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function manyParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) modifier1 modifier2 modifier3; + + function manyParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) returns (uint256 y1, uint256 y2, uint256 y3); + + function manyParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10; + + function manyParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function manyParamsManyModifiersManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function modifierOrderCorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderCorrect02() + private + pure + virtual + modifier1 + modifier2 + returns (string); + + function modifierOrderCorrect03() + external + payable + override + modifier1 + modifier2 + returns (address); + + function modifierOrderCorrect04() + internal + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect02() + external + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect03() + internal + pure + virtual + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect04() + external + payable + override + modifier1 + modifier2 + returns (uint256); +} + +contract FunctionDefinitions { + function() external {} + fallback() external {} + + function() external payable {} + fallback() external payable {} + receive() external payable {} + + function noParamsNoModifiersNoReturns() { + a = 1; + } + + function oneParam(uint256 x) { + a = 1; + } + + function oneModifier() modifier1 { + a = 1; + } + + function oneReturn() returns (uint256 y1) { + a = 1; + } + + function manyParams( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) { + a = 1; + } + + function manyModifiers() + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + { + a = 1; + } + + function manyReturns() + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function someParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) modifier1 modifier2 modifier3 { + a = 1; + } + + function someParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) returns (uint256 y1, uint256 y2, uint256 y3) { + a = 1; + } + + function someModifiersSomeReturns() + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3) + { + a = 1; + } + + function someParamSomeModifiersSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3) + { + a = 1; + } + + function someParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + { + a = 1; + } + + function someParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function manyParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) modifier1 modifier2 modifier3 { + a = 1; + } + + function manyParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) returns (uint256 y1, uint256 y2, uint256 y3) { + a = 1; + } + + function manyParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + public + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + { + a = 1; + } + + function manyParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function manyParamsManyModifiersManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function modifierOrderCorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderCorrect02() + private + pure + virtual + modifier1 + modifier2 + returns (string) + { + a = 1; + } + + function modifierOrderCorrect03() + external + payable + override + modifier1 + modifier2 + returns (address) + { + a = 1; + } + + function modifierOrderCorrect04() + internal + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect02() + external + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect03() + internal + pure + virtual + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect04() + external + payable + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + fallback() external payable virtual {} + receive() external payable virtual {} +} + +contract FunctionOverrides is + FunctionInterfaces, + FunctionDefinitions +{ + function noParamsNoModifiersNoReturns() override { + a = 1; + } + + function oneParam(uint256 x) + override( + FunctionInterfaces, + FunctionDefinitions, + SomeOtherFunctionContract, + SomeImport.AndAnotherFunctionContract + ) + { + a = 1; + } +} From aa3b189cf8c59c40ec6616e70fa2c1e0dfe968fb Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 28 Aug 2024 10:58:20 +0300 Subject: [PATCH 119/184] fix(cheatcodes): decode custom error in expectRevert (#8753) --- crates/cheatcodes/src/inspector.rs | 2 ++ crates/cheatcodes/src/test/expect.rs | 34 ++++++++++++-------- crates/forge/tests/cli/test_cmd.rs | 46 ++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 13 deletions(-) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 8e2331cf1..08aef103d 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -644,6 +644,7 @@ impl Cheatcodes { expected_revert.reason.as_deref(), outcome.result.result, outcome.result.output.clone(), + &self.config.available_artifacts, ) { Ok((address, retdata)) => { outcome.result.result = InstructionResult::Return; @@ -1124,6 +1125,7 @@ impl Inspector for Cheatcodes { expected_revert.reason.as_deref(), outcome.result.result, outcome.result.output.clone(), + &self.config.available_artifacts, ) { Err(error) => { trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch"); diff --git a/crates/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs index 4592f6367..be2aeca54 100644 --- a/crates/cheatcodes/src/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -1,6 +1,8 @@ use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Error, Result, Vm::*}; use alloy_primitives::{address, hex, Address, Bytes, LogData as RawLog, U256}; use alloy_sol_types::{SolError, SolValue}; +use foundry_common::ContractsByArtifact; +use foundry_evm_core::decode::RevertDecoder; use revm::interpreter::{ return_ok, InstructionResult, Interpreter, InterpreterAction, InterpreterResult, }; @@ -561,6 +563,7 @@ pub(crate) fn handle_expect_revert( expected_revert: Option<&[u8]>, status: InstructionResult, retdata: Bytes, + known_contracts: &Option, ) -> Result<(Option
, Bytes)> { let success_return = || { if is_create { @@ -598,20 +601,25 @@ pub(crate) fn handle_expect_revert( { Ok(success_return()) } else { - let stringify = |data: &[u8]| { - if let Ok(s) = String::abi_decode(data, true) { - return s; - } - if data.is_ascii() { - return std::str::from_utf8(data).unwrap().to_owned(); - } - hex::encode_prefixed(data) + let (actual, expected) = if let Some(contracts) = known_contracts { + let decoder = RevertDecoder::new().with_abis(contracts.iter().map(|(_, c)| &c.abi)); + ( + &decoder.decode(actual_revert.as_slice(), Some(status)), + &decoder.decode(expected_revert, Some(status)), + ) + } else { + let stringify = |data: &[u8]| { + if let Ok(s) = String::abi_decode(data, true) { + return s; + } + if data.is_ascii() { + return std::str::from_utf8(data).unwrap().to_owned(); + } + hex::encode_prefixed(data) + }; + (&stringify(&actual_revert), &stringify(expected_revert)) }; - Err(fmt_err!( - "Error != expected error: {} != {}", - stringify(&actual_revert), - stringify(expected_revert), - )) + Err(fmt_err!("Error != expected error: {} != {}", actual, expected,)) } } diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index c2cf46f8f..94775d01a 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -1601,3 +1601,49 @@ contract ATest is DSTest { ... "#]]); }); + +// https://github.com/foundry-rs/foundry/issues/8705 +forgetest_init!(test_expect_revert_decode, |prj, cmd| { + prj.wipe_contracts(); + + prj.add_test( + "Counter.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; +contract Counter { + uint256 public number; + error NumberNotEven(uint256 number); + error RandomError(); + function setNumber(uint256 newNumber) public { + if (newNumber % 2 != 0) { + revert NumberNotEven(newNumber); + } + number = newNumber; + } +} +contract CounterTest is Test { + Counter public counter; + function setUp() public { + counter = new Counter(); + counter.setNumber(0); + } + function test_decode() public { + vm.expectRevert(Counter.RandomError.selector); + counter.setNumber(1); + } + function test_decode_with_args() public { + vm.expectRevert(abi.encodePacked(Counter.NumberNotEven.selector, uint(2))); + counter.setNumber(1); + } +} + "#, + ) + .unwrap(); + + cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" +... +[FAIL. Reason: Error != expected error: NumberNotEven(1) != RandomError()] test_decode() ([GAS]) +[FAIL. Reason: Error != expected error: NumberNotEven(1) != NumberNotEven(2)] test_decode_with_args() ([GAS]) +... +"#]]); +}); From 327e29e3f3cb6492e33c43b1cdb1b923620ccb2f Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 28 Aug 2024 11:29:58 +0300 Subject: [PATCH 120/184] feat(cheatcodes): add expectPartialRevert cheatcode (#8763) --- crates/cheatcodes/assets/cheatcodes.json | 22 +++++++++- crates/cheatcodes/spec/src/vm.rs | 6 ++- crates/cheatcodes/src/inspector.rs | 2 + crates/cheatcodes/src/test/expect.rs | 55 +++++++++++++++++++----- crates/forge/tests/cli/test_cmd.rs | 49 +++++++++++++++++++++ testdata/cheats/Vm.sol | 1 + 6 files changed, 123 insertions(+), 12 deletions(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 03755f4e6..574d310ca 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -4711,6 +4711,26 @@ "status": "stable", "safety": "unsafe" }, + { + "func": { + "id": "expectPartialRevert", + "description": "Expects an error on next call that starts with the revert data.", + "declaration": "function expectPartialRevert(bytes4 revertData) external;", + "visibility": "external", + "mutability": "", + "signature": "expectPartialRevert(bytes4)", + "selector": "0x11fb5b9c", + "selectorBytes": [ + 17, + 251, + 91, + 156 + ] + }, + "group": "testing", + "status": "stable", + "safety": "unsafe" + }, { "func": { "id": "expectRevert_0", @@ -4734,7 +4754,7 @@ { "func": { "id": "expectRevert_1", - "description": "Expects an error on next call that starts with the revert data.", + "description": "Expects an error on next call that exactly matches the revert data.", "declaration": "function expectRevert(bytes4 revertData) external;", "visibility": "external", "mutability": "", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 1a092176d..575ccfa84 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -791,7 +791,7 @@ interface Vm { #[cheatcode(group = Testing, safety = Unsafe)] function expectRevert() external; - /// Expects an error on next call that starts with the revert data. + /// Expects an error on next call that exactly matches the revert data. #[cheatcode(group = Testing, safety = Unsafe)] function expectRevert(bytes4 revertData) external; @@ -799,6 +799,10 @@ interface Vm { #[cheatcode(group = Testing, safety = Unsafe)] function expectRevert(bytes calldata revertData) external; + /// Expects an error on next call that starts with the revert data. + #[cheatcode(group = Testing, safety = Unsafe)] + function expectPartialRevert(bytes4 revertData) external; + /// Expects an error on next cheatcode call with any revert data. #[cheatcode(group = Testing, safety = Unsafe, status = Internal)] function _expectCheatcodeRevert() external; diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 08aef103d..1ff2a6e99 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -642,6 +642,7 @@ impl Cheatcodes { false, true, expected_revert.reason.as_deref(), + expected_revert.partial_match, outcome.result.result, outcome.result.output.clone(), &self.config.available_artifacts, @@ -1123,6 +1124,7 @@ impl Inspector for Cheatcodes { cheatcode_call, false, expected_revert.reason.as_deref(), + expected_revert.partial_match, outcome.result.result, outcome.result.output.clone(), &self.config.available_artifacts, diff --git a/crates/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs index be2aeca54..838990415 100644 --- a/crates/cheatcodes/src/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -72,12 +72,14 @@ pub enum ExpectedRevertKind { #[derive(Clone, Debug)] pub struct ExpectedRevert { - /// The expected data returned by the revert, None being any + /// The expected data returned by the revert, None being any. pub reason: Option>, - /// The depth at which the revert is expected + /// The depth at which the revert is expected. pub depth: u64, /// The type of expected revert. pub kind: ExpectedRevertKind, + /// If true then only the first 4 bytes of expected data returned by the revert are checked. + pub partial_match: bool, } #[derive(Clone, Debug)] @@ -286,41 +288,66 @@ impl Cheatcode for expectEmitAnonymous_3Call { impl Cheatcode for expectRevert_0Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; - expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), false) + expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), false, false) } } impl Cheatcode for expectRevert_1Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData } = self; - expect_revert(ccx.state, Some(revertData.as_ref()), ccx.ecx.journaled_state.depth(), false) + expect_revert( + ccx.state, + Some(revertData.as_ref()), + ccx.ecx.journaled_state.depth(), + false, + false, + ) } } impl Cheatcode for expectRevert_2Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData } = self; - expect_revert(ccx.state, Some(revertData), ccx.ecx.journaled_state.depth(), false) + expect_revert(ccx.state, Some(revertData), ccx.ecx.journaled_state.depth(), false, false) + } +} + +impl Cheatcode for expectPartialRevertCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { revertData } = self; + expect_revert( + ccx.state, + Some(revertData.as_ref()), + ccx.ecx.journaled_state.depth(), + false, + true, + ) } } impl Cheatcode for _expectCheatcodeRevert_0Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { - expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), true) + expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), true, false) } } impl Cheatcode for _expectCheatcodeRevert_1Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData } = self; - expect_revert(ccx.state, Some(revertData.as_ref()), ccx.ecx.journaled_state.depth(), true) + expect_revert( + ccx.state, + Some(revertData.as_ref()), + ccx.ecx.journaled_state.depth(), + true, + false, + ) } } impl Cheatcode for _expectCheatcodeRevert_2Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData } = self; - expect_revert(ccx.state, Some(revertData), ccx.ecx.journaled_state.depth(), true) + expect_revert(ccx.state, Some(revertData), ccx.ecx.journaled_state.depth(), true, false) } } @@ -540,6 +567,7 @@ fn expect_revert( reason: Option<&[u8]>, depth: u64, cheatcode: bool, + partial_match: bool, ) -> Result { ensure!( state.expected_revert.is_none(), @@ -553,6 +581,7 @@ fn expect_revert( } else { ExpectedRevertKind::Default }, + partial_match, }); Ok(Default::default()) } @@ -561,6 +590,7 @@ pub(crate) fn handle_expect_revert( is_cheatcode: bool, is_create: bool, expected_revert: Option<&[u8]>, + partial_match: bool, status: InstructionResult, retdata: Bytes, known_contracts: &Option, @@ -575,7 +605,7 @@ pub(crate) fn handle_expect_revert( ensure!(!matches!(status, return_ok!()), "next call did not revert as expected"); - // If None, accept any revert + // If None, accept any revert. let Some(expected_revert) = expected_revert else { return Ok(success_return()); }; @@ -586,7 +616,12 @@ pub(crate) fn handle_expect_revert( let mut actual_revert: Vec = retdata.into(); - // Try decoding as known errors + // Compare only the first 4 bytes if partial match. + if partial_match && actual_revert.get(..4) == expected_revert.get(..4) { + return Ok(success_return()) + } + + // Try decoding as known errors. if matches!( actual_revert.get(..4).map(|s| s.try_into().unwrap()), Some(Vm::CheatcodeError::SELECTOR | alloy_sol_types::Revert::SELECTOR) diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 94775d01a..391de7a28 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -1647,3 +1647,52 @@ contract CounterTest is Test { ... "#]]); }); + +// Tests that `expectPartialRevert` cheatcode partially matches revert data. +forgetest_init!(test_expect_partial_revert, |prj, cmd| { + prj.wipe_contracts(); + prj.insert_ds_test(); + prj.insert_vm(); + prj.clear(); + + prj.add_source( + "Counter.t.sol", + r#"pragma solidity 0.8.24; +import {Vm} from "./Vm.sol"; +import {DSTest} from "./test.sol"; +contract Counter { + error WrongNumber(uint256 number); + function count() public pure { + revert WrongNumber(0); + } +} +contract CounterTest is DSTest { + Vm vm = Vm(HEVM_ADDRESS); + function testExpectPartialRevertWithSelector() public { + Counter counter = new Counter(); + vm.expectPartialRevert(Counter.WrongNumber.selector); + counter.count(); + } + function testExpectPartialRevertWith4Bytes() public { + Counter counter = new Counter(); + vm.expectPartialRevert(bytes4(0x238ace70)); + counter.count(); + } + function testExpectRevert() public { + Counter counter = new Counter(); + vm.expectRevert(Counter.WrongNumber.selector); + counter.count(); + } +} + "#, + ) + .unwrap(); + + cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" +... +[PASS] testExpectPartialRevertWith4Bytes() ([GAS]) +[PASS] testExpectPartialRevertWithSelector() ([GAS]) +[FAIL. Reason: Error != expected error: WrongNumber(0) != custom error 238ace70:] testExpectRevert() ([GAS]) +... +"#]]); +}); diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 44caa5933..b929053da 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -231,6 +231,7 @@ interface Vm { function expectEmit(bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData, address emitter) external; function expectEmit() external; function expectEmit(address emitter) external; + function expectPartialRevert(bytes4 revertData) external; function expectRevert() external; function expectRevert(bytes4 revertData) external; function expectRevert(bytes calldata revertData) external; From 0d8302880b79fa9c3c4aa52ab446583dece19a34 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Wed, 28 Aug 2024 11:59:31 +0200 Subject: [PATCH 121/184] feat: snapbox migration (#8728) * test basic flow * establish required test list by build errors * experiment with abstractions * basic test with stderr * move over the stderr lossy tests * add `get_stdout_lossy` helper * TestCommand will require a cleanup to route as much through Snapbox as possible, including execute and assertions * fix placeholder redacts * move tests * update tests * fix tests * improve tests * continue * continue * clean up * clean up * match against "Updating dependencies in" * fix broken tests, make sure to clear consistently to force recompilation where expected * port more tests * additional tests * more tests * clean up * avoid unnecessary fuse * fix malformed test + full output comparison asserting specific layout, not negative assertion * access list ordering is not consistent * enforce block layout * use stdout_eq file with relative path as dynamic joined CARGO_MANIFEST_DIR does not work * continue migrating tests * more tests * more tests * improve tests * restore test * replace stdout_lossy, unify access * replace more lossy stdout tests * continue porting tests * more tests * clean up * use redactions for scripts, enforce stricter regex * start porting more tests using undesired helpers * remove assert_non_empty_stdout helper, enforce stdout layouts * use snapbox inside of helpers * replace cmd.execute * fix CI tests * soldeer has inconsistent spelling for some reason * attempt fix flaky test * no idea why soldeer is so spotty * make tests more robust, redact "Compiling N files" with [COMPILING_FILES] --- Cargo.lock | 2 - crates/cast/tests/cli/main.rs | 842 ++++++++++++++++++----------- crates/forge/benches/test.rs | 9 +- crates/forge/tests/cli/build.rs | 16 +- crates/forge/tests/cli/cmd.rs | 758 ++++++++++++++++++-------- crates/forge/tests/cli/config.rs | 151 ++++-- crates/forge/tests/cli/create.rs | 58 +- crates/forge/tests/cli/debug.rs | 13 +- crates/forge/tests/cli/script.rs | 727 ++++++++++++++++++++----- crates/forge/tests/cli/soldeer.rs | 56 +- crates/forge/tests/cli/svm.rs | 15 +- crates/forge/tests/cli/test_cmd.rs | 264 ++++++--- crates/forge/tests/cli/verify.rs | 62 ++- crates/test-utils/Cargo.toml | 2 - crates/test-utils/src/macros.rs | 2 +- crates/test-utils/src/script.rs | 6 +- crates/test-utils/src/util.rs | 368 ++----------- crates/verify/src/etherscan/mod.rs | 9 +- 18 files changed, 2205 insertions(+), 1155 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4dce9a826..4b71c523e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4055,11 +4055,9 @@ dependencies = [ "rand", "regex", "serde_json", - "similar-asserts", "snapbox", "tracing", "tracing-subscriber", - "walkdir", ] [[package]] diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 6ef092cb1..8ece9ef65 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1,10 +1,10 @@ //! Contains various tests for checking cast commands use alloy_chains::NamedChain; -use alloy_primitives::{address, b256, Address, B256}; +use alloy_primitives::{b256, B256}; use anvil::{Hardfork, NodeConfig}; use foundry_test_utils::{ - casttest, + casttest, file, rpc::{next_http_rpc_endpoint, next_rpc_endpoint, next_ws_rpc_endpoint}, str, util::OutputExt, @@ -13,8 +13,21 @@ use std::{fs, io::Write, path::Path, str::FromStr}; // tests `--help` is printed to std out casttest!(print_help, |_prj, cmd| { - cmd.arg("--help"); - cmd.assert_non_empty_stdout(); + cmd.arg("--help").assert_success().stdout_eq(str![[r#" +Perform Ethereum RPC calls from the comfort of your command line + +Usage: cast[..] + +Commands: +... + +Options: + -h, --help Print help + -V, --version Print version + +Find more information in the book: http://book.getfoundry.sh/reference/cast/cast.html + +"#]]); }); // tests that the `cast block` command works correctly @@ -23,14 +36,41 @@ casttest!(latest_block, |_prj, cmd| { // Call `cast find-block` cmd.args(["block", "latest", "--rpc-url", eth_rpc_url.as_str()]); - let output = cmd.stdout_lossy(); - assert!(output.contains("transactions:")); - assert!(output.contains("gasUsed")); + cmd.assert_success().stdout_eq(str![[r#" + + +baseFeePerGas [..] +difficulty [..] +extraData [..] +gasLimit [..] +gasUsed [..] +hash [..] +logsBloom [..] +miner [..] +mixHash [..] +nonce [..] +number [..] +parentHash [..] +transactionsRoot [..] +receiptsRoot [..] +sha3Uncles [..] +size [..] +stateRoot [..] +timestamp [..] +withdrawalsRoot [..] +totalDifficulty [..] +transactions: [ +... +] + +"#]]); // cmd.cast_fuse().args(["block", "15007840", "-f", "hash", "--rpc-url", eth_rpc_url.as_str()]); - let output = cmd.stdout_lossy(); - assert_eq!(output.trim(), "0x950091817a57e22b6c1f3b951a15f52d41ac89b299cc8f9c89bb6d185f80c415") + cmd.assert_success().stdout_eq(str![[r#" +0x950091817a57e22b6c1f3b951a15f52d41ac89b299cc8f9c89bb6d185f80c415 + +"#]]); }); // tests that the `cast find-block` command works correctly @@ -40,23 +80,24 @@ casttest!(finds_block, |_prj, cmd| { let eth_rpc_url = next_http_rpc_endpoint(); // Call `cast find-block` - cmd.args(["find-block", "--rpc-url", eth_rpc_url.as_str(), ×tamp]); - let output = cmd.stdout_lossy(); - println!("{output}"); - - // Expect successful block query - // Query: 1647843609, Mar 21 2022 06:20:09 UTC - // Output block: https://etherscan.io/block/14428082 - // Output block time: Mar 21 2022 06:20:09 UTC - assert!(output.contains("14428082"), "{}", output); + // + cmd.args(["find-block", "--rpc-url", eth_rpc_url.as_str(), ×tamp]) + .assert_success() + .stdout_eq(str![[r#" +14428082 + +"#]]); }); // tests that we can create a new wallet with keystore casttest!(new_wallet_keystore_with_password, |_prj, cmd| { - cmd.args(["wallet", "new", ".", "--unsafe-password", "test"]); - let out = cmd.stdout_lossy(); - assert!(out.contains("Created new encrypted keystore file")); - assert!(out.contains("Address")); + cmd.args(["wallet", "new", ".", "--unsafe-password", "test"]).assert_success().stdout_eq(str![ + [r#" +Created new encrypted keystore file: [..] +[ADDRESS] + +"#] + ]); }); // tests that we can get the address of a keystore file @@ -73,9 +114,12 @@ casttest!(wallet_address_keystore_with_password_file, |_prj, cmd| { .unwrap(), "--password-file", keystore_dir.join("password-ec554").to_str().unwrap(), - ]); - let out = cmd.stdout_lossy(); - assert!(out.contains("0xeC554aeAFE75601AaAb43Bd4621A22284dB566C2")); + ]) + .assert_success() + .stdout_eq(str![[r#" +0xeC554aeAFE75601AaAb43Bd4621A22284dB566C2 + +"#]]); }); // tests that `cast wallet sign message` outputs the expected signature @@ -85,17 +129,29 @@ casttest!(wallet_sign_message_utf8_data, |_prj, cmd| { let msg = "test"; let expected = "0xfe28833983d6faa0715c7e8c3873c725ddab6fa5bf84d40e780676e463e6bea20fc6aea97dc273a98eb26b0914e224c8dd5c615ceaab69ddddcf9b0ae3de0e371c"; - cmd.args(["wallet", "sign", "--private-key", pk, msg]); - let output = cmd.stdout_lossy(); - assert_eq!(output.trim(), expected); + cmd.args(["wallet", "sign", "--private-key", pk, msg]).assert_success().stdout_eq(str![[r#" +0xfe28833983d6faa0715c7e8c3873c725ddab6fa5bf84d40e780676e463e6bea20fc6aea97dc273a98eb26b0914e224c8dd5c615ceaab69ddddcf9b0ae3de0e371c + +"#]]); // Success. cmd.cast_fuse() .args(["wallet", "verify", "-a", address, msg, expected]) - .assert_non_empty_stdout(); + .assert_success() + .stdout_eq(str![[r#" +Validation succeeded. Address 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf signed this message. + +"#]]); // Fail. - cmd.cast_fuse().args(["wallet", "verify", "-a", address, "other msg", expected]).assert_err(); + cmd.cast_fuse() + .args(["wallet", "verify", "-a", address, "other msg", expected]) + .assert_failure() + .stderr_eq(str![[r#" +Error: +Validation failed. Address 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf did not sign this message. + +"#]]); }); // tests that `cast wallet sign message` outputs the expected signature, given a 0x-prefixed data @@ -172,25 +228,74 @@ casttest!(wallet_list_local_accounts, |prj, cmd| { cmd.set_current_dir(prj.root()); // empty results - cmd.cast_fuse().args(["wallet", "list", "--dir", "keystore"]); - let list_output = cmd.stdout_lossy(); - assert!(list_output.is_empty()); + cmd.cast_fuse() + .args(["wallet", "list", "--dir", "keystore"]) + .assert_success() + .stdout_eq(str![""]); // create 10 wallets - cmd.cast_fuse().args(["wallet", "new", "keystore", "-n", "10", "--unsafe-password", "test"]); - cmd.stdout_lossy(); + cmd.cast_fuse() + .args(["wallet", "new", "keystore", "-n", "10", "--unsafe-password", "test"]) + .assert_success() + .stdout_eq(str![[r#" +Created new encrypted keystore file: [..] +[ADDRESS] +Created new encrypted keystore file: [..] +[ADDRESS] +Created new encrypted keystore file: [..] +[ADDRESS] +Created new encrypted keystore file: [..] +[ADDRESS] +Created new encrypted keystore file: [..] +[ADDRESS] +Created new encrypted keystore file: [..] +[ADDRESS] +Created new encrypted keystore file: [..] +[ADDRESS] +Created new encrypted keystore file: [..] +[ADDRESS] +Created new encrypted keystore file: [..] +[ADDRESS] +Created new encrypted keystore file: [..] +[ADDRESS] + +"#]]); // test list new wallet - cmd.cast_fuse().args(["wallet", "list", "--dir", "keystore"]); - let list_output = cmd.stdout_lossy(); - assert_eq!(list_output.matches('\n').count(), 10); + cmd.cast_fuse().args(["wallet", "list", "--dir", "keystore"]).assert_success().stdout_eq(str![ + [r#" +[..] (Local) +[..] (Local) +[..] (Local) +[..] (Local) +[..] (Local) +[..] (Local) +[..] (Local) +[..] (Local) +[..] (Local) +[..] (Local) + +"#] + ]); }); // tests that `cast wallet new-mnemonic --entropy` outputs the expected mnemonic casttest!(wallet_mnemonic_from_entropy, |_prj, cmd| { - cmd.args(["wallet", "new-mnemonic", "--entropy", "0xdf9bf37e6fcdf9bf37e6fcdf9bf37e3c"]); - let output = cmd.stdout_lossy(); - assert!(output.contains("test test test test test test test test test test test junk")); + cmd.args(["wallet", "new-mnemonic", "--entropy", "0xdf9bf37e6fcdf9bf37e6fcdf9bf37e3c"]) + .assert_success() + .stdout_eq(str![[r#" +Generating mnemonic from provided entropy... +Successfully generated a new mnemonic. +Phrase: +test test test test test test test test test test test junk + +Accounts: +- Account 0: +Address: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 +Private key: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + + +"#]]); }); // tests that `cast wallet private-key` with arguments outputs the private key @@ -217,9 +322,12 @@ casttest!(wallet_private_key_from_mnemonic_option, |_prj, cmd| { "test test test test test test test test test test test junk", "--mnemonic-index", "1", - ]); - let output = cmd.stdout_lossy(); - assert_eq!(output.trim(), "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"); + ]) + .assert_success() + .stdout_eq(str![[r#" +0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d + +"#]]); }); // tests that `cast wallet private-key` with derivation path outputs the private key @@ -231,9 +339,12 @@ casttest!(wallet_private_key_with_derivation_path, |_prj, cmd| { "test test test test test test test test test test test junk", "--mnemonic-derivation-path", "m/44'/60'/0'/0/1", - ]); - let output = cmd.stdout_lossy(); - assert_eq!(output.trim(), "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"); + ]) + .assert_success() + .stdout_eq(str![[r#" +0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d + +"#]]); }); // tests that `cast wallet import` creates a keystore for a private key and that `cast wallet @@ -250,19 +361,23 @@ casttest!(wallet_import_and_decrypt, |prj, cmd| { b256!("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"); // import private key - cmd.cast_fuse().args([ - "wallet", - "import", - account_name, - "--private-key", - &test_private_key.to_string(), - "-k", - "keystore", - "--unsafe-password", - "test", - ]); + cmd.cast_fuse() + .args([ + "wallet", + "import", + account_name, + "--private-key", + &test_private_key.to_string(), + "-k", + "keystore", + "--unsafe-password", + "test", + ]) + .assert_success() + .stdout_eq(str![[r#" +`testAccount` keystore was saved successfully. [ADDRESS] - cmd.assert_non_empty_stdout(); +"#]]); // check that the keystore file was created let keystore_file = keystore_path.join(account_name); @@ -281,7 +396,7 @@ casttest!(wallet_import_and_decrypt, |prj, cmd| { ]); // get the PK out of the output (last word in the output) - let decrypt_output = decrypt_output.stdout_lossy(); + let decrypt_output = decrypt_output.assert_success().get_output().stdout_lossy(); let private_key_string = decrypt_output.split_whitespace().last().unwrap(); // check that the decrypted private key matches the imported private key let decrypted_private_key = B256::from_str(private_key_string).unwrap(); @@ -292,18 +407,25 @@ casttest!(wallet_import_and_decrypt, |prj, cmd| { // tests that `cast estimate` is working correctly. casttest!(estimate_function_gas, |_prj, cmd| { let eth_rpc_url = next_http_rpc_endpoint(); - cmd.args([ - "estimate", - "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", // vitalik.eth - "--value", - "100", - "deposit()", - "--rpc-url", - eth_rpc_url.as_str(), - ]); - let out: u32 = cmd.stdout_lossy().trim().parse().unwrap(); + // ensure we get a positive non-error value for gas estimate - assert!(out.ge(&0)); + let output: u32 = cmd + .args([ + "estimate", + "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", // vitalik.eth + "--value", + "100", + "deposit()", + "--rpc-url", + eth_rpc_url.as_str(), + ]) + .assert_success() + .get_output() + .stdout_lossy() + .trim() + .parse() + .unwrap(); + assert!(output.ge(&0)); }); // tests that `cast estimate --create` is working correctly. @@ -311,41 +433,45 @@ casttest!(estimate_contract_deploy_gas, |_prj, cmd| { let eth_rpc_url = next_http_rpc_endpoint(); // sample contract code bytecode. Wouldn't run but is valid bytecode that the estimate method // accepts and could be deployed. - cmd.args([ - "estimate", - "--rpc-url", - eth_rpc_url.as_str(), - "--create", - "0000", - "ERC20(uint256,string,string)", - "100", - "Test", - "TST", - ]); + let output = cmd + .args([ + "estimate", + "--rpc-url", + eth_rpc_url.as_str(), + "--create", + "0000", + "ERC20(uint256,string,string)", + "100", + "Test", + "TST", + ]) + .assert_success() + .get_output() + .stdout_lossy(); - let gas: u32 = cmd.stdout_lossy().trim().parse().unwrap(); // ensure we get a positive non-error value for gas estimate - assert!(gas > 0); + let output: u32 = output.trim().parse().unwrap(); + assert!(output > 0); }); // tests that the `cast upload-signatures` command works correctly casttest!(upload_signatures, |_prj, cmd| { // test no prefix is accepted as function - cmd.args(["upload-signature", "transfer(address,uint256)"]); - let output = cmd.stdout_lossy(); - + let output = cmd + .args(["upload-signature", "transfer(address,uint256)"]) + .assert_success() + .get_output() + .stdout_lossy(); assert!(output.contains("Function transfer(address,uint256): 0xa9059cbb"), "{}", output); // test event prefix cmd.args(["upload-signature", "event Transfer(address,uint256)"]); - let output = cmd.stdout_lossy(); - + let output = cmd.assert_success().get_output().stdout_lossy(); assert!(output.contains("Event Transfer(address,uint256): 0x69ca02dd4edd7bf0a4abb9ed3b7af3f14778db5d61921c7dc7cd545266326de2"), "{}", output); // test error prefix cmd.args(["upload-signature", "error ERC20InsufficientBalance(address,uint256,uint256)"]); - let output = cmd.stdout_lossy(); - + let output = cmd.assert_success().get_output().stdout_lossy(); assert!( output.contains("Function ERC20InsufficientBalance(address,uint256,uint256): 0xe450d38c"), "{}", @@ -359,8 +485,7 @@ casttest!(upload_signatures, |_prj, cmd| { "transfer(address,uint256)", "approve(address,uint256)", ]); - let output = cmd.stdout_lossy(); - + let output = cmd.assert_success().get_output().stdout_lossy(); assert!(output.contains("Event Transfer(address,uint256): 0x69ca02dd4edd7bf0a4abb9ed3b7af3f14778db5d61921c7dc7cd545266326de2"), "{}", output); assert!(output.contains("Function transfer(address,uint256): 0xa9059cbb"), "{}", output); assert!(output.contains("Function approve(address,uint256): 0x095ea7b3"), "{}", output); @@ -378,8 +503,7 @@ casttest!(upload_signatures, |_prj, cmd| { .unwrap() .as_str(), ]); - let output = cmd.stdout_lossy(); - + let output = cmd.assert_success().get_output().stdout_lossy(); assert!(output.contains("Event Transfer(address,uint256): 0x69ca02dd4edd7bf0a4abb9ed3b7af3f14778db5d61921c7dc7cd545266326de2"), "{}", output); assert!(output.contains("Function transfer(address,uint256): 0xa9059cbb"), "{}", output); assert!(output.contains("Function approve(address,uint256): 0x095ea7b3"), "{}", output); @@ -394,14 +518,18 @@ casttest!(upload_signatures, |_prj, cmd| { // tests that the `cast to-rlp` and `cast from-rlp` commands work correctly casttest!(rlp, |_prj, cmd| { - cmd.args(["--to-rlp", "[\"0xaa\", [[\"bb\"]], \"0xcc\"]"]); - let out = cmd.stdout_lossy(); - assert!(out.contains("0xc881aac3c281bb81cc"), "{}", out); + cmd.args(["--to-rlp", "[\"0xaa\", [[\"bb\"]], \"0xcc\"]"]).assert_success().stdout_eq(str![[ + r#" +0xc881aac3c281bb81cc + +"# + ]]); cmd.cast_fuse(); - cmd.args(["--from-rlp", "0xcbc58455556666c0c0c2c1c0"]); - let out = cmd.stdout_lossy(); - assert!(out.contains("[[\"0x55556666\"],[],[],[[[]]]]"), "{}", out); + cmd.args(["--from-rlp", "0xcbc58455556666c0c0c2c1c0"]).assert_success().stdout_eq(str![[r#" +[["0x55556666"],[],[],[[[]]]] + +"#]]); }); // test for cast_rpc without arguments @@ -409,9 +537,12 @@ casttest!(rpc_no_args, |_prj, cmd| { let eth_rpc_url = next_http_rpc_endpoint(); // Call `cast rpc eth_chainId` - cmd.args(["rpc", "--rpc-url", eth_rpc_url.as_str(), "eth_chainId"]); - let output = cmd.stdout_lossy(); - assert_eq!(output.trim_end(), r#""0x1""#); + cmd.args(["rpc", "--rpc-url", eth_rpc_url.as_str(), "eth_chainId"]).assert_success().stdout_eq( + str![[r#" +"0x1" + +"#]], + ); }); // test for cast_rpc without arguments using websocket @@ -419,9 +550,12 @@ casttest!(ws_rpc_no_args, |_prj, cmd| { let eth_rpc_url = next_ws_rpc_endpoint(); // Call `cast rpc eth_chainId` - cmd.args(["rpc", "--rpc-url", eth_rpc_url.as_str(), "eth_chainId"]); - let output = cmd.stdout_lossy(); - assert_eq!(output.trim_end(), r#""0x1""#); + cmd.args(["rpc", "--rpc-url", eth_rpc_url.as_str(), "eth_chainId"]).assert_success().stdout_eq( + str![[r#" +"0x1" + +"#]], + ); }); // test for cast_rpc with arguments @@ -429,9 +563,12 @@ casttest!(rpc_with_args, |_prj, cmd| { let eth_rpc_url = next_http_rpc_endpoint(); // Call `cast rpc eth_getBlockByNumber 0x123 false` - cmd.args(["rpc", "--rpc-url", eth_rpc_url.as_str(), "eth_getBlockByNumber", "0x123", "false"]); - let output = cmd.stdout_lossy(); - assert!(output.contains(r#""number":"0x123""#), "{}", output); + cmd.args(["rpc", "--rpc-url", eth_rpc_url.as_str(), "eth_getBlockByNumber", "0x123", "false"]) + .assert_success() + .stdout_eq(str![[r#" +{"number":"0x123","hash":"0xc5dab4e189004a1312e9db43a40abb2de91ad7dd25e75880bf36016d8e9df524","transactions":[],"totalDifficulty":"0x4dea420908b","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","extraData":"0x476574682f4c5649562f76312e302e302f6c696e75782f676f312e342e32","nonce":"0x29d6547c196e00e0","miner":"0xbb7b8287f3f0a933474a79eae42cbca977791171","difficulty":"0x494433b31","gasLimit":"0x1388","gasUsed":"0x0","uncles":[],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x220","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","stateRoot":"0x3fe6bd17aa85376c7d566df97d9f2e536f37f7a87abb3a6f9e2891cf9442f2e4","mixHash":"0x943056aa305aa6d22a3c06110942980342d1f4d4b11c17711961436a0f963ea0","parentHash":"0x7abfd11e862ccde76d6ea8ee20978aac26f4bcb55de1188cc0335be13e817017","timestamp":"0x55ba4564"} + +"#]]); }); // test for cast_rpc with raw params @@ -446,9 +583,12 @@ casttest!(rpc_raw_params, |_prj, cmd| { "eth_getBlockByNumber", "--raw", r#"["0x123", false]"#, - ]); - let output = cmd.stdout_lossy(); - assert!(output.contains(r#""number":"0x123""#), "{}", output); + ]) + .assert_success() + .stdout_eq(str![[r#" +{"number":"0x123","hash":"0xc5dab4e189004a1312e9db43a40abb2de91ad7dd25e75880bf36016d8e9df524","transactions":[],"totalDifficulty":"0x4dea420908b","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","extraData":"0x476574682f4c5649562f76312e302e302f6c696e75782f676f312e342e32","nonce":"0x29d6547c196e00e0","miner":"0xbb7b8287f3f0a933474a79eae42cbca977791171","difficulty":"0x494433b31","gasLimit":"0x1388","gasUsed":"0x0","uncles":[],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x220","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","stateRoot":"0x3fe6bd17aa85376c7d566df97d9f2e536f37f7a87abb3a6f9e2891cf9442f2e4","mixHash":"0x943056aa305aa6d22a3c06110942980342d1f4d4b11c17711961436a0f963ea0","parentHash":"0x7abfd11e862ccde76d6ea8ee20978aac26f4bcb55de1188cc0335be13e817017","timestamp":"0x55ba4564"} + +"#]]); }); // test for cast_rpc with direct params @@ -460,17 +600,20 @@ casttest!(rpc_raw_params_stdin, |_prj, cmd| { |mut stdin| { stdin.write_all(b"\n[\n\"0x123\",\nfalse\n]\n").unwrap(); }, - ); - let output = cmd.stdout_lossy(); - assert!(output.contains(r#""number":"0x123""#), "{}", output); + ) + .assert_success() + .stdout_eq(str![[r#" +{"number":"0x123","hash":"0xc5dab4e189004a1312e9db43a40abb2de91ad7dd25e75880bf36016d8e9df524","transactions":[],"totalDifficulty":"0x4dea420908b","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","extraData":"0x476574682f4c5649562f76312e302e302f6c696e75782f676f312e342e32","nonce":"0x29d6547c196e00e0","miner":"0xbb7b8287f3f0a933474a79eae42cbca977791171","difficulty":"0x494433b31","gasLimit":"0x1388","gasUsed":"0x0","uncles":[],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x220","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","stateRoot":"0x3fe6bd17aa85376c7d566df97d9f2e536f37f7a87abb3a6f9e2891cf9442f2e4","mixHash":"0x943056aa305aa6d22a3c06110942980342d1f4d4b11c17711961436a0f963ea0","parentHash":"0x7abfd11e862ccde76d6ea8ee20978aac26f4bcb55de1188cc0335be13e817017","timestamp":"0x55ba4564"} + +"#]]); }); // checks `cast calldata` can handle arrays casttest!(calldata_array, |_prj, cmd| { - cmd.args(["calldata", "propose(string[])", "[\"\"]"]); - let out = cmd.stdout_lossy(); - assert_eq!(out.trim(),"0xcde2baba0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000" - ); + cmd.args(["calldata", "propose(string[])", "[\"\"]"]).assert_success().stdout_eq(str![[r#" +0xcde2baba0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000 + +"#]]); }); // @@ -483,10 +626,14 @@ casttest!(run_succeeds, |_prj, cmd| { "--quick", "--rpc-url", rpc.as_str(), - ]); - let output = cmd.stdout_lossy(); - assert!(output.contains("Transaction successfully executed")); - assert!(!output.contains("Revert")); + ]) + .assert_success() + .stdout_eq(str![[r#" +... +Transaction successfully executed. +[GAS] + +"#]]); }); // tests that `cast --to-base` commands are working correctly. @@ -507,11 +654,11 @@ casttest!(to_base, |_prj, cmd| { if subcmd == "--to-base" { for base in ["bin", "oct", "dec", "hex"] { cmd.cast_fuse().args([subcmd, value, base]); - assert!(!cmd.stdout_lossy().trim().is_empty()); + assert!(!cmd.assert_success().get_output().stdout_lossy().trim().is_empty()); } } else { cmd.cast_fuse().args([subcmd, value]); - assert!(!cmd.stdout_lossy().trim().is_empty()); + assert!(!cmd.assert_success().get_output().stdout_lossy().trim().is_empty()); } } } @@ -522,25 +669,68 @@ casttest!(receipt_revert_reason, |_prj, cmd| { let rpc = next_http_rpc_endpoint(); // - cmd.cast_fuse().args([ + cmd.args([ "receipt", "0x44f2aaa351460c074f2cb1e5a9e28cbc7d83f33e425101d2de14331c7b7ec31e", "--rpc-url", rpc.as_str(), - ]); - let output = cmd.stdout_lossy(); - assert!(!output.contains("revertReason")); + ]) + .assert_success() + .stdout_eq(str![[r#" + +blockHash 0x2cfe65be49863676b6dbc04d58176a14f39b123f1e2f4fea0383a2d82c2c50d0 +blockNumber 16239315 +contractAddress +cumulativeGasUsed 10743428 +effectiveGasPrice 10539984136 +from 0x199D5ED7F45F4eE35960cF22EAde2076e95B253F +gasUsed 21000 +logs [] +logsBloom 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +root +status 1 (success) +transactionHash 0x44f2aaa351460c074f2cb1e5a9e28cbc7d83f33e425101d2de14331c7b7ec31e +transactionIndex 116 +type 0 +blobGasPrice +blobGasUsed +authorizationList +to 0x91da5bf3F8Eb72724E6f50Ec6C3D199C6355c59c + +"#]]); // - cmd.cast_fuse().args([ - "receipt", - "0x0e07d8b53ed3d91314c80e53cf25bcde02084939395845cbb625b029d568135c", - "--rpc-url", - rpc.as_str(), - ]); - let output = cmd.stdout_lossy(); - assert!(output.contains("revertReason")); - assert!(output.contains("Transaction too old")); + cmd.cast_fuse() + .args([ + "receipt", + "0x0e07d8b53ed3d91314c80e53cf25bcde02084939395845cbb625b029d568135c", + "--rpc-url", + rpc.as_str(), + ]) + .assert_success() + .stdout_eq(str![[r#" + +blockHash 0x883f974b17ca7b28cb970798d1c80f4d4bb427473dc6d39b2a7fe24edc02902d +blockNumber 14839405 +contractAddress +cumulativeGasUsed 20273649 +effectiveGasPrice 21491736378 +from 0x3cF412d970474804623bb4e3a42dE13F9bCa5436 +gasUsed 24952 +logs [] +logsBloom 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +root +status 0 (failed) +transactionHash 0x0e07d8b53ed3d91314c80e53cf25bcde02084939395845cbb625b029d568135c +transactionIndex 173 +type 2 +blobGasPrice +blobGasUsed +authorizationList +to 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45 +revertReason Transaction too old, data: "0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000135472616e73616374696f6e20746f6f206f6c6400000000000000000000000000" + +"#]]); }); // tests that `cast --parse-bytes32-address` command is working correctly. @@ -548,9 +738,12 @@ casttest!(parse_bytes32_address, |_prj, cmd| { cmd.args([ "--parse-bytes32-address", "0x000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045", - ]); - let output = cmd.stdout_lossy(); - assert_eq!(output.trim(), "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045") + ]) + .assert_success() + .stdout_eq(str![[r#" +0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 + +"#]]); }); casttest!(access_list, |_prj, cmd| { @@ -564,12 +757,22 @@ casttest!(access_list, |_prj, cmd| { rpc.as_str(), "--gas-limit", // need to set this for alchemy.io to avoid "intrinsic gas too low" error "100000", - ]); + ]) + .assert_success() + .stdout_eq(str![[r#" +[GAS] +access list: +- address: [..] + keys: +... +- address: [..] + keys: +... +- address: [..] + keys: +... - let output = cmd.stdout_lossy(); - assert!(output.contains("address: 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599")); - assert!(output.contains("0x0d2a19d3ac39dc6cc6fd07423195495e18679bd8c7dd610aa1db7cd784a683a8")); - assert!(output.contains("0x7fba2702a7d6e85ac783a88eacdc48e51310443458071f6db9ac66f8ca7068b8")); +"#]]); }); casttest!(logs_topics, |_prj, cmd| { @@ -584,11 +787,9 @@ casttest!(logs_topics, |_prj, cmd| { "12421182", "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x000000000000000000000000ab5801a7d398351b8be11c439e05c5b3259aec9b", - ]); - - cmd.unchecked_output().stdout_matches_path( - Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/cast_logs.stdout"), - ); + ]) + .assert_success() + .stdout_eq(file!["../fixtures/cast_logs.stdout"]); }); casttest!(logs_topic_2, |_prj, cmd| { @@ -605,11 +806,9 @@ casttest!(logs_topic_2, |_prj, cmd| { "", "0x00000000000000000000000068a99f89e475a078645f4bac491360afe255dff1", /* Filter on the * `to` address */ - ]); - - cmd.unchecked_output().stdout_matches_path( - Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/cast_logs.stdout"), - ); + ]) + .assert_success() + .stdout_eq(file!["../fixtures/cast_logs.stdout"]); }); casttest!(logs_sig, |_prj, cmd| { @@ -624,11 +823,9 @@ casttest!(logs_sig, |_prj, cmd| { "12421182", "Transfer(address indexed from, address indexed to, uint256 value)", "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B", - ]); - - cmd.unchecked_output().stdout_matches_path( - Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/cast_logs.stdout"), - ); + ]) + .assert_success() + .stdout_eq(file!["../fixtures/cast_logs.stdout"]); }); casttest!(logs_sig_2, |_prj, cmd| { @@ -644,11 +841,9 @@ casttest!(logs_sig_2, |_prj, cmd| { "Transfer(address indexed from, address indexed to, uint256 value)", "", "0x68A99f89E475a078645f4BAC491360aFe255Dff1", - ]); - - cmd.unchecked_output().stdout_matches_path( - Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/cast_logs.stdout"), - ); + ]) + .assert_success() + .stdout_eq(file!["../fixtures/cast_logs.stdout"]); }); casttest!(mktx, |_prj, cmd| { @@ -669,12 +864,10 @@ casttest!(mktx, |_prj, cmd| { "--priority-gas-price", "1000000000", "0x0000000000000000000000000000000000000001", - ]); - let output = cmd.stdout_lossy(); - assert_eq!( - output.trim(), - "0x02f86b0180843b9aca008502540be4008252089400000000000000000000000000000000000000016480c001a070d55e79ed3ac9fc8f51e78eb91fd054720d943d66633f2eb1bc960f0126b0eca052eda05a792680de3181e49bab4093541f75b49d1ecbe443077b3660c836016a" - ); + ]).assert_success().stdout_eq(str![[r#" +0x02f86b0180843b9aca008502540be4008252089400000000000000000000000000000000000000016480c001a070d55e79ed3ac9fc8f51e78eb91fd054720d943d66633f2eb1bc960f0126b0eca052eda05a792680de3181e49bab4093541f75b49d1ecbe443077b3660c836016a + +"#]]); }); // ensure recipient or code is required @@ -684,11 +877,11 @@ casttest!(mktx_requires_to, |_prj, cmd| { "--private-key", "0x0000000000000000000000000000000000000000000000000000000000000001", ]); - let output = cmd.stderr_lossy(); - assert_eq!( - output.trim(), - "Error: \nMust specify a recipient address or contract code to deploy" - ); + cmd.assert_failure().stderr_eq(str![[r#" +Error: +Must specify a recipient address or contract code to deploy + +"#]]); }); casttest!(mktx_signer_from_mismatch, |_prj, cmd| { @@ -702,10 +895,14 @@ casttest!(mktx_signer_from_mismatch, |_prj, cmd| { "1", "0x0000000000000000000000000000000000000001", ]); - let output = cmd.stderr_lossy(); - assert!( - output.contains("The specified sender via CLI/env vars does not match the sender configured via\nthe hardware wallet's HD Path.") - ); + cmd.assert_failure().stderr_eq(str![[r#" +Error: +The specified sender via CLI/env vars does not match the sender configured via +the hardware wallet's HD Path. +Please use the `--hd-path ` parameter to specify the BIP32 Path which +corresponds to the sender, or let foundry automatically detect it by not specifying any sender address. + +"#]]); }); casttest!(mktx_signer_from_match, |_prj, cmd| { @@ -726,43 +923,39 @@ casttest!(mktx_signer_from_match, |_prj, cmd| { "--priority-gas-price", "1000000000", "0x0000000000000000000000000000000000000001", - ]); - let output = cmd.stdout_lossy(); - assert_eq!( - output.trim(), - "0x02f86b0180843b9aca008502540be4008252089400000000000000000000000000000000000000018080c001a0cce9a61187b5d18a89ecd27ec675e3b3f10d37f165627ef89a15a7fe76395ce8a07537f5bffb358ffbef22cda84b1c92f7211723f9e09ae037e81686805d3e5505" - ); + ]).assert_success().stdout_eq(str![[r#" +0x02f86b0180843b9aca008502540be4008252089400000000000000000000000000000000000000018080c001a0cce9a61187b5d18a89ecd27ec675e3b3f10d37f165627ef89a15a7fe76395ce8a07537f5bffb358ffbef22cda84b1c92f7211723f9e09ae037e81686805d3e5505 + +"#]]); }); // tests that the raw encoded transaction is returned casttest!(tx_raw, |_prj, cmd| { let rpc = next_http_rpc_endpoint(); - // - cmd.cast_fuse().args([ + // + cmd.args([ "tx", "0x44f2aaa351460c074f2cb1e5a9e28cbc7d83f33e425101d2de14331c7b7ec31e", "raw", "--rpc-url", rpc.as_str(), - ]); - let output = cmd.stdout_lossy(); + ]).assert_success().stdout_eq(str![[r#" +0xf86d824c548502743b65088275309491da5bf3f8eb72724e6f50ec6c3d199c6355c59c87a0a73f33e9e4cc8025a0428518b1748a08bbeb2392ea055b418538944d30adfc2accbbfa8362a401d3a4a07d6093ab2580efd17c11b277de7664fce56e6953cae8e925bec3313399860470 - // - assert_eq!( - output.trim(), - "0xf86d824c548502743b65088275309491da5bf3f8eb72724e6f50ec6c3d199c6355c59c87a0a73f33e9e4cc8025a0428518b1748a08bbeb2392ea055b418538944d30adfc2accbbfa8362a401d3a4a07d6093ab2580efd17c11b277de7664fce56e6953cae8e925bec3313399860470" - ); +"#]]); + // cmd.cast_fuse().args([ "tx", "0x44f2aaa351460c074f2cb1e5a9e28cbc7d83f33e425101d2de14331c7b7ec31e", "--raw", "--rpc-url", rpc.as_str(), - ]); - let output2 = cmd.stdout_lossy(); - assert_eq!(output, output2); + ]).assert_success().stdout_eq(str![[r#" +0xf86d824c548502743b65088275309491da5bf3f8eb72724e6f50ec6c3d199c6355c59c87a0a73f33e9e4cc8025a0428518b1748a08bbeb2392ea055b418538944d30adfc2accbbfa8362a401d3a4a07d6093ab2580efd17c11b277de7664fce56e6953cae8e925bec3313399860470 + +"#]]); }); // ensure receipt or code is required @@ -772,62 +965,66 @@ casttest!(send_requires_to, |_prj, cmd| { "--private-key", "0x0000000000000000000000000000000000000000000000000000000000000001", ]); - let output = cmd.stderr_lossy(); - assert_eq!( - output.trim(), - "Error: \nMust specify a recipient address or contract code to deploy" - ); + cmd.assert_failure().stderr_eq(str![[r#" +Error: +Must specify a recipient address or contract code to deploy + +"#]]); }); casttest!(storage, |_prj, cmd| { - let empty = "0x0000000000000000000000000000000000000000000000000000000000000000"; - let rpc = next_http_rpc_endpoint(); - cmd.cast_fuse().args(["storage", "vitalik.eth", "1", "--rpc-url", &rpc]); - assert_eq!(cmd.stdout_lossy().trim(), empty); + cmd.args(["storage", "vitalik.eth", "1", "--rpc-url", &rpc]).assert_success().stdout_eq(str![ + [r#" +0x0000000000000000000000000000000000000000000000000000000000000000 + +"#] + ]); let rpc = next_http_rpc_endpoint(); - cmd.cast_fuse().args(["storage", "vitalik.eth", "0x01", "--rpc-url", &rpc]); - assert_eq!(cmd.stdout_lossy().trim(), empty); + cmd.cast_fuse() + .args(["storage", "vitalik.eth", "0x01", "--rpc-url", &rpc]) + .assert_success() + .stdout_eq(str![[r#" +0x0000000000000000000000000000000000000000000000000000000000000000 + +"#]]); let rpc = next_http_rpc_endpoint(); let usdt = "0xdac17f958d2ee523a2206206994597c13d831ec7"; let decimals_slot = "0x09"; - let six = "0x0000000000000000000000000000000000000000000000000000000000000006"; - cmd.cast_fuse().args(["storage", usdt, decimals_slot, "--rpc-url", &rpc]); - assert_eq!(cmd.stdout_lossy().trim(), six); + cmd.cast_fuse() + .args(["storage", usdt, decimals_slot, "--rpc-url", &rpc]) + .assert_success() + .stdout_eq(str![[r#" +0x0000000000000000000000000000000000000000000000000000000000000006 + +"#]]); let rpc = next_http_rpc_endpoint(); let total_supply_slot = "0x01"; - let issued = "0x000000000000000000000000000000000000000000000000000000174876e800"; let block_before = "4634747"; let block_after = "4634748"; - cmd.cast_fuse().args([ - "storage", - usdt, - total_supply_slot, - "--rpc-url", - &rpc, - "--block", - block_before, - ]); - assert_eq!(cmd.stdout_lossy().trim(), empty); - cmd.cast_fuse().args([ - "storage", - usdt, - total_supply_slot, - "--rpc-url", - &rpc, - "--block", - block_after, - ]); - assert_eq!(cmd.stdout_lossy().trim(), issued); + cmd.cast_fuse() + .args(["storage", usdt, total_supply_slot, "--rpc-url", &rpc, "--block", block_before]) + .assert_success() + .stdout_eq(str![[r#" +0x0000000000000000000000000000000000000000000000000000000000000000 + +"#]]); + + cmd.cast_fuse() + .args(["storage", usdt, total_supply_slot, "--rpc-url", &rpc, "--block", block_after]) + .assert_success() + .stdout_eq(str![[r#" +0x000000000000000000000000000000000000000000000000000000174876e800 + +"#]]); }); // casttest!(storage_layout, |_prj, cmd| { - cmd.cast_fuse() - .args([ + cmd.args([ "storage", "--rpc-url", next_rpc_endpoint(NamedChain::Optimism).as_str(), @@ -874,27 +1071,39 @@ casttest!(storage_layout, |_prj, cmd| { casttest!(balance, |_prj, cmd| { let rpc = next_http_rpc_endpoint(); let usdt = "0xdac17f958d2ee523a2206206994597c13d831ec7"; - cmd.cast_fuse().args([ - "balance", - "0x0000000000000000000000000000000000000000", - "--erc20", - usdt, - "--rpc-url", - &rpc, - ]); - cmd.cast_fuse().args([ - "balance", - "0x0000000000000000000000000000000000000000", - "--erc721", - usdt, - "--rpc-url", - &rpc, - ]); - let usdt_result = cmd.stdout_lossy(); - let alias_result = cmd.stdout_lossy(); + let usdt_result = cmd + .args([ + "balance", + "0x0000000000000000000000000000000000000000", + "--erc20", + usdt, + "--rpc-url", + &rpc, + ]) + .assert_success() + .get_output() + .stdout_lossy() + .trim() + .to_string(); - assert_ne!(usdt_result, "0x0000000000000000000000000000000000000000000000000000000000000000"); + let alias_result = cmd + .cast_fuse() + .args([ + "balance", + "0x0000000000000000000000000000000000000000", + "--erc721", + usdt, + "--rpc-url", + &rpc, + ]) + .assert_success() + .get_output() + .stdout_lossy() + .trim() + .to_string(); + + assert_ne!(usdt_result, "0"); assert_eq!(alias_result, usdt_result); }); @@ -906,10 +1115,8 @@ casttest!(interface_no_constructor, |prj, cmd| { let path = prj.root().join("interface.json"); fs::write(&path, interface).unwrap(); // Call `cast find-block` - cmd.args(["interface"]).arg(&path); - let output = cmd.stdout_lossy(); - - let s = r#"// SPDX-License-Identifier: UNLICENSED + cmd.args(["interface"]).arg(&path).assert_success().stdout_eq(str![[ + r#"// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.4; interface Interface { @@ -928,8 +1135,10 @@ interface Interface { uint256[] memory minIncomingAssetAmounts_ ); function redeem(address _vaultProxy, bytes memory, bytes memory _assetData) external; -}"#; - assert_eq!(output.trim(), s); +} + +"# + ]]); }); // tests that fetches WETH interface from etherscan @@ -937,10 +1146,9 @@ interface Interface { casttest!(fetch_weth_interface_from_etherscan, |_prj, cmd| { let weth_address = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"; let api_key = "ZUB97R31KSYX7NYVW6224Q6EYY6U56H591"; - cmd.args(["interface", "--etherscan-api-key", api_key, weth_address]); - let output = cmd.stdout_lossy(); - - let weth_interface = r#"// SPDX-License-Identifier: UNLICENSED + cmd.args(["interface", "--etherscan-api-key", api_key, weth_address]) + .assert_success() + .stdout_eq(str![[r#"// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.4; interface WETH9 { @@ -962,65 +1170,89 @@ interface WETH9 { function transfer(address dst, uint256 wad) external returns (bool); function transferFrom(address src, address dst, uint256 wad) external returns (bool); function withdraw(uint256 wad) external; -}"#; - assert_eq!(output.trim(), weth_interface); -}); +} -const ENS_NAME: &str = "emo.eth"; -const ENS_NAMEHASH: B256 = - b256!("0a21aaf2f6414aa664deb341d1114351fdb023cad07bf53b28e57c26db681910"); -const ENS_ADDRESS: Address = address!("28679A1a632125fbBf7A68d850E50623194A709E"); +"#]]); +}); casttest!(ens_namehash, |_prj, cmd| { - cmd.args(["namehash", ENS_NAME]); - let out = cmd.stdout_lossy().trim().parse::(); - assert_eq!(out, Ok(ENS_NAMEHASH)); + cmd.args(["namehash", "emo.eth"]).assert_success().stdout_eq(str![[r#" +0x0a21aaf2f6414aa664deb341d1114351fdb023cad07bf53b28e57c26db681910 + +"#]]); }); casttest!(ens_lookup, |_prj, cmd| { let eth_rpc_url = next_http_rpc_endpoint(); - cmd.args(["lookup-address", &ENS_ADDRESS.to_string(), "--rpc-url", ð_rpc_url, "--verify"]); - let out = cmd.stdout_lossy(); - assert_eq!(out.trim(), ENS_NAME); + cmd.args([ + "lookup-address", + "0x28679A1a632125fbBf7A68d850E50623194A709E", + "--rpc-url", + ð_rpc_url, + "--verify", + ]) + .assert_success() + .stdout_eq(str![[r#" +emo.eth + +"#]]); }); casttest!(ens_resolve, |_prj, cmd| { let eth_rpc_url = next_http_rpc_endpoint(); - cmd.args(["resolve-name", ENS_NAME, "--rpc-url", ð_rpc_url, "--verify"]); - let out = cmd.stdout_lossy().trim().parse::
(); - assert_eq!(out, Ok(ENS_ADDRESS)); + cmd.args(["resolve-name", "emo.eth", "--rpc-url", ð_rpc_url, "--verify"]) + .assert_success() + .stdout_eq(str![[r#" +0x28679A1a632125fbBf7A68d850E50623194A709E + +"#]]); }); casttest!(ens_resolve_no_dot_eth, |_prj, cmd| { let eth_rpc_url = next_http_rpc_endpoint(); - let name = ENS_NAME.strip_suffix(".eth").unwrap(); - cmd.args(["resolve-name", name, "--rpc-url", ð_rpc_url, "--verify"]); - let (_out, err) = cmd.unchecked_output_lossy(); - assert!(err.contains("not found"), "{err:?}"); + cmd.args(["resolve-name", "emo", "--rpc-url", ð_rpc_url, "--verify"]) + .assert_failure() + .stderr_eq(str![[r#" +Error: +ENS resolver not found for name "emo" + +"#]]); }); casttest!(index7201, |_prj, cmd| { - let tests = - [("example.main", "0x183a6125c38840424c4a85fa12bab2ab606c4b6d0e7cc73c0c06ba5300eab500")]; - for (id, expected) in tests { - cmd.cast_fuse(); - assert_eq!(cmd.args(["index-erc7201", id]).stdout_lossy().trim(), expected); - } + cmd.args(["index-erc7201", "example.main"]).assert_success().stdout_eq(str![[r#" +0x183a6125c38840424c4a85fa12bab2ab606c4b6d0e7cc73c0c06ba5300eab500 + +"#]]); }); casttest!(index7201_unknown_formula_id, |_prj, cmd| { - cmd.args(["index-7201", "test", "--formula-id", "unknown"]).assert_err(); + cmd.args(["index-erc7201", "test", "--formula-id", "unknown"]).assert_failure().stderr_eq( + str![[r#" +Error: +unsupported formula ID: unknown + +"#]], + ); }); casttest!(block_number, |_prj, cmd| { let eth_rpc_url = next_http_rpc_endpoint(); - let s = cmd.args(["block-number", "--rpc-url", eth_rpc_url.as_str()]).stdout_lossy(); + let s = cmd + .args(["block-number", "--rpc-url", eth_rpc_url.as_str()]) + .assert_success() + .get_output() + .stdout_lossy(); assert!(s.trim().parse::().unwrap() > 0, "{s}") }); casttest!(block_number_latest, |_prj, cmd| { let eth_rpc_url = next_http_rpc_endpoint(); - let s = cmd.args(["block-number", "--rpc-url", eth_rpc_url.as_str(), "latest"]).stdout_lossy(); + let s = cmd + .args(["block-number", "--rpc-url", eth_rpc_url.as_str(), "latest"]) + .assert_success() + .get_output() + .stdout_lossy(); assert!(s.trim().parse::().unwrap() > 0, "{s}") }); @@ -1033,6 +1265,8 @@ casttest!(block_number_hash, |_prj, cmd| { eth_rpc_url.as_str(), "0x88e96d4537bea4d9c05d12549907b32561d3bf31f45aae734cdc119f13406cb6", ]) + .assert_success() + .get_output() .stdout_lossy(); assert_eq!(s.trim().parse::().unwrap(), 1, "{s}") }); @@ -1041,6 +1275,7 @@ casttest!(send_eip7702, async |_prj, cmd| { let (_api, handle) = anvil::spawn(NodeConfig::test().with_hardfork(Some(Hardfork::PragueEOF))).await; let endpoint = handle.http_endpoint(); + cmd.args([ "send", "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", @@ -1063,12 +1298,13 @@ casttest!(send_eip7702, async |_prj, cmd| { }); casttest!(hash_message, |_prj, cmd| { - let tests = [ - ("hello", "0x50b2c43fd39106bafbba0da34fc430e1f91e3c96ea2acee2bc34119f92b37750"), - ("0x68656c6c6f", "0x50b2c43fd39106bafbba0da34fc430e1f91e3c96ea2acee2bc34119f92b37750"), - ]; - for (message, expected) in tests { - cmd.cast_fuse(); - assert_eq!(cmd.args(["hash-message", message]).stdout_lossy().trim(), expected); - } + cmd.args(["hash-message", "hello"]).assert_success().stdout_eq(str![[r#" +0x50b2c43fd39106bafbba0da34fc430e1f91e3c96ea2acee2bc34119f92b37750 + +"#]]); + + cmd.cast_fuse().args(["hash-message", "0x68656c6c6f"]).assert_success().stdout_eq(str![[r#" +0x50b2c43fd39106bafbba0da34fc430e1f91e3c96ea2acee2bc34119f92b37750 + +"#]]); }); diff --git a/crates/forge/benches/test.rs b/crates/forge/benches/test.rs index 7646a3c21..593bce3d3 100644 --- a/crates/forge/benches/test.rs +++ b/crates/forge/benches/test.rs @@ -1,5 +1,8 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use foundry_test_utils::{util::setup_forge_remote, TestCommand, TestProject}; +use foundry_test_utils::{ + util::{lossy_string, setup_forge_remote}, + TestCommand, TestProject, +}; /// Returns a cloned and `forge built` `solmate` project fn built_solmate() -> (TestProject, TestCommand) { @@ -15,7 +18,9 @@ fn forge_test_benchmark(c: &mut Criterion) { let mut cmd = prj.forge_command(); cmd.arg("test"); b.iter(|| { - cmd.print_output(); + let output = cmd.execute(); + println!("stdout:\n{}", lossy_string(&output.stdout)); + println!("\nstderr:\n{}", lossy_string(&output.stderr)); }); }); } diff --git a/crates/forge/tests/cli/build.rs b/crates/forge/tests/cli/build.rs index f5848173c..d9861f19e 100644 --- a/crates/forge/tests/cli/build.rs +++ b/crates/forge/tests/cli/build.rs @@ -45,7 +45,7 @@ contract Dummy { // tests build output is as expected forgetest_init!(exact_build_output, |prj, cmd| { cmd.args(["build", "--force"]).assert_success().stdout_eq(str![[r#" -Compiling 27 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! @@ -54,11 +54,15 @@ Compiler run successful! // tests build output is as expected forgetest_init!(build_sizes_no_forge_std, |prj, cmd| { - cmd.args(["build", "--sizes"]); - let stdout = cmd.stdout_lossy(); - assert!(!stdout.contains("console"), "\n{stdout}"); - assert!(!stdout.contains("std"), "\n{stdout}"); - assert!(stdout.contains("Counter"), "\n{stdout}"); + cmd.args(["build", "--sizes"]).assert_success().stdout_eq(str![ + r#" +... +| Contract | Size (B) | Margin (B) | +|----------|----------|------------| +| Counter | 247 | 24,329 | +... +"# + ]); }); // tests that skip key in config can be used to skip non-compilable contract diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 9160973e9..1a8d2cb7e 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -1,7 +1,6 @@ //! Contains various tests for checking forge's commands use crate::constants::*; -use alloy_primitives::hex; use foundry_compilers::artifacts::{remappings::Remapping, ConfigurableContractArtifact, Metadata}; use foundry_config::{ parse_with_profile, BasicConfig, Chain, Config, FuzzConfig, InvariantConfig, SolidityErrorCode, @@ -21,8 +20,21 @@ use std::{ // tests `--help` is printed to std out forgetest!(print_help, |_prj, cmd| { - cmd.arg("--help"); - cmd.assert_non_empty_stdout(); + cmd.arg("--help").assert_success().stdout_eq(str![[r#" +Build, test, fuzz, debug and deploy Solidity contracts + +Usage: forge[..] + +Commands: +... + +Options: + -h, --help Print help + -V, --version Print version + +Find more information in the book: http://book.getfoundry.sh/reference/forge/forge.html + +"#]]); }); // checks that `clean` can be invoked even if out and cache don't exist @@ -52,10 +64,9 @@ forgetest!( fs::write(block2_file, "{}").unwrap(); fs::create_dir_all(etherscan_cache_dir).unwrap(); - cmd.args(["cache", "ls"]); - let output_string = String::from_utf8_lossy(&cmd.output().stdout).to_string(); - let output_lines = output_string.split('\n').collect::>(); - println!("{output_string}"); + let output = cmd.args(["cache", "ls"]).assert_success().get_output().stdout_lossy(); + let output_lines = output.split('\n').collect::>(); + println!("{output}"); assert_eq!(output_lines.len(), 6); assert!(output_lines[0].starts_with("-️ mainnet (")); @@ -213,8 +224,14 @@ forgetest!(can_init_repo_with_config, |prj, cmd| { let foundry_toml = prj.root().join(Config::FILE_NAME); assert!(!foundry_toml.exists()); - cmd.args(["init", "--force"]).arg(prj.root()); - cmd.assert_non_empty_stdout(); + cmd.args(["init", "--force"]).arg(prj.root()).assert_success().stdout_eq(str![[r#" +Target directory is not empty, but `--force` was specified +Initializing [..]... +Installing forge-std in [..] (url: Some("https://github.com/foundry-rs/forge-std"), tag: None) + Installed forge-std [..] + Initialized forge project + +"#]]); let s = read_string(&foundry_toml); let _config: BasicConfig = parse_with_profile(&s).unwrap().unwrap().1; @@ -256,8 +273,13 @@ https://github.com/foundry-rs/foundry/issues/new/choose forgetest!(can_init_no_git, |prj, cmd| { prj.wipe(); - cmd.arg("init").arg(prj.root()).arg("--no-git"); - cmd.assert_non_empty_stdout(); + cmd.arg("init").arg(prj.root()).arg("--no-git").assert_success().stdout_eq(str![[r#" +Initializing [..]... +Installing forge-std in [..] (url: Some("https://github.com/foundry-rs/forge-std"), tag: None) + Installed forge-std + Initialized forge project + +"#]]); prj.assert_config_exists(); assert!(!prj.root().join(".git").exists()); @@ -269,8 +291,7 @@ forgetest!(can_init_no_git, |prj, cmd| { forgetest!(can_init_quiet, |prj, cmd| { prj.wipe(); - cmd.arg("init").arg(prj.root()).arg("-q"); - let _ = cmd.output(); + cmd.arg("init").arg(prj.root()).arg("-q").assert_empty_stdout(); }); // `forge init foobar` works with dir argument @@ -284,10 +305,14 @@ forgetest!(can_init_with_dir, |prj, cmd| { // `forge init foobar --template [template]` works with dir argument forgetest!(can_init_with_dir_and_template, |prj, cmd| { - cmd.args(["init", "foobar", "--template", "foundry-rs/forge-template"]); + cmd.args(["init", "foobar", "--template", "foundry-rs/forge-template"]) + .assert_success() + .stdout_eq(str![[r#" +Initializing [..] from https://github.com/foundry-rs/forge-template... + Initialized forge project + +"#]]); - cmd.assert_success(); - cmd.assert_non_empty_stdout(); assert!(prj.root().join("foobar/.git").exists()); assert!(prj.root().join("foobar/foundry.toml").exists()); assert!(prj.root().join("foobar/lib/forge-std").exists()); @@ -306,10 +331,14 @@ forgetest!(can_init_with_dir_and_template_and_branch, |prj, cmd| { "foundry-rs/forge-template", "--branch", "test/deployments", - ]); + ]) + .assert_success() + .stdout_eq(str![[r#" +Initializing [..] from https://github.com/foundry-rs/forge-template... + Initialized forge project + +"#]]); - cmd.assert_success(); - cmd.assert_non_empty_stdout(); assert!(prj.root().join("foobar/.dapprc").exists()); assert!(prj.root().join("foobar/lib/ds-test").exists()); // assert that gitmodules were correctly initialized @@ -321,11 +350,22 @@ forgetest!(can_init_with_dir_and_template_and_branch, |prj, cmd| { // `forge init --force` works on non-empty dirs forgetest!(can_init_non_empty, |prj, cmd| { prj.create_file("README.md", "non-empty dir"); - cmd.arg("init").arg(prj.root()); - cmd.assert_err(); + cmd.arg("init").arg(prj.root()).assert_failure().stderr_eq(str![[r#" +Error: +Cannot run `init` on a non-empty directory. +Run with the `--force` flag to initialize regardless. + +"#]]); + + cmd.arg("--force").assert_success().stdout_eq(str![[r#" +Target directory is not empty, but `--force` was specified +Initializing [..]... +Installing forge-std in [..] (url: Some("https://github.com/foundry-rs/forge-std"), tag: None) + Installed forge-std [..] + Initialized forge project + +"#]]); - cmd.arg("--force"); - cmd.assert_non_empty_stdout(); assert!(prj.root().join(".git").exists()); assert!(prj.root().join("lib/forge-std").exists()); }); @@ -345,11 +385,21 @@ forgetest!(can_init_in_empty_repo, |prj, cmd| { assert!(status.success()); assert!(root.join(".git").exists()); - cmd.arg("init").arg(root); - cmd.assert_err(); + cmd.arg("init").arg(root).assert_failure().stderr_eq(str![[r#" +Error: +Cannot run `init` on a non-empty directory. +Run with the `--force` flag to initialize regardless. + +"#]]); + + cmd.arg("--force").assert_success().stdout_eq(str![[r#" +Target directory is not empty, but `--force` was specified +Initializing [..]... +Installing forge-std in [..] (url: Some("https://github.com/foundry-rs/forge-std"), tag: None) + Installed forge-std [..] + Initialized forge project - cmd.arg("--force"); - cmd.assert_non_empty_stdout(); +"#]]); assert!(root.join("lib/forge-std").exists()); }); @@ -371,11 +421,21 @@ forgetest!(can_init_in_non_empty_repo, |prj, cmd| { prj.create_file("README.md", "non-empty dir"); prj.create_file(".gitignore", "not foundry .gitignore"); - cmd.arg("init").arg(root); - cmd.assert_err(); + cmd.arg("init").arg(root).assert_failure().stderr_eq(str![[r#" +Error: +Cannot run `init` on a non-empty directory. +Run with the `--force` flag to initialize regardless. - cmd.arg("--force"); - cmd.assert_non_empty_stdout(); +"#]]); + + cmd.arg("--force").assert_success().stdout_eq(str![[r#" +Target directory is not empty, but `--force` was specified +Initializing [..]... +Installing forge-std in [..] (url: Some("https://github.com/foundry-rs/forge-std"), tag: None) + Installed forge-std [..] + Initialized forge project + +"#]]); assert!(root.join("lib/forge-std").exists()); // not overwritten @@ -388,8 +448,13 @@ forgetest!(can_init_in_non_empty_repo, |prj, cmd| { forgetest!(can_init_vscode, |prj, cmd| { prj.wipe(); - cmd.arg("init").arg(prj.root()).arg("--vscode"); - cmd.assert_non_empty_stdout(); + cmd.arg("init").arg(prj.root()).arg("--vscode").assert_success().stdout_eq(str![[r#" +Initializing [..]... +Installing forge-std in [..] (url: Some("https://github.com/foundry-rs/forge-std"), tag: None) + Installed forge-std [..] + Initialized forge project + +"#]]); let settings = prj.root().join(".vscode/settings.json"); assert!(settings.is_file()); @@ -411,8 +476,16 @@ forgetest!(can_init_vscode, |prj, cmd| { // checks that forge can init with template forgetest!(can_init_template, |prj, cmd| { prj.wipe(); - cmd.args(["init", "--template", "foundry-rs/forge-template"]).arg(prj.root()); - cmd.assert_non_empty_stdout(); + + cmd.args(["init", "--template", "foundry-rs/forge-template"]) + .arg(prj.root()) + .assert_success() + .stdout_eq(str![[r#" +Initializing [..] from https://github.com/foundry-rs/forge-template... + Initialized forge project + +"#]]); + assert!(prj.root().join(".git").exists()); assert!(prj.root().join("foundry.toml").exists()); assert!(prj.root().join("lib/forge-std").exists()); @@ -426,8 +499,14 @@ forgetest!(can_init_template, |prj, cmd| { forgetest!(can_init_template_with_branch, |prj, cmd| { prj.wipe(); cmd.args(["init", "--template", "foundry-rs/forge-template", "--branch", "test/deployments"]) - .arg(prj.root()); - cmd.assert_non_empty_stdout(); + .arg(prj.root()) + .assert_success() + .stdout_eq(str![[r#" +Initializing [..] from https://github.com/foundry-rs/forge-template... + Initialized forge project + +"#]]); + assert!(prj.root().join(".git").exists()); assert!(prj.root().join(".dapprc").exists()); assert!(prj.root().join("lib/ds-test").exists()); @@ -440,8 +519,13 @@ forgetest!(can_init_template_with_branch, |prj, cmd| { // checks that init fails when the provided template doesn't exist forgetest!(fail_init_nonexistent_template, |prj, cmd| { prj.wipe(); - cmd.args(["init", "--template", "a"]).arg(prj.root()); - cmd.assert_non_empty_stderr(); + cmd.args(["init", "--template", "a"]).arg(prj.root()).assert_failure().stderr_eq(str![[r#" +remote: Not Found +fatal: repository 'https://github.com/a/' not found +Error: +git fetch exited with code 128 + +"#]]); }); // checks that clone works @@ -457,8 +541,20 @@ forgetest!(can_clone, |prj, cmd| { next_etherscan_api_key().as_str(), "0x044b75f554b886A065b9567891e45c79542d7357", ]) - .arg(prj.root()); - cmd.assert_non_empty_stdout(); + .arg(prj.root()) + .assert_success() + .stdout_eq(str![[r#" +Downloading the source code of 0x044b75f554b886A065b9567891e45c79542d7357 from Etherscan... +Initializing [..]... +Installing forge-std in [..] (url: Some("https://github.com/foundry-rs/forge-std"), tag: None) + Installed forge-std [..] + Initialized forge project +Collecting the creation information of 0x044b75f554b886A065b9567891e45c79542d7357 from Etherscan... +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +"#]]); let s = read_string(&foundry_toml); let _config: BasicConfig = parse_with_profile(&s).unwrap().unwrap().1; @@ -475,8 +571,8 @@ forgetest!(can_clone_quiet, |prj, cmd| { "--quiet", "0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec", ]) - .arg(prj.root()); - cmd.assert_empty_stdout(); + .arg(prj.root()) + .assert_empty_stdout(); }); // checks that clone works with --no-remappings-txt @@ -493,8 +589,20 @@ forgetest!(can_clone_no_remappings_txt, |prj, cmd| { "--no-remappings-txt", "0x33e690aEa97E4Ef25F0d140F1bf044d663091DAf", ]) - .arg(prj.root()); - cmd.assert_non_empty_stdout(); + .arg(prj.root()) + .assert_success() + .stdout_eq(str![[r#" +Downloading the source code of 0x33e690aEa97E4Ef25F0d140F1bf044d663091DAf from Etherscan... +Initializing [..]... +Installing forge-std in [..] (url: Some("https://github.com/foundry-rs/forge-std"), tag: None) + Installed forge-std [..] + Initialized forge project +Collecting the creation information of 0x33e690aEa97E4Ef25F0d140F1bf044d663091DAf from Etherscan... +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +"#]]); let s = read_string(&foundry_toml); let _config: BasicConfig = parse_with_profile(&s).unwrap().unwrap().1; @@ -507,16 +615,21 @@ forgetest!(can_clone_keep_directory_structure, |prj, cmd| { let foundry_toml = prj.root().join(Config::FILE_NAME); assert!(!foundry_toml.exists()); - cmd.args([ - "clone", - "--etherscan-api-key", - next_etherscan_api_key().as_str(), - "--keep-directory-structure", - "0x33e690aEa97E4Ef25F0d140F1bf044d663091DAf", - ]) - .arg(prj.root()); - let out = cmd.unchecked_output(); - if out.stdout_lossy().contains("502 Bad Gateway") { + let output = cmd + .forge_fuse() + .args([ + "clone", + "--etherscan-api-key", + next_etherscan_api_key().as_str(), + "--keep-directory-structure", + "0x33e690aEa97E4Ef25F0d140F1bf044d663091DAf", + ]) + .arg(prj.root()) + .assert_success() + .get_output() + .stdout_lossy(); + + if output.contains("502 Bad Gateway") { // etherscan nginx proxy issue, skip this test: // // stdout: @@ -526,10 +639,9 @@ forgetest!(can_clone_keep_directory_structure, |prj, cmd| { // Gateway\r\n\r\n

502 Bad // Gateway

\r\n
nginx
\r\n\r\n\r\n" - eprintln!("Skipping test due to 502 Bad Gateway: {}", cmd.make_error_message(&out, false)); - return + eprintln!("Skipping test due to 502 Bad Gateway"); + return; } - cmd.ensure_success(&out).unwrap(); let s = read_string(&foundry_toml); let _config: BasicConfig = parse_with_profile(&s).unwrap().unwrap().1; @@ -557,15 +669,18 @@ forgetest!(can_clean_hardhat, PathStyle::HardHat, |prj, cmd| { forgetest_init!(can_clean_config, |prj, cmd| { let config = Config { out: "custom-out".into(), ..Default::default() }; prj.write_config(config); - cmd.arg("build"); - cmd.assert_non_empty_stdout(); + cmd.arg("build").assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +"#]]); // default test contract is written in custom out directory let artifact = prj.root().join(format!("custom-out/{TEMPLATE_TEST_CONTRACT_ARTIFACT_JSON}")); assert!(artifact.exists()); - cmd.forge_fuse().arg("clean"); - cmd.output(); + cmd.forge_fuse().arg("clean").assert_empty_stdout(); assert!(!artifact.exists()); }); @@ -586,24 +701,37 @@ forgetest_init!(can_clean_test_cache, |prj, cmd| { assert!(fuzz_cache_dir.exists()); assert!(invariant_cache_dir.exists()); - cmd.forge_fuse().arg("clean"); - cmd.output(); + cmd.forge_fuse().arg("clean").assert_empty_stdout(); assert!(!fuzz_cache_dir.exists()); assert!(!invariant_cache_dir.exists()); }); // checks that extra output works forgetest_init!(can_emit_extra_output, |prj, cmd| { - cmd.args(["build", "--extra-output", "metadata"]); - cmd.assert_non_empty_stdout(); + prj.clear(); + + cmd.args(["build", "--extra-output", "metadata"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +"#]]); let artifact_path = prj.paths().artifacts.join(TEMPLATE_CONTRACT_ARTIFACT_JSON); let artifact: ConfigurableContractArtifact = foundry_compilers::utils::read_json_file(&artifact_path).unwrap(); assert!(artifact.metadata.is_some()); - cmd.forge_fuse().args(["build", "--extra-output-files", "metadata", "--force"]).root_arg(); - cmd.assert_non_empty_stdout(); + cmd.forge_fuse() + .args(["build", "--extra-output-files", "metadata", "--force"]) + .root_arg() + .assert_success() + .stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +"#]]); let metadata_path = prj.paths().artifacts.join(format!("{TEMPLATE_CONTRACT_ARTIFACT_BASE}.metadata.json")); @@ -612,8 +740,14 @@ forgetest_init!(can_emit_extra_output, |prj, cmd| { // checks that extra output works forgetest_init!(can_emit_multiple_extra_output, |prj, cmd| { - cmd.args(["build", "--extra-output", "metadata", "ir-optimized", "--extra-output", "ir"]); - cmd.assert_non_empty_stdout(); + cmd.args(["build", "--extra-output", "metadata", "ir-optimized", "--extra-output", "ir"]) + .assert_success() + .stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +"#]]); let artifact_path = prj.paths().artifacts.join(TEMPLATE_CONTRACT_ARTIFACT_JSON); let artifact: ConfigurableContractArtifact = @@ -631,8 +765,14 @@ forgetest_init!(can_emit_multiple_extra_output, |prj, cmd| { "evm.bytecode.sourceMap", "--force", ]) - .root_arg(); - cmd.assert_non_empty_stdout(); + .root_arg() + .assert_success() + .stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +"#]]); let metadata_path = prj.paths().artifacts.join(format!("{TEMPLATE_CONTRACT_ARTIFACT_BASE}.metadata.json")); @@ -659,10 +799,30 @@ contract Greeter { ) .unwrap(); - cmd.arg("build"); + cmd.arg("build").assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful with warnings: +Warning (5667): Unused function parameter. Remove or comment out the variable name to silence this warning. + [FILE]:5:18: + | +5 | function foo(uint256 a) public { + | ^^^^^^^^^ + +Warning (2072): Unused local variable. + [FILE]:6:9: + | +6 | uint256 x = 1; + | ^^^^^^^^^ + +Warning (2018): Function state mutability can be restricted to pure + [FILE]:5:5: + | +5 | function foo(uint256 a) public { + | ^ (Relevant source part starts here and spans across multiple lines). + - let output = cmd.stdout_lossy(); - assert!(output.contains("Warning"), "{output}"); +"#]]); }); // Tests that direct import paths are handled correctly @@ -700,13 +860,12 @@ library FooLib { ) .unwrap(); - cmd.arg("build"); - - assert!(cmd.stdout_lossy().ends_with( - " + cmd.arg("build").assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] Compiler run successful! -" - )); + +"#]]); }); // tests that the `inspect` command works correctly @@ -726,17 +885,18 @@ contract Foo { ) .unwrap(); - let check_output = |output: String| { - let output = output.trim(); - assert!(output.starts_with("0x") && hex::decode(output).is_ok(), "{output}"); - }; + cmd.arg("inspect").arg(contract_name).arg("bytecode").assert_success().stdout_eq(str![[r#" +0x60806040[..] - cmd.arg("inspect").arg(contract_name).arg("bytecode"); - check_output(cmd.stdout_lossy()); +"#]]); let info = format!("src/{}:{}", path.file_name().unwrap().to_string_lossy(), contract_name); - cmd.forge_fuse().arg("inspect").arg(info).arg("bytecode"); - check_output(cmd.stdout_lossy()); + cmd.forge_fuse().arg("inspect").arg(info).arg("bytecode").assert_success().stdout_eq(str![[ + r#" +0x60806040[..] + +"# + ]]); }); // test that `forge snapshot` commands work @@ -757,7 +917,7 @@ contract ATest is DSTest { .unwrap(); cmd.args(["snapshot"]).assert_success().stdout_eq(str![[r#" -Compiling 2 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! @@ -769,8 +929,16 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) "#]]); - cmd.arg("--check"); - let _ = cmd.output(); + cmd.arg("--check").assert_success().stdout_eq(str![[r#" +No files changed, compilation skipped + +Ran 1 test for src/ATest.t.sol:ATest +[PASS] testExample() ([GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"#]]); }); // test that `forge build` does not print `(with warnings)` if file path is ignored @@ -793,20 +961,27 @@ contract A { ) .unwrap(); - cmd.args(["build", "--force"]); - let out = cmd.stdout_lossy(); - // expect no warning as path is ignored - assert!(out.contains("Compiler run successful!")); - assert!(!out.contains("Compiler run successful with warnings:")); + cmd.args(["build", "--force"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +"#]]); // Reconfigure without ignored paths or error codes and check for warnings // need to reset empty error codes as default would set some error codes prj.write_config(Config { ignored_error_codes: vec![], ..Default::default() }); - let out = cmd.stdout_lossy(); - // expect warnings as path is not ignored - assert!(out.contains("Compiler run successful with warnings:"), "{out}"); - assert!(out.contains("Warning") && out.contains("SPDX-License-Identifier"), "{out}"); + cmd.forge_fuse().args(["build", "--force"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful with warnings: +Warning (1878): SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information. +Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information. +[FILE] + + +"#]]); }); // test that `forge build` does not print `(with warnings)` if there arent any @@ -827,19 +1002,27 @@ contract A { ) .unwrap(); - cmd.args(["build", "--force"]); - let out = cmd.stdout_lossy(); - // no warnings - assert!(out.contains("Compiler run successful!")); - assert!(!out.contains("Compiler run successful with warnings:")); + cmd.args(["build", "--force"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +"#]]); // don't ignore errors let config = Config { ignored_error_codes: vec![], ..Default::default() }; prj.write_config(config); - let out = cmd.stdout_lossy(); - assert!(out.contains("Compiler run successful with warnings:"), "{out}"); - assert!(out.contains("Warning") && out.contains("SPDX-License-Identifier"), "{out}"); + cmd.forge_fuse().args(["build", "--force"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful with warnings: +Warning (1878): SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information. +Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information. +[FILE] + + +"#]]); }); // test that `forge build` compiles when severity set to error, fails when set to warning, and @@ -859,14 +1042,30 @@ contract A { .unwrap(); // there are no errors - cmd.args(["build", "--force"]); - let out = cmd.stdout_lossy(); - assert!(out.contains("Compiler run successful with warnings:"), "{out}"); + cmd.args(["build", "--force"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful with warnings: +Warning (1878): SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information. +Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information. +[FILE] + + +"#]]); // warning fails to compile let config = Config { ignored_error_codes: vec![], deny_warnings: true, ..Default::default() }; prj.write_config(config); - cmd.assert_err(); + + cmd.forge_fuse().args(["build", "--force"]).assert_failure().stderr_eq(str![[r#" +Error: +Compiler run failed: +Warning (1878): SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information. +Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information. +[FILE] + + +"#]]); // ignores error code and compiles let config = Config { @@ -875,10 +1074,13 @@ contract A { ..Default::default() }; prj.write_config(config); - let out = cmd.stdout_lossy(); - assert!(out.contains("Compiler run successful!")); - assert!(!out.contains("Compiler run successful with warnings:")); + cmd.forge_fuse().args(["build", "--force"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +"#]]); }); // test that a failing `forge build` does not impact followup builds @@ -910,8 +1112,14 @@ contract BTest is DSTest { ) .unwrap(); - cmd.arg("build"); - cmd.assert_non_empty_stdout(); + cmd.arg("build").assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +... +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +"#]]); + prj.assert_cache_exists(); prj.assert_artifacts_dir_exists(); @@ -928,16 +1136,34 @@ contract CTest is DSTest { prj.add_source("CTest.t.sol", syntax_err).unwrap(); // `forge build --force` which should fail - cmd.arg("--force"); - cmd.assert_err(); + cmd.forge_fuse().args(["build", "--force"]).assert_failure().stderr_eq(str![[r#" +Error: +Compiler run failed: +Error (2314): Expected ';' but got identifier + [FILE]:7:19: + | +7 | THIS WILL CAUSE AN ERROR + | ^^^^^ + + +"#]]); // but ensure this cleaned cache and artifacts assert!(!prj.paths().artifacts.exists()); assert!(!prj.cache().exists()); // still errors - cmd.forge_fuse().arg("build"); - cmd.assert_err(); + cmd.forge_fuse().args(["build", "--force"]).assert_failure().stderr_eq(str![[r#" +Error: +Compiler run failed: +Error (2314): Expected ';' but got identifier + [FILE]:7:19: + | +7 | THIS WILL CAUSE AN ERROR + | ^^^^^ + + +"#]]); // resolve the error by replacing the file prj.add_source( @@ -953,7 +1179,13 @@ contract CTest is DSTest { ) .unwrap(); - cmd.assert_non_empty_stdout(); + cmd.forge_fuse().args(["build", "--force"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +"#]]); + prj.assert_cache_exists(); prj.assert_artifacts_dir_exists(); @@ -962,7 +1194,17 @@ contract CTest is DSTest { // introduce the error again but building without force prj.add_source("CTest.t.sol", syntax_err).unwrap(); - cmd.assert_err(); + cmd.forge_fuse().arg("build").assert_failure().stderr_eq(str![[r#" +Error: +Compiler run failed: +Error (2314): Expected ';' but got identifier + [FILE]:7:19: + | +7 | THIS WILL CAUSE AN ERROR + | ^^^^^ + + +"#]]); // ensure unchanged cache file let cache_after = fs::read_to_string(prj.cache()).unwrap(); @@ -981,8 +1223,15 @@ forgetest!(can_install_and_remove, |prj, cmd| { let forge_std_mod = git_mod.join("forge-std"); let install = |cmd: &mut TestCommand| { - cmd.forge_fuse().args(["install", "foundry-rs/forge-std", "--no-commit"]); - cmd.assert_non_empty_stdout(); + cmd.forge_fuse() + .args(["install", "foundry-rs/forge-std", "--no-commit"]) + .assert_success() + .stdout_eq(str![[r#" +Installing forge-std in [..] (url: Some("https://github.com/foundry-rs/forge-std"), tag: None) + Installed forge-std [..] + +"#]]); + assert!(forge_std.exists()); assert!(forge_std_mod.exists()); @@ -991,8 +1240,14 @@ forgetest!(can_install_and_remove, |prj, cmd| { }; let remove = |cmd: &mut TestCommand, target: &str| { - cmd.forge_fuse().args(["remove", "--force", target]); - cmd.assert_non_empty_stdout(); + // TODO: flaky behavior with URL, sometimes it is None, sometimes it is Some("https://github.com/lib/forge-std") + cmd.forge_fuse().args(["remove", "--force", target]).assert_success().stdout_eq(str![[ + r#" +Removing 'forge-std' in [..], (url: [..], tag: None) + +"# + ]]); + assert!(!forge_std.exists()); assert!(!forge_std_mod.exists()); let submods = read_string(&git_mod_file); @@ -1017,8 +1272,8 @@ forgetest!(can_install_empty, |prj, cmd| { // create initial commit fs::write(prj.root().join("README.md"), "Initial commit").unwrap(); - cmd.git_add().unwrap(); - cmd.git_commit("Initial commit").unwrap(); + cmd.git_add(); + cmd.git_commit("Initial commit"); cmd.forge_fuse().args(["install"]); cmd.assert_empty_stdout(); @@ -1036,8 +1291,15 @@ forgetest!(can_reinstall_after_manual_remove, |prj, cmd| { let forge_std_mod = git_mod.join("forge-std"); let install = |cmd: &mut TestCommand| { - cmd.forge_fuse().args(["install", "foundry-rs/forge-std", "--no-commit"]); - cmd.assert_non_empty_stdout(); + cmd.forge_fuse() + .args(["install", "foundry-rs/forge-std", "--no-commit"]) + .assert_success() + .stdout_eq(str![[r#" +Installing forge-std in [..] (url: Some("https://github.com/foundry-rs/forge-std"), tag: None) + Installed forge-std [..] + +"#]]); + assert!(forge_std.exists()); assert!(forge_std_mod.exists()); @@ -1098,8 +1360,14 @@ forgetest!( let package_mod = git_mod.join("forge-5980-test"); // install main dependency - cmd.forge_fuse().args(["install", "evalir/forge-5980-test", "--no-commit"]); - cmd.assert_non_empty_stdout(); + cmd.forge_fuse() + .args(["install", "evalir/forge-5980-test", "--no-commit"]) + .assert_success() + .stdout_eq(str![[r#" +Installing forge-5980-test in [..] (url: Some("https://github.com/evalir/forge-5980-test"), tag: None) + Installed forge-5980-test + +"#]]); // assert paths exist assert!(package.exists()); @@ -1111,8 +1379,7 @@ forgetest!( // try to update the top-level dependency; there should be no update for this dependency, // but its sub-dependency has upstream (breaking) changes; forge should not attempt to // update the sub-dependency - cmd.forge_fuse().args(["update", "lib/forge-5980-test"]); - cmd.stdout_lossy(); + cmd.forge_fuse().args(["update", "lib/forge-5980-test"]).assert_empty_stdout(); // add explicit remappings for test file let config = Config { @@ -1142,9 +1409,12 @@ contract CounterCopy is Counter { .unwrap(); // build and check output - cmd.forge_fuse().arg("build"); - let output = cmd.stdout_lossy(); - assert!(output.contains("Compiler run successful",)); + cmd.forge_fuse().arg("build").assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +"#]]); } ); @@ -1245,23 +1515,36 @@ contract ContractThreeTest is DSTest { gas_reports_ignore: (vec![]), ..Default::default() }); - cmd.forge_fuse(); - let first_out = cmd.arg("test").arg("--gas-report").stdout_lossy(); + + let first_out = cmd + .forge_fuse() + .arg("test") + .arg("--gas-report") + .assert_success() + .get_output() + .stdout_lossy(); assert!(first_out.contains("foo") && first_out.contains("bar") && first_out.contains("baz")); - cmd.forge_fuse(); prj.write_config(Config { gas_reports: (vec![]), ..Default::default() }); - cmd.forge_fuse(); - let second_out = cmd.arg("test").arg("--gas-report").stdout_lossy(); + let second_out = cmd + .forge_fuse() + .arg("test") + .arg("--gas-report") + .assert_success() + .get_output() + .stdout_lossy(); assert!(second_out.contains("foo") && second_out.contains("bar") && second_out.contains("baz")); - cmd.forge_fuse(); prj.write_config(Config { gas_reports: (vec!["*".to_string()]), ..Default::default() }); - cmd.forge_fuse(); - let third_out = cmd.arg("test").arg("--gas-report").stdout_lossy(); + let third_out = cmd + .forge_fuse() + .arg("test") + .arg("--gas-report") + .assert_success() + .get_output() + .stdout_lossy(); assert!(third_out.contains("foo") && third_out.contains("bar") && third_out.contains("baz")); - cmd.forge_fuse(); prj.write_config(Config { gas_reports: (vec![ "ContractOne".to_string(), @@ -1270,8 +1553,13 @@ contract ContractThreeTest is DSTest { ]), ..Default::default() }); - cmd.forge_fuse(); - let fourth_out = cmd.arg("test").arg("--gas-report").stdout_lossy(); + let fourth_out = cmd + .forge_fuse() + .arg("test") + .arg("--gas-report") + .assert_success() + .get_output() + .stdout_lossy(); assert!(fourth_out.contains("foo") && fourth_out.contains("bar") && fourth_out.contains("baz")); }); @@ -1369,7 +1657,8 @@ contract ContractThreeTest is DSTest { // report for One prj.write_config(Config { gas_reports: vec!["ContractOne".to_string()], ..Default::default() }); cmd.forge_fuse(); - let first_out = cmd.arg("test").arg("--gas-report").stdout_lossy(); + let first_out = + cmd.arg("test").arg("--gas-report").assert_success().get_output().stdout_lossy(); assert!( first_out.contains("foo") && !first_out.contains("bar") && !first_out.contains("baz"), "foo:\n{first_out}" @@ -1378,7 +1667,8 @@ contract ContractThreeTest is DSTest { // report for Two prj.write_config(Config { gas_reports: vec!["ContractTwo".to_string()], ..Default::default() }); cmd.forge_fuse(); - let second_out = cmd.arg("test").arg("--gas-report").stdout_lossy(); + let second_out = + cmd.arg("test").arg("--gas-report").assert_success().get_output().stdout_lossy(); assert!( !second_out.contains("foo") && second_out.contains("bar") && !second_out.contains("baz"), "bar:\n{second_out}" @@ -1390,7 +1680,8 @@ contract ContractThreeTest is DSTest { ..Default::default() }); cmd.forge_fuse(); - let third_out = cmd.arg("test").arg("--gas-report").stdout_lossy(); + let third_out = + cmd.arg("test").arg("--gas-report").assert_success().get_output().stdout_lossy(); assert!( !third_out.contains("foo") && !third_out.contains("bar") && third_out.contains("baz"), "baz:\n{third_out}" @@ -1495,7 +1786,8 @@ contract ContractThreeTest is DSTest { ..Default::default() }); cmd.forge_fuse(); - let first_out = cmd.arg("test").arg("--gas-report").stdout_lossy(); + let first_out = + cmd.arg("test").arg("--gas-report").assert_success().get_output().stdout_lossy(); assert!(!first_out.contains("foo") && first_out.contains("bar") && first_out.contains("baz")); // ignore ContractTwo @@ -1506,7 +1798,8 @@ contract ContractThreeTest is DSTest { ..Default::default() }); cmd.forge_fuse(); - let second_out = cmd.arg("test").arg("--gas-report").stdout_lossy(); + let second_out = + cmd.arg("test").arg("--gas-report").assert_success().get_output().stdout_lossy(); assert!( second_out.contains("foo") && !second_out.contains("bar") && second_out.contains("baz") ); @@ -1523,7 +1816,8 @@ contract ContractThreeTest is DSTest { ..Default::default() }); cmd.forge_fuse(); - let third_out = cmd.arg("test").arg("--gas-report").stdout_lossy(); + let third_out = + cmd.arg("test").arg("--gas-report").assert_success().get_output().stdout_lossy(); assert!(third_out.contains("foo") && third_out.contains("bar") && third_out.contains("baz")); }); @@ -1566,9 +1860,12 @@ forgetest_init!(can_use_absolute_imports, |prj, cmd| { ) .unwrap(); - cmd.arg("build"); - let stdout = cmd.stdout_lossy(); - assert!(stdout.contains("Compiler run successful")); + cmd.arg("build").assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +"#]]); }); // @@ -1609,9 +1906,12 @@ contract MyTest is IMyTest {} ) .unwrap(); - cmd.arg("build"); - let stdout = cmd.stdout_lossy(); - assert!(stdout.contains("Compiler run successful"), "{stdout}"); + cmd.arg("build").assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +"#]]); }); // checks `forge inspect irOptimized works @@ -1621,61 +1921,90 @@ forgetest_init!(can_inspect_ir_optimized, |_prj, cmd| { }); // checks forge bind works correctly on the default project -forgetest_init!(can_bind, |_prj, cmd| { - cmd.arg("bind"); - cmd.assert_non_empty_stdout(); +forgetest_init!(can_bind, |prj, cmd| { + prj.clear(); + + cmd.arg("bind").assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! +Generating bindings for [..] contracts +Bindings have been generated to [..] + +"#]]); }); // checks missing dependencies are auto installed forgetest_init!(can_install_missing_deps_test, |prj, cmd| { + prj.clear(); + // wipe forge-std let forge_std_dir = prj.root().join("lib/forge-std"); pretty_err(&forge_std_dir, fs::remove_dir_all(&forge_std_dir)); - cmd.arg("test"); + cmd.arg("test").assert_success().stdout_eq(str![[r#" +Missing dependencies found. Installing now... + +[UPDATING_DEPENDENCIES] +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 2 tests for test/Counter.t.sol:CounterTest +[PASS] testFuzz_SetNumber(uint256) (runs: 256, [AVG_GAS]) +[PASS] test_Increment() ([GAS]) +Suite result: ok. 2 passed; 0 failed; 0 skipped; [ELAPSED] - let output = cmd.stdout_lossy(); - assert!(output.contains("Missing dependencies found. Installing now"), "{}", output); - assert!(output.contains("[PASS]"), "{}", output); +Ran 1 test suite [ELAPSED]: 2 tests passed, 0 failed, 0 skipped (2 total tests) + +"#]]); }); // checks missing dependencies are auto installed forgetest_init!(can_install_missing_deps_build, |prj, cmd| { + prj.clear(); + // wipe forge-std let forge_std_dir = prj.root().join("lib/forge-std"); pretty_err(&forge_std_dir, fs::remove_dir_all(&forge_std_dir)); - cmd.arg("build"); + // Build the project + cmd.arg("build").assert_success().stdout_eq(str![[r#" +Missing dependencies found. Installing now... + +[UPDATING_DEPENDENCIES] +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +"#]]); - let output = cmd.stdout_lossy(); - assert!(output.contains("Missing dependencies found. Installing now"), "{output}"); + // Expect compilation to be skipped as no files have changed + cmd.arg("build").assert_success().stdout_eq(str![[r#" +No files changed, compilation skipped - // re-run - let output = cmd.stdout_lossy(); - assert!(!output.contains("Missing dependencies found. Installing now"), "{output}"); - assert!(output.contains("No files changed, compilation skipped"), "{output}"); +"#]]); }); // checks that extra output works forgetest_init!(can_build_skip_contracts, |prj, cmd| { prj.clear(); - // only builds the single template contract `src/*` + // Only builds the single template contract `src/*` cmd.args(["build", "--skip", "tests", "--skip", "scripts"]).assert_success().stdout_eq(str![[ r#" -... -Compiling 1 files [..] -[..] +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] Compiler run successful! -... + "# ]]); - // re-run command - let out = cmd.stdout_lossy(); + // Expect compilation to be skipped as no files have changed + cmd.arg("build").assert_success().stdout_eq(str![[r#" +No files changed, compilation skipped - // unchanged - assert!(out.contains("No files changed, compilation skipped"), "{}", out); +"#]]); }); forgetest_init!(can_build_skip_glob, |prj, cmd| { @@ -1693,7 +2022,7 @@ function test_run() external {} cmd.args(["build", "--skip", "*/test/**", "--skip", "*/script/**", "--force"]) .assert_success() .stdout_eq(str![[r#" -Compiling 1 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! @@ -1703,7 +2032,7 @@ Compiler run successful! .args(["build", "--skip", "./test/**", "--skip", "./script/**", "--force"]) .assert_success() .stdout_eq(str![[r#" -Compiling 1 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! @@ -1740,7 +2069,7 @@ function test_bar() external {} // Build 2 files within test dir prj.clear(); cmd.args(["build", "test", "--force"]).assert_success().stdout_eq(str![[r#" -Compiling 2 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! @@ -1750,7 +2079,7 @@ Compiler run successful! prj.clear(); cmd.forge_fuse(); cmd.args(["build", "src", "--force"]).assert_success().stdout_eq(str![[r#" -Compiling 1 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! @@ -1760,7 +2089,7 @@ Compiler run successful! prj.clear(); cmd.forge_fuse(); cmd.args(["build", "src", "test", "--force"]).assert_success().stdout_eq(str![[r#" -Compiling 3 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! @@ -1770,7 +2099,7 @@ Compiler run successful! prj.clear(); cmd.forge_fuse(); cmd.args(["build", "test/Bar.sol", "--force"]).assert_success().stdout_eq(str![[r#" -Compiling 1 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! @@ -1781,45 +2110,44 @@ Compiler run successful! forgetest_init!(can_build_sizes_repeatedly, |prj, cmd| { prj.clear_cache(); - cmd.args(["build", "--sizes"]); - let out = cmd.stdout_lossy(); - - // contains: Counter ┆ 0.247 ┆ 24.329 - assert!(out.contains(TEMPLATE_CONTRACT)); + cmd.args(["build", "--sizes"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! +| Contract | Size (B) | Margin (B) | +|----------|----------|------------| +| Counter | 247 | 24,329 | - // get the entire table - let table = out.split("Compiler run successful!").nth(1).unwrap().trim(); - let unchanged = cmd.stdout_lossy(); - assert!(unchanged.contains(table), "{}", table); +"#]]); }); // checks that build --names includes all contracts even if unchanged forgetest_init!(can_build_names_repeatedly, |prj, cmd| { prj.clear_cache(); - cmd.args(["build", "--names"]); - let out = cmd.stdout_lossy(); - - assert!(out.contains(TEMPLATE_CONTRACT)); - - // get the entire list - let list = out.split("Compiler run successful!").nth(1).unwrap().trim(); + cmd.args(["build", "--names"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + compiler version: [..] + - [..] +... - let unchanged = cmd.stdout_lossy(); - assert!(unchanged.contains(list), "{}", list); +"#]]); }); // forgetest_init!(can_inspect_counter_pretty, |prj, cmd| { - cmd.args(["inspect", "src/Counter.sol:Counter", "abi", "--pretty"]); - let output = cmd.stdout_lossy(); - assert_eq!( - output.trim(), - "interface Counter { + cmd.args(["inspect", "src/Counter.sol:Counter", "abi", "--pretty"]).assert_success().stdout_eq( + str![[r#" +interface Counter { function increment() external; function number() external view returns (uint256); function setNumber(uint256 newNumber) external; -}" +} + + +"#]], ); }); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 88ac186b2..2cb11e72f 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -14,7 +14,7 @@ use foundry_config::{ use foundry_evm::opts::EvmOpts; use foundry_test_utils::{ foundry_compilers::artifacts::{remappings::Remapping, EvmVersion}, - util::{pretty_err, TestCommand, OTHER_SOLC_VERSION}, + util::{pretty_err, OutputExt, TestCommand, OTHER_SOLC_VERSION}, }; use path_slash::PathBufExt; use similar_asserts::assert_eq; @@ -160,10 +160,10 @@ forgetest!(can_extract_config_values, |prj, cmd| { // tests config gets printed to std out forgetest!(can_show_config, |prj, cmd| { - cmd.arg("config"); let expected = Config::load_with_root(prj.root()).to_string_pretty().unwrap().trim().to_string(); - assert_eq!(expected, cmd.stdout_lossy().trim().to_string()); + let output = cmd.arg("config").assert_success().get_output().stdout_lossy().trim().to_string(); + assert_eq!(expected, output); }); // checks that config works @@ -188,9 +188,9 @@ forgetest_init!(can_override_config, |prj, cmd| { Remapping::from(profile.remappings[0].clone()).to_string() ); - cmd.arg("config"); - let expected = profile.to_string_pretty().unwrap(); - assert_eq!(expected.trim().to_string(), cmd.stdout_lossy().trim().to_string()); + let expected = profile.to_string_pretty().unwrap().trim().to_string(); + let output = cmd.arg("config").assert_success().get_output().stdout_lossy().trim().to_string(); + assert_eq!(expected, output); // remappings work let remappings_txt = @@ -236,9 +236,16 @@ forgetest_init!(can_override_config, |prj, cmd| { std::env::remove_var("DAPP_REMAPPINGS"); pretty_err(&remappings_txt, fs::remove_file(&remappings_txt)); - cmd.set_cmd(prj.forge_bin()).args(["config", "--basic"]); - let expected = profile.into_basic().to_string_pretty().unwrap(); - assert_eq!(expected.trim().to_string(), cmd.stdout_lossy().trim().to_string()); + let expected = profile.into_basic().to_string_pretty().unwrap().trim().to_string(); + let output = cmd + .forge_fuse() + .args(["config", "--basic"]) + .assert_success() + .get_output() + .stdout_lossy() + .trim() + .to_string(); + assert_eq!(expected, output); }); forgetest_init!(can_parse_remappings_correctly, |prj, cmd| { @@ -255,13 +262,18 @@ forgetest_init!(can_parse_remappings_correctly, |prj, cmd| { // the loaded config has resolved, absolute paths assert_eq!("forge-std/=lib/forge-std/src/", Remapping::from(r.clone()).to_string()); - cmd.arg("config"); - let expected = profile.to_string_pretty().unwrap(); - assert_eq!(expected.trim().to_string(), cmd.stdout_lossy().trim().to_string()); + let expected = profile.to_string_pretty().unwrap().trim().to_string(); + let output = cmd.arg("config").assert_success().get_output().stdout_lossy().trim().to_string(); + assert_eq!(expected, output); let install = |cmd: &mut TestCommand, dep: &str| { - cmd.forge_fuse().args(["install", dep, "--no-commit"]); - cmd.assert_non_empty_stdout(); + cmd.forge_fuse().args(["install", dep, "--no-commit"]).assert_success().stdout_eq(str![[ + r#" +Installing solmate in [..] (url: Some("https://github.com/transmissions11/solmate"), tag: None) + Installed solmate + +"# + ]]); }; install(&mut cmd, "transmissions11/solmate"); @@ -288,9 +300,16 @@ forgetest_init!(can_parse_remappings_correctly, |prj, cmd| { ); pretty_err(&remappings_txt, fs::remove_file(&remappings_txt)); - cmd.set_cmd(prj.forge_bin()).args(["config", "--basic"]); - let expected = profile.into_basic().to_string_pretty().unwrap(); - assert_eq!(expected.trim().to_string(), cmd.stdout_lossy().trim().to_string()); + let expected = profile.into_basic().to_string_pretty().unwrap().trim().to_string(); + let output = cmd + .forge_fuse() + .args(["config", "--basic"]) + .assert_success() + .get_output() + .stdout_lossy() + .trim() + .to_string(); + assert_eq!(expected, output); }); forgetest_init!(can_detect_config_vals, |prj, _cmd| { @@ -348,9 +367,12 @@ contract Greeter {} let config = Config { solc: Some(OTHER_SOLC_VERSION.into()), ..Default::default() }; prj.write_config(config); - cmd.arg("build"); + cmd.arg("build").assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! - assert!(cmd.stdout_lossy().contains("Compiler run successful!")); +"#]]); }); // tests that `--use ` works @@ -364,25 +386,45 @@ contract Foo {} ) .unwrap(); - cmd.args(["build", "--use", OTHER_SOLC_VERSION]); - let stdout = cmd.stdout_lossy(); - assert!(stdout.contains("Compiler run successful")); + cmd.args(["build", "--use", OTHER_SOLC_VERSION]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +"#]]); cmd.forge_fuse() .args(["build", "--force", "--use", &format!("solc:{OTHER_SOLC_VERSION}")]) - .root_arg(); - let stdout = cmd.stdout_lossy(); - assert!(stdout.contains("Compiler run successful")); + .root_arg() + .assert_success() + .stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +"#]]); // fails to use solc that does not exist cmd.forge_fuse().args(["build", "--use", "this/solc/does/not/exist"]); - assert!(cmd.stderr_lossy().contains("`solc` this/solc/does/not/exist does not exist")); + cmd.assert_failure().stderr_eq(str![[r#" +Error: +`solc` this/solc/does/not/exist does not exist + +"#]]); // `OTHER_SOLC_VERSION` was installed in previous step, so we can use the path to this directly let local_solc = Solc::find_or_install(&OTHER_SOLC_VERSION.parse().unwrap()).unwrap(); - cmd.forge_fuse().args(["build", "--force", "--use"]).arg(local_solc.solc).root_arg(); - let stdout = cmd.stdout_lossy(); - assert!(stdout.contains("Compiler run successful")); + cmd.forge_fuse() + .args(["build", "--force", "--use"]) + .arg(local_solc.solc) + .root_arg() + .assert_success() + .stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +"#]]); }); // test to ensure yul optimizer can be set as intended @@ -571,8 +613,13 @@ forgetest!(can_update_libs_section, |prj, cmd| { let init = Config { libs: vec!["node_modules".into()], ..Default::default() }; prj.write_config(init); - cmd.args(["install", "foundry-rs/forge-std", "--no-commit"]); - cmd.assert_non_empty_stdout(); + cmd.args(["install", "foundry-rs/forge-std", "--no-commit"]).assert_success().stdout_eq(str![ + [r#" +Installing forge-std in [..] (url: Some("https://github.com/foundry-rs/forge-std"), tag: None) + Installed forge-std [..] + +"#] + ]); let config = cmd.forge_fuse().config(); // `lib` was added automatically @@ -580,8 +627,14 @@ forgetest!(can_update_libs_section, |prj, cmd| { assert_eq!(config.libs, expected); // additional install don't edit `libs` - cmd.forge_fuse().args(["install", "dapphub/ds-test", "--no-commit"]); - cmd.assert_non_empty_stdout(); + cmd.forge_fuse() + .args(["install", "dapphub/ds-test", "--no-commit"]) + .assert_success() + .stdout_eq(str![[r#" +Installing ds-test in [..] (url: Some("https://github.com/dapphub/ds-test"), tag: None) + Installed ds-test + +"#]]); let config = cmd.forge_fuse().config(); assert_eq!(config.libs, expected); @@ -592,8 +645,13 @@ forgetest!(can_update_libs_section, |prj, cmd| { forgetest!(config_emit_warnings, |prj, cmd| { cmd.git_init(); - cmd.args(["install", "foundry-rs/forge-std", "--no-commit"]); - cmd.assert_non_empty_stdout(); + cmd.args(["install", "foundry-rs/forge-std", "--no-commit"]).assert_success().stdout_eq(str![ + [r#" +Installing forge-std in [..] (url: Some("https://github.com/foundry-rs/forge-std"), tag: None) + Installed forge-std [..] + +"#] + ]); let faulty_toml = r"[default] src = 'src' @@ -603,16 +661,12 @@ forgetest!(config_emit_warnings, |prj, cmd| { fs::write(prj.root().join("foundry.toml"), faulty_toml).unwrap(); fs::write(prj.root().join("lib").join("forge-std").join("foundry.toml"), faulty_toml).unwrap(); - cmd.forge_fuse().args(["config"]); - let output = cmd.execute(); - assert!(output.status.success()); - assert_eq!( - String::from_utf8_lossy(&output.stderr) - .lines() - .filter(|line| line.contains("unknown config section") && line.contains("[default]")) - .count(), - 1, - ); + cmd.forge_fuse().args(["config"]).assert_success().stderr_eq(str![[r#" +warning: Found unknown config section in foundry.toml: [default] +This notation for profiles has been deprecated and may result in the profile not being registered in future versions. +Please use [profile.default] instead or run `forge config --fix`. + +"#]]); }); forgetest_init!(can_skip_remappings_auto_detection, |prj, cmd| { @@ -705,8 +759,11 @@ forgetest_init!(can_resolve_symlink_fs_permissions, |prj, cmd| { // tests if evm version is normalized for config output forgetest!(normalize_config_evm_version, |_prj, cmd| { - cmd.args(["config", "--use", "0.8.0", "--json"]); - let output = cmd.stdout_lossy(); + let output = cmd + .args(["config", "--use", "0.8.0", "--json"]) + .assert_success() + .get_output() + .stdout_lossy(); let config: Config = serde_json::from_str(&output).unwrap(); assert_eq!(config.evm_version, EvmVersion::Istanbul); }); diff --git a/crates/forge/tests/cli/create.rs b/crates/forge/tests/cli/create.rs index c7071dcf5..ebf8c81db 100644 --- a/crates/forge/tests/cli/create.rs +++ b/crates/forge/tests/cli/create.rs @@ -10,7 +10,7 @@ use foundry_compilers::artifacts::{remappings::Remapping, BytecodeHash}; use foundry_config::Config; use foundry_test_utils::{ forgetest, forgetest_async, str, - util::{TestCommand, TestProject}, + util::{OutputExt, TestCommand, TestProject}, }; use std::str::FromStr; @@ -104,12 +104,16 @@ where { if let Some(info) = info { let contract_path = f(&prj); - cmd.arg("create"); - cmd.args(info.create_args()).arg(contract_path); - let out = cmd.stdout_lossy(); - let _address = utils::parse_deployed_address(out.as_str()) - .unwrap_or_else(|| panic!("Failed to parse deployer {out}")); + let output = cmd + .arg("create") + .args(info.create_args()) + .arg(contract_path) + .assert_success() + .get_output() + .stdout_lossy(); + let _address = utils::parse_deployed_address(output.as_str()) + .unwrap_or_else(|| panic!("Failed to parse deployer {output}")); } } @@ -151,7 +155,7 @@ forgetest_async!(can_create_template_contract, |prj, cmd| { ]); cmd.assert().stdout_eq(str![[r#" -Compiling 1 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 @@ -192,7 +196,7 @@ forgetest_async!(can_create_using_unlocked, |prj, cmd| { ]); cmd.assert().stdout_eq(str![[r#" -Compiling 1 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 @@ -249,7 +253,7 @@ contract ConstructorContract { ]) .assert_success() .stdout_eq(str![[r#" -Compiling 1 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 @@ -286,7 +290,7 @@ contract TupleArrayConstructorContract { ]) .assert() .stdout_eq(str![[r#" -Compiling 1 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 @@ -323,15 +327,29 @@ contract UniswapV2Swap { ) .unwrap(); - cmd.forge_fuse().args([ - "create", - "./src/UniswapV2Swap.sol:UniswapV2Swap", - "--rpc-url", - rpc.as_str(), - "--private-key", - pk.as_str(), - ]); + cmd.forge_fuse() + .args([ + "create", + "./src/UniswapV2Swap.sol:UniswapV2Swap", + "--rpc-url", + rpc.as_str(), + "--private-key", + pk.as_str(), + ]) + .assert_success() + .stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful with warnings: +Warning (2018): Function state mutability can be restricted to pure + [FILE]:6:5: + | +6 | function pairInfo() public view returns (uint reserveA, uint reserveB, uint totalSupply) { + | ^ (Relevant source part starts here and spans across multiple lines). - let (stdout, _) = cmd.output_lossy(); - assert!(stdout.contains("Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3")); +Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 +Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3 +[TX_HASH] + +"#]]); }); diff --git a/crates/forge/tests/cli/debug.rs b/crates/forge/tests/cli/debug.rs index 3e3d08c7e..8ee1eb3f2 100644 --- a/crates/forge/tests/cli/debug.rs +++ b/crates/forge/tests/cli/debug.rs @@ -7,8 +7,14 @@ forgetest_async!( #[ignore = "ran manually"] manual_debug_setup, |prj, cmd| { - cmd.args(["init", "--force"]).arg(prj.root()).assert_non_empty_stdout(); - cmd.forge_fuse(); + cmd.args(["init", "--force"]).arg(prj.root()).assert_success().stdout_eq(str![[r#" +Target directory is not empty, but `--force` was specified +Initializing [..]... +Installing forge-std in [..] (url: Some("https://github.com/foundry-rs/forge-std"), tag: None) + Installed forge-std [..] + Initialized forge project + +"#]]); prj.add_source("Counter2.sol", r#" contract A { @@ -74,8 +80,7 @@ contract Script0 is Script, Test { ) .unwrap(); - cmd.args(["build"]).assert_success(); - cmd.forge_fuse(); + cmd.forge_fuse().args(["build"]).assert_success(); cmd.args([ "script", diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 2b92559f2..6ef99d9d1 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -54,7 +54,7 @@ contract Demo { .unwrap(); cmd.arg("script").arg(script).assert_success().stdout_eq(str![[r#" -Compiling 1 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! Script ran successfully. @@ -113,7 +113,7 @@ contract Demo { cmd.arg("script").arg(script).arg("--sig").arg("myFunction()").assert_success().stdout_eq( str![[r#" -Compiling 1 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! Script ran successfully. @@ -145,10 +145,11 @@ forgetest_async!(assert_exit_code_error_on_failure_script, |prj, cmd| { cmd.arg("script").arg(script); // run command and assert error exit code - cmd.assert_err(); + cmd.assert_failure().stderr_eq(str![[r#" +Error: +script failed: revert: failed - let output = cmd.stderr_lossy(); - assert!(output.contains("script failed: revert: failed")); +"#]]); }); // Tests that execution throws upon encountering a revert in the script with --json option. @@ -161,10 +162,11 @@ forgetest_async!(assert_exit_code_error_on_failure_script_with_json, |prj, cmd| cmd.arg("script").arg(script).arg("--json"); // run command and assert error exit code - cmd.assert_err(); + cmd.assert_failure().stderr_eq(str![[r#" +Error: +script failed: revert: failed - let output = cmd.stderr_lossy(); - assert!(output.contains("script failed: revert: failed")); +"#]]); }); // Tests that the manually specified gas limit is used when using the --unlocked option @@ -182,7 +184,7 @@ contract GasWaster { } } contract DeployScript is Script { - function run() external returns (uint256 result, uint8) { + function run() external { vm.startBroadcast(); GasWaster gasWaster = new GasWaster(); gasWaster.wasteGas{gas: 500000}(200000); @@ -213,11 +215,66 @@ contract DeployScript is Script { "--slow", "--broadcast", "--unlocked", - ]); + ]) + .assert_success() + .stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful with warnings: +Warning (2018): Function state mutability can be restricted to view + [FILE]:7:5: + | +7 | function wasteGas(uint256 minGas) public { + | ^ (Relevant source part starts here and spans across multiple lines). + +Traces: + [81040] DeployScript::run() + ├─ [0] VM::startBroadcast() + │ └─ ← [Return] + ├─ [45299] → new GasWaster@[..] + │ └─ ← [Return] 226 bytes of code + ├─ [226] GasWaster::wasteGas(200000 [2e5]) + │ └─ ← [Stop] + └─ ← [Stop] + + +Script ran successfully. + +## Setting up 1 EVM. +========================== +Simulated On-chain Traces: + +Gas limit was set in script to 500000 + [45299] → new GasWaster@[..] + └─ ← [Return] 226 bytes of code + + [226] GasWaster::wasteGas(200000 [2e5]) + └─ ← [Stop] + + +========================== + +Chain 1 + +[ESTIMATED_GAS_PRICE] + +[ESTIMATED_TOTAL_GAS_USED] + +[ESTIMATED_AMOUNT_REQUIRED] - let output = cmd.stdout_lossy(); - assert!(output.contains("ONCHAIN EXECUTION COMPLETE & SUCCESSFUL")); - assert!(output.contains("Gas limit was set in script to 500000")); +========================== + + +========================== + +ONCHAIN EXECUTION COMPLETE & SUCCESSFUL. + +[SAVED_TRANSACTIONS] + +[SAVED_SENSITIVE_VALUES] + + +"#]]); }); // Tests that the manually specified gas limit is used. @@ -235,7 +292,7 @@ contract GasWaster { } } contract DeployScript is Script { - function run() external returns (uint256 result, uint8) { + function run() external { vm.startBroadcast(); GasWaster gasWaster = new GasWaster(); gasWaster.wasteGas{gas: 500000}(200000); @@ -266,11 +323,66 @@ contract DeployScript is Script { "--broadcast", "--private-key", &private_key, - ]); + ]) + .assert_success() + .stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful with warnings: +Warning (2018): Function state mutability can be restricted to view + [FILE]:7:5: + | +7 | function wasteGas(uint256 minGas) public { + | ^ (Relevant source part starts here and spans across multiple lines). + +Traces: + [81040] DeployScript::run() + ├─ [0] VM::startBroadcast() + │ └─ ← [Return] + ├─ [45299] → new GasWaster@[..] + │ └─ ← [Return] 226 bytes of code + ├─ [226] GasWaster::wasteGas(200000 [2e5]) + │ └─ ← [Stop] + └─ ← [Stop] + + +Script ran successfully. + +## Setting up 1 EVM. +========================== +Simulated On-chain Traces: + +Gas limit was set in script to 500000 + [45299] → new GasWaster@[..] + └─ ← [Return] 226 bytes of code + + [226] GasWaster::wasteGas(200000 [2e5]) + └─ ← [Stop] - let output = cmd.stdout_lossy(); - assert!(output.contains("ONCHAIN EXECUTION COMPLETE & SUCCESSFUL")); - assert!(output.contains("Gas limit was set in script to 500000")); + +========================== + +Chain 1 + +[ESTIMATED_GAS_PRICE] + +[ESTIMATED_TOTAL_GAS_USED] + +[ESTIMATED_AMOUNT_REQUIRED] + +========================== + + +========================== + +ONCHAIN EXECUTION COMPLETE & SUCCESSFUL. + +[SAVED_TRANSACTIONS] + +[SAVED_SENSITIVE_VALUES] + + +"#]]); }); // Tests that the run command can run functions with arguments @@ -300,7 +412,7 @@ contract Demo { .arg("2") .assert_success() .stdout_eq(str![[r#" -Compiling 1 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! Script ran successfully. @@ -331,7 +443,7 @@ contract Demo { .unwrap(); cmd.arg("script").arg(script).assert_success().stdout_eq(str![[r#" -Compiling 1 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! Script ran successfully. @@ -358,20 +470,24 @@ import "forge-std/Script.sol"; contract HashChecker { bytes32 public lastHash; + function update() public { bytes32 newHash = blockhash(block.number - 1); require(newHash != lastHash, "Hash didn't change"); lastHash = newHash; } - function checkLastHash() public { + function checkLastHash() public view { require(lastHash != bytes32(0), "Hash shouldn't be zero"); } } + contract DeployScript is Script { - function run() external returns (uint256 result, uint8) { + HashChecker public hashChecker; + + function run() external { vm.startBroadcast(); - HashChecker hashChecker = new HashChecker(); + hashChecker = new HashChecker(); } }"#, ) @@ -399,12 +515,36 @@ contract DeployScript is Script { "--skip-simulation", "--private-key", &private_key, - ]); + ]) + .assert_success() + .stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! +Traces: + [116040] DeployScript::run() + ├─ [0] VM::startBroadcast() + │ └─ ← [Return] + ├─ [75723] → new HashChecker@[..] + │ └─ ← [Return] 378 bytes of code + └─ ← [Stop] + + +Script ran successfully. + +SKIPPING ON CHAIN SIMULATION. + - let output = cmd.stdout_lossy(); +========================== - assert!(output.contains("SKIPPING ON CHAIN SIMULATION")); - assert!(output.contains("ONCHAIN EXECUTION COMPLETE & SUCCESSFUL")); +ONCHAIN EXECUTION COMPLETE & SUCCESSFUL. + +[SAVED_TRANSACTIONS] + +[SAVED_SENSITIVE_VALUES] + + +"#]]); let run_log = std::fs::read_to_string("broadcast/DeployScript.sol/1/run-latest.json").unwrap(); let run_object: Value = serde_json::from_str(&run_log).unwrap(); @@ -420,9 +560,11 @@ import "forge-std/Script.sol"; import { HashChecker } from "./DeployScript.sol"; contract RunScript is Script { - function run() external returns (uint256 result, uint8) { + HashChecker public hashChecker; + + function run() external { vm.startBroadcast(); - HashChecker hashChecker = HashChecker(CONTRACT_ADDRESS); + hashChecker = HashChecker(CONTRACT_ADDRESS); uint numUpdates = 8; vm.roll(block.number - numUpdates); for(uint i = 0; i < numUpdates; i++) { @@ -437,28 +579,100 @@ contract RunScript is Script { let run_script = prj.add_source("RunScript", &run_code).unwrap(); let run_contract = run_script.display().to_string() + ":RunScript"; - cmd.forge_fuse(); - cmd.set_current_dir(prj.root()); - cmd.args([ - "script", - &run_contract, - "--root", - prj.root().to_str().unwrap(), - "--fork-url", - &handle.http_endpoint(), - "-vvvvv", - "--broadcast", - "--slow", - "--skip-simulation", - "--gas-estimate-multiplier", - "200", - "--private-key", - &private_key, - ]); + cmd.forge_fuse() + .args([ + "script", + &run_contract, + "--root", + prj.root().to_str().unwrap(), + "--fork-url", + &handle.http_endpoint(), + "-vvvvv", + "--broadcast", + "--slow", + "--skip-simulation", + "--gas-estimate-multiplier", + "200", + "--private-key", + &private_key, + ]) + .assert_success() + .stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! +Traces: + [51327] RunScript::run() + ├─ [0] VM::startBroadcast() + │ └─ ← [Return] + ├─ [0] VM::roll([..]) + │ └─ ← [Return] + ├─ [0] VM::roll([..]) + │ └─ ← [Return] + ├─ [22394] [..]::update() + │ └─ ← [Stop] + ├─ [239] [..]::checkLastHash() [staticcall] + │ └─ ← [Stop] + ├─ [0] VM::roll([..]) + │ └─ ← [Return] + ├─ [494] [..]::update() + │ └─ ← [Stop] + ├─ [239] [..]::checkLastHash() [staticcall] + │ └─ ← [Stop] + ├─ [0] VM::roll([..]) + │ └─ ← [Return] + ├─ [494] [..]::update() + │ └─ ← [Stop] + ├─ [239] [..]::checkLastHash() [staticcall] + │ └─ ← [Stop] + ├─ [0] VM::roll([..]) + │ └─ ← [Return] + ├─ [494] [..]::update() + │ └─ ← [Stop] + ├─ [239] [..]::checkLastHash() [staticcall] + │ └─ ← [Stop] + ├─ [0] VM::roll([..]) + │ └─ ← [Return] + ├─ [494] [..]::update() + │ └─ ← [Stop] + ├─ [239] [..]::checkLastHash() [staticcall] + │ └─ ← [Stop] + ├─ [0] VM::roll([..]) + │ └─ ← [Return] + ├─ [494] [..]::update() + │ └─ ← [Stop] + ├─ [239] [..]::checkLastHash() [staticcall] + │ └─ ← [Stop] + ├─ [0] VM::roll([..]) + │ └─ ← [Return] + ├─ [494] [..]::update() + │ └─ ← [Stop] + ├─ [239] [..]::checkLastHash() [staticcall] + │ └─ ← [Stop] + ├─ [0] VM::roll([..]) + │ └─ ← [Return] + ├─ [494] [..]::update() + │ └─ ← [Stop] + ├─ [239] [..]::checkLastHash() [staticcall] + │ └─ ← [Stop] + └─ ← [Stop] - let output = cmd.stdout_lossy(); - assert!(output.contains("SKIPPING ON CHAIN SIMULATION")); - assert!(output.contains("ONCHAIN EXECUTION COMPLETE & SUCCESSFUL")); + +Script ran successfully. + +SKIPPING ON CHAIN SIMULATION. + + +========================== + +ONCHAIN EXECUTION COMPLETE & SUCCESSFUL. + +[SAVED_TRANSACTIONS] + +[SAVED_SENSITIVE_VALUES] + + +"#]]); }); forgetest_async!(can_deploy_script_without_lib, |prj, cmd| { @@ -801,12 +1015,17 @@ struct Transaction { // test we output arguments forgetest_async!(can_execute_script_with_arguments, |prj, cmd| { - cmd.args(["init", "--force"]).arg(prj.root()); - cmd.assert_non_empty_stdout(); - cmd.forge_fuse(); + cmd.args(["init", "--force"]).arg(prj.root()).assert_success().stdout_eq(str![[r#" +Target directory is not empty, but `--force` was specified +Initializing [..]... +Installing forge-std in [..] (url: Some("https://github.com/foundry-rs/forge-std"), tag: None) + Installed forge-std [..] + Initialized forge project + +"#]]); let (_api, handle) = spawn(NodeConfig::test()).await; - let script = prj .add_script( + let script = prj.add_script( "Counter.s.sol", r#" import "forge-std/Script.sol"; @@ -822,6 +1041,9 @@ contract A { int c; bytes32 d; bool e; + bytes f; + Point g; + string h; constructor(address _a, uint _b, int _c, bytes32 _d, bool _e, bytes memory _f, Point memory _g, string memory _h) { a = _a; @@ -829,6 +1051,9 @@ contract A { c = _c; d = _d; e = _e; + f = _f; + g = _g; + h = _h; } } @@ -843,16 +1068,48 @@ contract Script0 is Script { ) .unwrap(); - cmd.arg("script").arg(script).args([ - "--tc", - "Script0", - "--sender", - "0x00a329c0648769A73afAc7F9381E08FB43dBEA72", - "--rpc-url", - handle.http_endpoint().as_str(), - ]); + cmd + .forge_fuse() + .arg("script") + .arg(script) + .args([ + "--tc", + "Script0", + "--sender", + "0x00a329c0648769A73afAc7F9381E08FB43dBEA72", + "--rpc-url", + handle.http_endpoint().as_str(), + ]) + .assert_success() + .stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! +... +Script ran successfully. + +## Setting up 1 EVM. + +========================== - assert!(cmd.stdout_lossy().contains("SIMULATION COMPLETE")); +Chain 31337 + +[ESTIMATED_GAS_PRICE] + +[ESTIMATED_TOTAL_GAS_USED] + +[ESTIMATED_AMOUNT_REQUIRED] + +========================== + +SIMULATION COMPLETE. To broadcast these transactions, add --broadcast and wallet configuration(s) to the previous command. See forge script --help for more. + +[SAVED_TRANSACTIONS] + +[SAVED_SENSITIVE_VALUES] + + +"#]]); let run_latest = foundry_common::fs::json_files(&prj.root().join("broadcast")) .find(|path| path.ends_with("run-latest.json")) @@ -880,9 +1137,14 @@ contract Script0 is Script { // test we output arguments forgetest_async!(can_execute_script_with_arguments_nested_deploy, |prj, cmd| { - cmd.args(["init", "--force"]).arg(prj.root()); - cmd.assert_non_empty_stdout(); - cmd.forge_fuse(); + cmd.args(["init", "--force"]).arg(prj.root()).assert_success().stdout_eq(str![[r#" +Target directory is not empty, but `--force` was specified +Initializing [..]... +Installing forge-std in [..] (url: Some("https://github.com/foundry-rs/forge-std"), tag: None) + Installed forge-std [..] + Initialized forge project + +"#]]); let (_api, handle) = spawn(NodeConfig::test()).await; let script = prj @@ -927,16 +1189,48 @@ contract Script0 is Script { ) .unwrap(); - cmd.arg("script").arg(script).args([ - "--tc", - "Script0", - "--sender", - "0x00a329c0648769A73afAc7F9381E08FB43dBEA72", - "--rpc-url", - handle.http_endpoint().as_str(), - ]); + cmd + .forge_fuse() + .arg("script") + .arg(script) + .args([ + "--tc", + "Script0", + "--sender", + "0x00a329c0648769A73afAc7F9381E08FB43dBEA72", + "--rpc-url", + handle.http_endpoint().as_str(), + ]) + .assert_success() + .stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! +... +Script ran successfully. + +## Setting up 1 EVM. + +========================== + +Chain 31337 + +[ESTIMATED_GAS_PRICE] + +[ESTIMATED_TOTAL_GAS_USED] - assert!(cmd.stdout_lossy().contains("SIMULATION COMPLETE")); +[ESTIMATED_AMOUNT_REQUIRED] + +========================== + +SIMULATION COMPLETE. To broadcast these transactions, add --broadcast and wallet configuration(s) to the previous command. See forge script --help for more. + +[SAVED_TRANSACTIONS] + +[SAVED_SENSITIVE_VALUES] + + +"#]]); let run_latest = foundry_common::fs::json_files(&prj.root().join("broadcast")) .find(|file| file.ends_with("run-latest.json")) @@ -981,7 +1275,7 @@ contract Demo { .args(["--skip", "tests", "--skip", TEMPLATE_CONTRACT]) .assert_success() .stdout_eq(str![[r#" -Compiling 1 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! Script ran successfully. @@ -1010,9 +1304,14 @@ forgetest_async!(does_script_override_correctly, |prj, cmd| { }); forgetest_async!(assert_tx_origin_is_not_overritten, |prj, cmd| { - cmd.args(["init", "--force"]).arg(prj.root()); - cmd.assert_non_empty_stdout(); - cmd.forge_fuse(); + cmd.args(["init", "--force"]).arg(prj.root()).assert_success().stdout_eq(str![[r#" +Target directory is not empty, but `--force` was specified +Initializing [..]... +Installing forge-std in [..] (url: Some("https://github.com/foundry-rs/forge-std"), tag: None) + Installed forge-std [..] + Initialized forge project + +"#]]); let script = prj .add_script( @@ -1068,14 +1367,32 @@ contract ContractC { ) .unwrap(); - cmd.arg("script").arg(script).args(["--tc", "ScriptTxOrigin"]); - assert!(cmd.stdout_lossy().contains("Script ran successfully.")); + cmd.forge_fuse() + .arg("script") + .arg(script) + .args(["--tc", "ScriptTxOrigin"]) + .assert_success() + .stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! +Script ran successfully. +[GAS] + +If you wish to simulate on-chain transactions pass a RPC URL. + +"#]]); }); forgetest_async!(assert_can_create_multiple_contracts_with_correct_nonce, |prj, cmd| { - cmd.args(["init", "--force"]).arg(prj.root()); - cmd.assert_non_empty_stdout(); - cmd.forge_fuse(); + cmd.args(["init", "--force"]).arg(prj.root()).assert_success().stdout_eq(str![[r#" +Target directory is not empty, but `--force` was specified +Initializing [..]... +Installing forge-std in [..] (url: Some("https://github.com/foundry-rs/forge-std"), tag: None) + Installed forge-std [..] + Initialized forge project + +"#]]); let script = prj .add_script( @@ -1088,18 +1405,20 @@ contract Contract { console.log(tx.origin); } } + contract SubContract { constructor() { console.log(tx.origin); } } + contract BadContract { constructor() { - // new SubContract(); + new SubContract(); console.log(tx.origin); } } -contract NestedCreateFail is Script { +contract NestedCreate is Script { function run() public { address sender = address(uint160(uint(keccak256("woops")))); @@ -1114,8 +1433,26 @@ contract NestedCreateFail is Script { ) .unwrap(); - cmd.arg("script").arg(script).args(["--tc", "NestedCreateFail"]); - assert!(cmd.stdout_lossy().contains("Script ran successfully.")); + cmd.forge_fuse() + .arg("script") + .arg(script) + .args(["--tc", "NestedCreate"]) + .assert_success() + .stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! +Script ran successfully. +[GAS] + +== Logs == + 0x159E2f2F1C094625A2c6c8bF59526d91454c2F3c + 0x159E2f2F1C094625A2c6c8bF59526d91454c2F3c + 0x159E2f2F1C094625A2c6c8bF59526d91454c2F3c + +If you wish to simulate on-chain transactions pass a RPC URL. + +"#]]); }); forgetest_async!(assert_can_detect_target_contract_with_interfaces, |prj, cmd| { @@ -1132,8 +1469,14 @@ interface Interface {} ) .unwrap(); - cmd.arg("script").arg(script); - assert!(cmd.stdout_lossy().contains("Script ran successfully.")); + cmd.arg("script").arg(script).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! +Script ran successfully. +[GAS] + +"#]]); }); forgetest_async!(assert_can_detect_unlinked_target_with_libraries, |prj, cmd| { @@ -1154,8 +1497,16 @@ contract Script { ) .unwrap(); - cmd.arg("script").arg(script); - assert!(cmd.stdout_lossy().contains("Script ran successfully.")); + cmd.arg("script").arg(script).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! +Script ran successfully. +[GAS] + +If you wish to simulate on-chain transactions pass a RPC URL. + +"#]]); }); forgetest_async!(assert_can_resume_with_additional_contracts, |prj, cmd| { @@ -1239,7 +1590,7 @@ forgetest_async!(can_sign_with_script_wallet_multiple, |prj, cmd| { forgetest_async!(fails_with_function_name_and_overloads, |prj, cmd| { let script = prj .add_script( - "Sctipt.s.sol", + "Script.s.sol", r#" contract Script { function run() external {} @@ -1251,13 +1602,22 @@ contract Script { .unwrap(); cmd.arg("script").args([&script.to_string_lossy(), "--sig", "run"]); - assert!(cmd.stderr_lossy().contains("Multiple functions with the same name")); + cmd.assert_failure().stderr_eq(str![[r#" +Error: +Multiple functions with the same name `run` found in the ABI + +"#]]); }); forgetest_async!(can_decode_custom_errors, |prj, cmd| { - cmd.args(["init", "--force"]).arg(prj.root()); - cmd.assert_non_empty_stdout(); - cmd.forge_fuse(); + cmd.args(["init", "--force"]).arg(prj.root()).assert_success().stdout_eq(str![[r#" +Target directory is not empty, but `--force` was specified +Initializing [..]... +Installing forge-std in [..] (url: Some("https://github.com/foundry-rs/forge-std"), tag: None) + Installed forge-std [..] + Initialized forge project + +"#]]); let script = prj .add_script( @@ -1284,8 +1644,12 @@ contract CustomErrorScript is Script { ) .unwrap(); - cmd.arg("script").arg(script).args(["--tc", "CustomErrorScript"]); - assert!(cmd.stderr_lossy().contains("script failed: CustomError()")); + cmd.forge_fuse().arg("script").arg(script).args(["--tc", "CustomErrorScript"]); + cmd.assert_failure().stderr_eq(str![[r#" +Error: +script failed: CustomError() + +"#]]); }); // https://github.com/foundry-rs/foundry/issues/7620 @@ -1297,9 +1661,9 @@ forgetest_async!(can_run_zero_base_fee, |prj, cmd| { import "forge-std/Script.sol"; contract SimpleScript is Script { - function run() external { + function run() external returns (bool success) { vm.startBroadcast(); - address(0).call(""); + (success, ) = address(0).call(""); } } "#, @@ -1325,25 +1689,90 @@ contract SimpleScript is Script { "2000000", "--priority-gas-price", "100000", - ]); + ]) + .assert_success() + .stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! +... +Script ran successfully. + +== Return == +success: bool true + +## Setting up 1 EVM. + +========================== + +Chain 31337 - let output = cmd.stdout_lossy(); - assert!(output.contains("ONCHAIN EXECUTION COMPLETE & SUCCESSFUL")); +[ESTIMATED_GAS_PRICE] + +[ESTIMATED_TOTAL_GAS_USED] + +[ESTIMATED_AMOUNT_REQUIRED] + +========================== + + +========================== + +ONCHAIN EXECUTION COMPLETE & SUCCESSFUL. + +[SAVED_TRANSACTIONS] + +[SAVED_SENSITIVE_VALUES] + + +"#]]); // Ensure that we can correctly estimate gas when base fee is zero but priority fee is not. - cmd.forge_fuse().args([ - "script", - "SimpleScript", - "--fork-url", - &handle.http_endpoint(), - "--sender", - format!("{dev:?}").as_str(), - "--broadcast", - "--unlocked", - ]); + cmd.forge_fuse() + .args([ + "script", + "SimpleScript", + "--fork-url", + &handle.http_endpoint(), + "--sender", + format!("{dev:?}").as_str(), + "--broadcast", + "--unlocked", + ]) + .assert_success() + .stdout_eq(str![[r#" +No files changed, compilation skipped +... +Script ran successfully. + +== Return == +success: bool true + +## Setting up 1 EVM. + +========================== + +Chain 31337 + +[ESTIMATED_GAS_PRICE] + +[ESTIMATED_TOTAL_GAS_USED] + +[ESTIMATED_AMOUNT_REQUIRED] + +========================== + + +========================== - let output = cmd.stdout_lossy(); - assert!(output.contains("ONCHAIN EXECUTION COMPLETE & SUCCESSFUL")); +ONCHAIN EXECUTION COMPLETE & SUCCESSFUL. + +[SAVED_TRANSACTIONS] + +[SAVED_SENSITIVE_VALUES] + + +"#]]); }); // https://github.com/foundry-rs/foundry/pull/7742 @@ -1355,9 +1784,9 @@ forgetest_async!(unlocked_no_sender, |prj, cmd| { import "forge-std/Script.sol"; contract SimpleScript is Script { - function run() external { + function run() external returns (bool success) { vm.startBroadcast(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); - address(0).call(""); + (success, ) = address(0).call(""); } } "#, @@ -1373,10 +1802,43 @@ contract SimpleScript is Script { &handle.http_endpoint(), "--broadcast", "--unlocked", - ]); + ]) + .assert_success() + .stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! +... +Script ran successfully. + +== Return == +success: bool true + +## Setting up 1 EVM. + +========================== + +Chain 31337 + +[ESTIMATED_GAS_PRICE] - let output = cmd.stdout_lossy(); - assert!(output.contains("ONCHAIN EXECUTION COMPLETE & SUCCESSFUL")); +[ESTIMATED_TOTAL_GAS_USED] + +[ESTIMATED_AMOUNT_REQUIRED] + +========================== + + +========================== + +ONCHAIN EXECUTION COMPLETE & SUCCESSFUL. + +[SAVED_TRANSACTIONS] + +[SAVED_SENSITIVE_VALUES] + + +"#]]); }); // https://github.com/foundry-rs/foundry/issues/7833 @@ -1411,8 +1873,11 @@ contract SimpleScript is Script { "--unlocked", ]); - let output = cmd.stderr_lossy(); - assert!(output.contains("missing CREATE2 deployer")); + cmd.assert_failure().stderr_eq(str![[r#" +Error: +script failed: missing CREATE2 deployer + +"#]]); }); forgetest_async!(can_switch_forks_in_setup, |prj, cmd| { @@ -1450,9 +1915,21 @@ contract SimpleScript is Script { &url, "--sender", "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", - ]); + ]) + .assert_success() + .stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful with warnings: +Warning (2018): Function state mutability can be restricted to view + [FILE]:13:5: + | +13 | function run() external { + | ^ (Relevant source part starts here and spans across multiple lines). + +Script ran successfully. - cmd.stdout_lossy(); +"#]]); }); // Asserts that running the same script twice only deploys library once. diff --git a/crates/forge/tests/cli/soldeer.rs b/crates/forge/tests/cli/soldeer.rs index bacf47230..21666edbb 100644 --- a/crates/forge/tests/cli/soldeer.rs +++ b/crates/forge/tests/cli/soldeer.rs @@ -7,14 +7,17 @@ use std::{ use foundry_test_utils::forgesoldeer; use std::io::Write; + forgesoldeer!(install_dependency, |prj, cmd| { let command = "install"; let dependency = "forge-std~1.8.1"; let foundry_file = prj.root().join("foundry.toml"); - cmd.arg("soldeer").args([command, dependency]); - cmd.execute(); + cmd.arg("soldeer").args([command, dependency]).assert_success().stdout_eq(str![[r#" +🦌 Running [..]oldeer install 🦌 +... +"#]]); // Making sure the path was created to the dependency and that foundry.toml exists // meaning that the dependencies were installed correctly @@ -53,8 +56,10 @@ forgesoldeer!(install_dependency_git, |prj, cmd| { let foundry_file = prj.root().join("foundry.toml"); - cmd.arg("soldeer").args([command, dependency, git]); - cmd.execute(); + cmd.arg("soldeer").args([command, dependency, git]).assert_success().stdout_eq(str![[r#" +🦌 Running [..]oldeer install 🦌 +... +"#]]); // Making sure the path was created to the dependency and that README.md exists // meaning that the dependencies were installed correctly @@ -93,8 +98,13 @@ forgesoldeer!(install_dependency_git_commit, |prj, cmd| { let foundry_file = prj.root().join("foundry.toml"); - cmd.arg("soldeer").args([command, dependency, git, rev_flag, commit]); - cmd.execute(); + cmd.arg("soldeer") + .args([command, dependency, git, rev_flag, commit]) + .assert_success() + .stdout_eq(str![[r#" +🦌 Running [..]oldeer install 🦌 +... +"#]]); // Making sure the path was created to the dependency and that README.md exists // meaning that the dependencies were installed correctly @@ -147,8 +157,11 @@ mario-custom-branch = { version = "1.0", git = "https://gitlab.com/mario4582928/ eprintln!("Couldn't write to file: {e}"); } - cmd.arg("soldeer").arg(command); - cmd.execute(); + cmd.arg("soldeer").arg(command).assert_success().stdout_eq(str![[r#" +🦌 Running [..]oldeer update 🦌 +... + +"#]]); // Making sure the path was created to the dependency and that foundry.toml exists // meaning that the dependencies were installed correctly @@ -216,8 +229,11 @@ forge-std = "1.8.1" eprintln!("Couldn't write to file: {e}"); } - cmd.arg("soldeer").arg(command); - cmd.execute(); + cmd.arg("soldeer").arg(command).assert_success().stdout_eq(str![[r#" +🦌 Running [..]oldeer update 🦌 +... + +"#]]); // Making sure the path was created to the dependency and that foundry.toml exists // meaning that the dependencies were installed correctly @@ -252,12 +268,20 @@ forge-std = "1.8.1" forgesoldeer!(login, |prj, cmd| { let command = "login"; - cmd.arg("soldeer").arg(command); - let output = cmd.unchecked_output(); - - // On login, we can only check if the prompt is displayed in the stdout - let stdout = String::from_utf8(output.stdout).expect("Could not parse the output"); - assert!(stdout.contains("Please enter your email")); + cmd.arg("soldeer") + .arg(command) + .assert_failure() + .stderr_eq(str![[r#" +Error: +Failed to run [..] + +"#]]) + .stdout_eq(str![[r#" +🦌 Running [..]oldeer login 🦌 +... +ℹ️ If you do not have an account, please go to soldeer.xyz to create one. +📧 Please enter your email: +"#]]); }); forgesoldeer!(install_dependency_with_remappings_config, |prj, cmd| { diff --git a/crates/forge/tests/cli/svm.rs b/crates/forge/tests/cli/svm.rs index 8403053d8..ab8db41f8 100644 --- a/crates/forge/tests/cli/svm.rs +++ b/crates/forge/tests/cli/svm.rs @@ -57,5 +57,18 @@ contract CounterTest is Test {{ "# ); prj.add_test("Counter", &src).unwrap(); - cmd.arg("test").stdout_lossy().contains("[PASS]"); + cmd.arg("test").assert_success().stdout_eq(str![[r#" +... +Ran 1 test for test/Counter.sol:CounterTest +[PASS] testAssert() ([GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] +... +Ran 2 tests for test/Counter.t.sol:CounterTest +[PASS] testFuzz_SetNumber(uint256) (runs: 256, [AVG_GAS]) +[PASS] test_Increment() ([GAS]) +Suite result: ok. 2 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 2 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) + +"#]]); }); diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 391de7a28..23ffa890d 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -4,7 +4,7 @@ use alloy_primitives::U256; use foundry_config::{Config, FuzzConfig}; use foundry_test_utils::{ rpc, str, - util::{OTHER_SOLC_VERSION, SOLC_VERSION}, + util::{OutputExt, OTHER_SOLC_VERSION, SOLC_VERSION}, }; use similar_asserts::assert_eq; use std::{path::PathBuf, str::FromStr}; @@ -128,7 +128,7 @@ forgetest!(can_fuzz_array_params, |prj, cmd| { r#" import "./test.sol"; contract ATest is DSTest { - function testArray(uint64[2] calldata values) external { + function testArray(uint64[2] calldata) external { assertTrue(true); } } @@ -136,8 +136,18 @@ contract ATest is DSTest { ) .unwrap(); - cmd.arg("test"); - cmd.stdout_lossy().contains("[PASS]"); + cmd.arg("test").assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for src/ATest.t.sol:ATest +[PASS] testArray(uint64[2]) (runs: 256, [AVG_GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"#]]); }); // tests that `bytecode_hash` will be sanitized @@ -151,7 +161,7 @@ forgetest!(can_test_pre_bytecode_hash, |prj, cmd| { pragma solidity 0.5.17; import "./test.sol"; contract ATest is DSTest { - function testArray(uint64[2] calldata values) external { + function testArray(uint64[2] calldata) external { assertTrue(true); } } @@ -159,8 +169,18 @@ contract ATest is DSTest { ) .unwrap(); - cmd.arg("test"); - cmd.stdout_lossy().contains("[PASS]"); + cmd.arg("test").assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for src/ATest.t.sol:ATest +[PASS] testArray(uint64[2]) (runs: 256, [AVG_GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"#]]); }); // tests that using the --match-path option only runs files matching the path @@ -194,7 +214,7 @@ contract FailTest is DSTest { .unwrap(); cmd.args(["test", "--match-path", "*src/ATest.t.sol"]).assert_success().stdout_eq(str![[r#" -Compiling 3 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! @@ -241,7 +261,7 @@ contract FailTest is DSTest { let test_path = test_path.to_string_lossy(); cmd.args(["test", "--match-path", test_path.as_ref()]).assert_success().stdout_eq(str![[r#" -Compiling 3 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! @@ -278,7 +298,7 @@ contract MyTest is DSTest { .unwrap(); cmd.arg("test").assert_success().stdout_eq(str![[r#" -Compiling 2 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! @@ -293,9 +313,22 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) // checks that forge test repeatedly produces the same output #[cfg(not(feature = "isolate-by-default"))] -forgetest_init!(can_test_repeatedly, |_prj, cmd| { - cmd.arg("test"); - cmd.assert_non_empty_stdout(); +forgetest_init!(can_test_repeatedly, |prj, cmd| { + prj.clear(); + + cmd.arg("test").assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 2 tests for test/Counter.t.sol:CounterTest +[PASS] testFuzz_SetNumber(uint256) (runs: 256, [AVG_GAS]) +[PASS] test_Increment() ([GAS]) +Suite result: ok. 2 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 2 tests passed, 0 failed, 0 skipped (2 total tests) + +"#]]); for _ in 0..5 { cmd.assert_success().stdout_eq(str![[r#" @@ -339,7 +372,7 @@ contract ContractTest is DSTest { prj.write_config(config); cmd.arg("test").assert_success().stdout_eq(str![[r#" -Compiling 2 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! @@ -356,7 +389,7 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) prj.write_config(config); cmd.forge_fuse().arg("test").assert_success().stdout_eq(str![[r#" -Compiling 2 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! @@ -417,7 +450,7 @@ contract ContractTest is Test { .unwrap(); cmd.arg("test").assert_success().stdout_eq(str![[r#" -Compiling 2 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! @@ -448,7 +481,7 @@ forgetest_init!(exit_code_error_on_fail_fast, |prj, cmd| { cmd.args(["test", "--fail-fast"]); // run command and assert error exit code - cmd.assert_err(); + cmd.assert_empty_stderr(); }); forgetest_init!(exit_code_error_on_fail_fast_with_json, |prj, cmd| { @@ -459,7 +492,7 @@ forgetest_init!(exit_code_error_on_fail_fast_with_json, |prj, cmd| { cmd.args(["test", "--fail-fast", "--json"]); // run command and assert error exit code - cmd.assert_err(); + cmd.assert_empty_stderr(); }); // https://github.com/foundry-rs/foundry/pull/6531 @@ -489,7 +522,7 @@ contract USDTCallingTest is Test { .unwrap(); cmd.args(["test", "-vvvv"]).assert_success().stdout_eq(str![[r#" -Compiling 1 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! @@ -535,7 +568,7 @@ contract CustomTypesTest is Test { .unwrap(); cmd.args(["test", "-vvvv"]).assert_failure().stdout_eq(str![[r#" -Compiling 1 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! @@ -738,7 +771,7 @@ contract CounterTest is Test { // make sure there are only 61 runs (with proptest shrinking same test results in 298 runs) cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" -Compiling 23 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! @@ -789,7 +822,7 @@ contract CounterTest is Test { // make sure invariant test exit early with 0 runs cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" -Compiling 23 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! @@ -836,8 +869,29 @@ contract ReplayFailuresTest is Test { ) .unwrap(); - cmd.args(["test"]); - cmd.assert_err(); + cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 4 tests for test/ReplayFailures.t.sol:ReplayFailuresTest +[PASS] testA() ([GAS]) +[FAIL. Reason: revert: testB failed] testB() ([GAS]) +[PASS] testC() ([GAS]) +[FAIL. Reason: revert: testD failed] testD() ([GAS]) +Suite result: FAILED. 2 passed; 2 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 2 tests passed, 2 failed, 0 skipped (4 total tests) + +Failing tests: +Encountered 2 failing tests in test/ReplayFailures.t.sol:ReplayFailuresTest +[FAIL. Reason: revert: testB failed] testB() ([GAS]) +[FAIL. Reason: revert: testD failed] testD() ([GAS]) + +Encountered a total of 2 failing tests, 2 tests succeeded + +"#]]); + // Test failure filter should be persisted. assert!(prj.root().join("cache/test-failures").exists()); @@ -893,22 +947,52 @@ contract PrecompileLabelsTest is Test { ) .unwrap(); - let output = cmd.args(["test", "-vvvv"]).stdout_lossy(); - assert!(output.contains("VM: [0x7109709ECfa91a80626fF3989D68f67F5b1DD12D]")); - assert!(output.contains("console: [0x000000000000000000636F6e736F6c652e6c6f67]")); - assert!(output.contains("Create2Deployer: [0x4e59b44847b379578588920cA78FbF26c0B4956C]")); - assert!(output.contains("DefaultSender: [0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38]")); - assert!(output.contains("DefaultTestContract: [0xb4c79daB8f259C7Aee6E5b2Aa729821864227e84]")); - assert!(output.contains("ECRecover: [0x0000000000000000000000000000000000000001]")); - assert!(output.contains("SHA-256: [0x0000000000000000000000000000000000000002]")); - assert!(output.contains("RIPEMD-160: [0x0000000000000000000000000000000000000003]")); - assert!(output.contains("Identity: [0x0000000000000000000000000000000000000004]")); - assert!(output.contains("ModExp: [0x0000000000000000000000000000000000000005]")); - assert!(output.contains("ECAdd: [0x0000000000000000000000000000000000000006]")); - assert!(output.contains("ECMul: [0x0000000000000000000000000000000000000007]")); - assert!(output.contains("ECPairing: [0x0000000000000000000000000000000000000008]")); - assert!(output.contains("Blake2F: [0x0000000000000000000000000000000000000009]")); - assert!(output.contains("PointEvaluation: [0x000000000000000000000000000000000000000A]")); + cmd.args(["test", "-vvvv"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/Contract.t.sol:PrecompileLabelsTest +[PASS] testPrecompileLabels() ([GAS]) +Traces: + [9474] PrecompileLabelsTest::testPrecompileLabels() + ├─ [0] VM::deal(VM: [0x7109709ECfa91a80626fF3989D68f67F5b1DD12D], 1000000000000000000 [1e18]) + │ └─ ← [Return] + ├─ [0] VM::deal(console: [0x000000000000000000636F6e736F6c652e6c6f67], 1000000000000000000 [1e18]) + │ └─ ← [Return] + ├─ [0] VM::deal(Create2Deployer: [0x4e59b44847b379578588920cA78FbF26c0B4956C], 1000000000000000000 [1e18]) + │ └─ ← [Return] + ├─ [0] VM::deal(DefaultSender: [0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38], 1000000000000000000 [1e18]) + │ └─ ← [Return] + ├─ [0] VM::deal(DefaultTestContract: [0xb4c79daB8f259C7Aee6E5b2Aa729821864227e84], 1000000000000000000 [1e18]) + │ └─ ← [Return] + ├─ [0] VM::deal(ECRecover: [0x0000000000000000000000000000000000000001], 1000000000000000000 [1e18]) + │ └─ ← [Return] + ├─ [0] VM::deal(SHA-256: [0x0000000000000000000000000000000000000002], 1000000000000000000 [1e18]) + │ └─ ← [Return] + ├─ [0] VM::deal(RIPEMD-160: [0x0000000000000000000000000000000000000003], 1000000000000000000 [1e18]) + │ └─ ← [Return] + ├─ [0] VM::deal(Identity: [0x0000000000000000000000000000000000000004], 1000000000000000000 [1e18]) + │ └─ ← [Return] + ├─ [0] VM::deal(ModExp: [0x0000000000000000000000000000000000000005], 1000000000000000000 [1e18]) + │ └─ ← [Return] + ├─ [0] VM::deal(ECAdd: [0x0000000000000000000000000000000000000006], 1000000000000000000 [1e18]) + │ └─ ← [Return] + ├─ [0] VM::deal(ECMul: [0x0000000000000000000000000000000000000007], 1000000000000000000 [1e18]) + │ └─ ← [Return] + ├─ [0] VM::deal(ECPairing: [0x0000000000000000000000000000000000000008], 1000000000000000000 [1e18]) + │ └─ ← [Return] + ├─ [0] VM::deal(Blake2F: [0x0000000000000000000000000000000000000009], 1000000000000000000 [1e18]) + │ └─ ← [Return] + ├─ [0] VM::deal(PointEvaluation: [0x000000000000000000000000000000000000000A], 1000000000000000000 [1e18]) + │ └─ ← [Return] + └─ ← [Stop] + +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"#]]); }); // tests that `forge test` with config `show_logs: true` for fuzz tests will @@ -937,9 +1021,23 @@ forgetest_init!(should_show_logs_when_fuzz_test, |prj, cmd| { "#, ) .unwrap(); - cmd.args(["test", "-vv"]); - let stdout = cmd.stdout_lossy(); - assert!(stdout.contains("inside fuzz test, x is:"), "\n{stdout}"); + cmd.args(["test", "-vv"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/ContractFuzz.t.sol:ContractFuzz +[PASS] testFuzzConsoleLog(uint256) (runs: 3, [AVG_GAS]) +Logs: + inside fuzz test, x is: [..] + inside fuzz test, x is: [..] + inside fuzz test, x is: [..] + +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"#]]); }); // tests that `forge test` with inline config `show_logs = true` for fuzz tests will @@ -968,9 +1066,23 @@ forgetest_init!(should_show_logs_when_fuzz_test_inline_config, |prj, cmd| { "#, ) .unwrap(); - cmd.args(["test", "-vv"]); - let stdout = cmd.stdout_lossy(); - assert!(stdout.contains("inside fuzz test, x is:"), "\n{stdout}"); + cmd.args(["test", "-vv"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/ContractFuzz.t.sol:ContractFuzz +[PASS] testFuzzConsoleLog(uint256) (runs: 3, [AVG_GAS]) +Logs: + inside fuzz test, x is: [..] + inside fuzz test, x is: [..] + inside fuzz test, x is: [..] + +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"#]]); }); // tests that `forge test` with config `show_logs: false` for fuzz tests will not display @@ -1000,9 +1112,18 @@ forgetest_init!(should_not_show_logs_when_fuzz_test, |prj, cmd| { "#, ) .unwrap(); - cmd.args(["test", "-vv"]); - let stdout = cmd.stdout_lossy(); - assert!(!stdout.contains("inside fuzz test, x is:"), "\n{stdout}"); + cmd.args(["test", "-vv"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/ContractFuzz.t.sol:ContractFuzz +[PASS] testFuzzConsoleLog(uint256) (runs: 3, [AVG_GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"#]]); }); // tests that `forge test` with inline config `show_logs = false` for fuzz tests will not @@ -1031,9 +1152,18 @@ forgetest_init!(should_not_show_logs_when_fuzz_test_inline_config, |prj, cmd| { "#, ) .unwrap(); - cmd.args(["test", "-vv"]); - let stdout = cmd.stdout_lossy(); - assert!(!stdout.contains("inside fuzz test, x is:"), "\n{stdout}"); + cmd.args(["test", "-vv"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/ContractFuzz.t.sol:ContractFuzz +[PASS] testFuzzConsoleLog(uint256) (runs: 3, [AVG_GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"#]]); }); // tests internal functions trace @@ -1084,7 +1214,7 @@ contract SimpleContractTest is Test { ) .unwrap(); cmd.args(["test", "-vvvv", "--decode-internal"]).assert_success().stdout_eq(str![[r#" -Compiling 24 files with [SOLC_VERSION] +[COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! @@ -1211,29 +1341,37 @@ contract DeterministicRandomnessTest is Test { // Run the test twice with the same seed and verify the outputs are the same. let seed1 = "0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"; - cmd.args(["test", "--fuzz-seed", seed1, "-vv"]).assert_success(); - let out1 = cmd.stdout_lossy(); + let out1 = cmd + .args(["test", "--fuzz-seed", seed1, "-vv"]) + .assert_success() + .get_output() + .stdout_lossy(); let res1 = extract_test_result(&out1); - cmd.forge_fuse(); - cmd.args(["test", "--fuzz-seed", seed1, "-vv"]).assert_success(); - let out2 = cmd.stdout_lossy(); + let out2 = cmd + .forge_fuse() + .args(["test", "--fuzz-seed", seed1, "-vv"]) + .assert_success() + .get_output() + .stdout_lossy(); let res2 = extract_test_result(&out2); assert_eq!(res1, res2); // Run the test with another seed and verify the output differs. let seed2 = "0xb1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"; - cmd.forge_fuse(); - cmd.args(["test", "--fuzz-seed", seed2, "-vv"]).assert_success(); - let out3 = cmd.stdout_lossy(); + let out3 = cmd + .forge_fuse() + .args(["test", "--fuzz-seed", seed2, "-vv"]) + .assert_success() + .get_output() + .stdout_lossy(); let res3 = extract_test_result(&out3); assert_ne!(res3, res1); // Run the test without a seed and verify the outputs differs once again. cmd.forge_fuse(); - cmd.args(["test", "-vv"]).assert_success(); - let out4 = cmd.stdout_lossy(); + let out4 = cmd.args(["test", "-vv"]).assert_success().get_output().stdout_lossy(); let res4 = extract_test_result(&out4); assert_ne!(res4, res1); assert_ne!(res4, res3); diff --git a/crates/forge/tests/cli/verify.rs b/crates/forge/tests/cli/verify.rs index 2f1f1368d..c8ce2c25f 100644 --- a/crates/forge/tests/cli/verify.rs +++ b/crates/forge/tests/cli/verify.rs @@ -5,7 +5,7 @@ use crate::utils::{self, EnvExternalities}; use foundry_common::retry::Retry; use foundry_test_utils::{ forgetest, - util::{TestCommand, TestProject}, + util::{OutputExt, TestCommand, TestProject}, }; use std::time::Duration; @@ -77,7 +77,7 @@ fn parse_verification_result(cmd: &mut TestCommand, retries: u32) -> eyre::Resul // give etherscan some time to verify the contract let retry = Retry::new(retries, Some(Duration::from_secs(30))); retry.run(|| -> eyre::Result<()> { - let output = cmd.unchecked_output(); + let output = cmd.execute(); let out = String::from_utf8_lossy(&output.stdout); println!("{out}"); if out.contains("Contract successfully verified") { @@ -97,7 +97,7 @@ fn await_verification_response(info: EnvExternalities, mut cmd: TestCommand) { let retry = Retry::new(5, Some(Duration::from_secs(60))); retry .run(|| -> eyre::Result { - let output = cmd.unchecked_output(); + let output = cmd.execute(); let out = String::from_utf8_lossy(&output.stdout); utils::parse_verification_guid(&out).ok_or_else(|| { eyre::eyre!( @@ -132,11 +132,15 @@ fn verify_on_chain(info: Option, prj: TestProject, mut cmd: Te add_verify_target(&prj); let contract_path = "src/Verify.sol:Verify"; - cmd.arg("create").args(info.create_args()).arg(contract_path); - - let out = cmd.stdout_lossy(); - let address = utils::parse_deployed_address(out.as_str()) - .unwrap_or_else(|| panic!("Failed to parse deployer {out}")); + let output = cmd + .arg("create") + .args(info.create_args()) + .arg(contract_path) + .assert_success() + .get_output() + .stdout_lossy(); + let address = utils::parse_deployed_address(output.as_str()) + .unwrap_or_else(|| panic!("Failed to parse deployer {output}")); cmd.forge_fuse().arg("verify-contract").root_arg().args([ "--chain-id".to_string(), @@ -161,15 +165,21 @@ fn guess_constructor_args(info: Option, prj: TestProject, mut add_verify_target_with_constructor(&prj); let contract_path = "src/Verify.sol:Verify"; - cmd.arg("create").args(info.create_args()).arg(contract_path).args(vec![ - "--constructor-args", - "(239,SomeString)", - "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", - ]); - - let out = cmd.stdout_lossy(); - let address = utils::parse_deployed_address(out.as_str()) - .unwrap_or_else(|| panic!("Failed to parse deployer {out}")); + let output = cmd + .arg("create") + .args(info.create_args()) + .arg(contract_path) + .args(vec![ + "--constructor-args", + "(239,SomeString)", + "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", + ]) + .assert_success() + .get_output() + .stdout_lossy(); + + let address = utils::parse_deployed_address(output.as_str()) + .unwrap_or_else(|| panic!("Failed to parse deployer {output}")); cmd.forge_fuse().arg("verify-contract").root_arg().args([ "--rpc-url".to_string(), @@ -195,15 +205,15 @@ fn create_verify_on_chain(info: Option, prj: TestProject, mut add_single_verify_target_file(&prj); let contract_path = "src/Verify.sol:Verify"; - cmd.arg("create").args(info.create_args()).args([ - contract_path, - "--etherscan-api-key", - info.etherscan.as_str(), - "--verify", - ]); - - let out = cmd.stdout_lossy(); - assert!(out.contains("Contract successfully verified"), "{}", out); + let output = cmd + .arg("create") + .args(info.create_args()) + .args([contract_path, "--etherscan-api-key", info.etherscan.as_str(), "--verify"]) + .assert_success() + .get_output() + .stdout_lossy(); + + assert!(output.contains("Contract successfully verified"), "{}", output); } } diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index 960e22124..ca0f60d0d 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -25,12 +25,10 @@ alloy-provider.workspace = true eyre.workspace = true fd-lock = "4.0.0" parking_lot.workspace = true -similar-asserts.workspace = true regex = "1" serde_json.workspace = true tracing.workspace = true tracing-subscriber = { workspace = true, features = ["env-filter"] } -walkdir.workspace = true rand.workspace = true snapbox = { version = "0.6.9", features = ["json", "regex"] } diff --git a/crates/test-utils/src/macros.rs b/crates/test-utils/src/macros.rs index 064a94ef2..cc92bb040 100644 --- a/crates/test-utils/src/macros.rs +++ b/crates/test-utils/src/macros.rs @@ -15,7 +15,7 @@ /// // adds `init` to forge's command arguments /// cmd.arg("init"); /// // executes forge and panics if the command failed or output is empty -/// cmd.assert_non_empty_stdout(); +/// cmd.assert_success().stdout_eq(str![[r#""#]]); /// }); /// ``` /// diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index 0bc4d399d..9b0185732 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -1,4 +1,4 @@ -use crate::{init_tracing, TestCommand}; +use crate::{init_tracing, util::lossy_string, TestCommand}; use alloy_primitives::Address; use alloy_provider::Provider; use eyre::Result; @@ -221,7 +221,9 @@ impl ScriptTester { } pub fn run(&mut self, expected: ScriptOutcome) -> &mut Self { - let (stdout, stderr) = self.cmd.unchecked_output_lossy(); + let out = self.cmd.execute(); + let (stdout, stderr) = (lossy_string(&out.stdout), lossy_string(&out.stderr)); + trace!(target: "tests", "STDOUT\n{stdout}\n\nSTDERR\n{stderr}"); let output = if expected.is_err() { &stderr } else { &stdout }; diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index ac5674d9a..4b9f8f53e 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -11,7 +11,7 @@ use foundry_compilers::{ use foundry_config::Config; use parking_lot::Mutex; use regex::Regex; -use snapbox::cmd::OutputAssert; +use snapbox::{cmd::OutputAssert, str}; use std::{ env, ffi::OsStr, @@ -204,7 +204,7 @@ impl ExtTester { } test_cmd.env("FOUNDRY_INVARIANT_DEPTH", "15"); - test_cmd.assert_non_empty_stdout(); + test_cmd.assert_success(); } } @@ -382,8 +382,7 @@ pub fn try_setup_forge_remote( let prj = TestProject::with_project(tmp); if config.run_build { let mut cmd = prj.forge_command(); - cmd.arg("build"); - cmd.ensure_execute_success().wrap_err("`forge build` unsuccessful")?; + cmd.arg("build").assert_success(); } for addon in config.run_commands { debug_assert!(!addon.is_empty()); @@ -835,104 +834,83 @@ impl TestCommand { self } - /// Does not apply [`snapbox`] redactions to the command output. - pub fn with_no_redact(&mut self) -> &mut Self { - self.redact_output = false; - self - } - /// Returns the `Config` as spit out by `forge config` #[track_caller] pub fn config(&mut self) -> Config { self.cmd.args(["config", "--json"]); - let output = self.output(); - let c = lossy_string(&output.stdout); - let config = serde_json::from_str(c.as_ref()).unwrap(); + let output = self.assert().success().get_output().stdout_lossy(); + let config = serde_json::from_str(output.as_ref()).unwrap(); self.forge_fuse(); config } /// Runs `git init` inside the project's dir #[track_caller] - pub fn git_init(&self) -> Output { + pub fn git_init(&self) { let mut cmd = Command::new("git"); cmd.arg("init").current_dir(self.project.root()); - let output = cmd.output().unwrap(); - self.ensure_success(&output).unwrap(); - output - } - - /// Returns a new [Command] that is inside the current project dir - pub fn cmd_in_current_dir(&self, program: &str) -> Command { - let mut cmd = Command::new(program); - cmd.current_dir(self.project.root()); - cmd + let output = OutputAssert::new(cmd.output().unwrap()); + output.success(); } /// Runs `git add .` inside the project's dir #[track_caller] - pub fn git_add(&self) -> Result<()> { - let mut cmd = self.cmd_in_current_dir("git"); + pub fn git_add(&self) { + let mut cmd = Command::new("git"); + cmd.current_dir(self.project.root()); cmd.arg("add").arg("."); - let output = cmd.output()?; - self.ensure_success(&output) + let output = OutputAssert::new(cmd.output().unwrap()); + output.success(); } /// Runs `git commit .` inside the project's dir #[track_caller] - pub fn git_commit(&self, msg: &str) -> Result<()> { - let mut cmd = self.cmd_in_current_dir("git"); + pub fn git_commit(&self, msg: &str) { + let mut cmd = Command::new("git"); + cmd.current_dir(self.project.root()); cmd.arg("commit").arg("-m").arg(msg); - let output = cmd.output()?; - self.ensure_success(&output) + let output = OutputAssert::new(cmd.output().unwrap()); + output.success(); } - /// Executes the command and returns the `(stdout, stderr)` of the output as lossy `String`s. - /// - /// Expects the command to be successful. + /// Runs the command, returning a [`snapbox`] object to assert the command output. #[track_caller] - pub fn output_lossy(&mut self) -> (String, String) { - let output = self.output(); - (lossy_string(&output.stdout), lossy_string(&output.stderr)) + pub fn assert(&mut self) -> OutputAssert { + let assert = OutputAssert::new(self.execute()); + if self.redact_output { + return assert.with_assert(test_assert()); + }; + assert } - /// Executes the command and returns the `(stdout, stderr)` of the output as lossy `String`s. - /// - /// Does not expect the command to be successful. + /// Runs the command and asserts that it resulted in success. #[track_caller] - pub fn unchecked_output_lossy(&mut self) -> (String, String) { - let output = self.unchecked_output(); - (lossy_string(&output.stdout), lossy_string(&output.stderr)) + pub fn assert_success(&mut self) -> OutputAssert { + self.assert().success() } - /// Executes the command and returns the stderr as lossy `String`. - /// - /// **Note**: This function checks whether the command was successful. + /// Runs the command and asserts that it **failed** nothing was printed to stdout. #[track_caller] - pub fn stdout_lossy(&mut self) -> String { - lossy_string(&self.output().stdout) + pub fn assert_empty_stdout(&mut self) { + self.assert_success().stdout_eq(str![[r#""#]]); } - /// Executes the command and returns the stderr as lossy `String`. - /// - /// **Note**: This function does **not** check whether the command was successful. + /// Runs the command and asserts that it failed. #[track_caller] - pub fn stderr_lossy(&mut self) -> String { - lossy_string(&self.unchecked_output().stderr) + pub fn assert_failure(&mut self) -> OutputAssert { + self.assert().failure() } - /// Returns the output but does not expect that the command was successful + /// Runs the command and asserts that it **failed** nothing was printed to stderr. #[track_caller] - pub fn unchecked_output(&mut self) -> Output { - self.execute() + pub fn assert_empty_stderr(&mut self) { + self.assert_failure().stderr_eq(str![[r#""#]]); } - /// Gets the output of a command. If the command failed, then this panics. - #[track_caller] - pub fn output(&mut self) -> Output { - let output = self.execute(); - self.ensure_success(&output).unwrap(); - output + /// Does not apply [`snapbox`] redactions to the command output. + pub fn with_no_redact(&mut self) -> &mut Self { + self.redact_output = false; + self } /// Executes command, applies stdin function and returns output @@ -951,142 +929,6 @@ impl TestCommand { } child.wait_with_output() } - - /// Executes command and expects an successful result - #[track_caller] - pub fn ensure_execute_success(&mut self) -> Result { - let out = self.try_execute()?; - self.ensure_success(&out)?; - Ok(out) - } - - /// Runs the command and prints its output - /// You have to pass --nocapture to cargo test or the print won't be displayed. - /// The full command would be: cargo test -- --nocapture - #[track_caller] - pub fn print_output(&mut self) { - let output = self.execute(); - println!("stdout:\n{}", lossy_string(&output.stdout)); - println!("\nstderr:\n{}", lossy_string(&output.stderr)); - } - - /// Writes the content of the output to new fixture files - #[track_caller] - pub fn write_fixtures(&mut self, name: impl AsRef) { - let name = name.as_ref(); - if let Some(parent) = name.parent() { - fs::create_dir_all(parent).unwrap(); - } - let output = self.execute(); - fs::write(format!("{}.stdout", name.display()), &output.stdout).unwrap(); - fs::write(format!("{}.stderr", name.display()), &output.stderr).unwrap(); - } - - /// Runs the command and asserts that it **failed** (resulted in an error exit code). - #[track_caller] - pub fn assert_err(&mut self) { - let out = self.execute(); - if out.status.success() { - self.make_panic(&out, true); - } - } - - /// Runs the command and asserts that it **failed** and something was printed to stderr. - #[track_caller] - pub fn assert_non_empty_stderr(&mut self) { - let out = self.execute(); - if out.status.success() || out.stderr.is_empty() { - self.make_panic(&out, true); - } - } - - /// Runs the command and asserts that it **succeeded** and something was printed to stdout. - #[track_caller] - pub fn assert_non_empty_stdout(&mut self) { - let out = self.execute(); - if !out.status.success() || out.stdout.is_empty() { - self.make_panic(&out, false); - } - } - - /// Runs the command and asserts that it **failed** nothing was printed to stdout. - #[track_caller] - pub fn assert_empty_stdout(&mut self) { - let out = self.execute(); - if !out.status.success() || !out.stderr.is_empty() { - self.make_panic(&out, true); - } - } - - #[track_caller] - pub fn ensure_success(&self, out: &Output) -> Result<()> { - if out.status.success() { - Ok(()) - } else { - Err(self.make_error(out, false)) - } - } - - #[track_caller] - fn make_panic(&self, out: &Output, expected_fail: bool) -> ! { - panic!("{}", self.make_error_message(out, expected_fail)) - } - - #[track_caller] - fn make_error(&self, out: &Output, expected_fail: bool) -> eyre::Report { - eyre::eyre!("{}", self.make_error_message(out, expected_fail)) - } - - pub fn make_error_message(&self, out: &Output, expected_fail: bool) -> String { - let msg = if expected_fail { - "expected failure but command succeeded!" - } else { - "command failed but expected success!" - }; - format!( - "\ ---- {:?} --- -{msg} - -status: {} - -paths: -{} - -stdout: -{} - -stderr: -{}", - self.cmd, - out.status, - self.project.inner.paths(), - lossy_string(&out.stdout), - lossy_string(&out.stderr), - ) - } - - /// Runs the command, returning a [`snapbox`] object to assert the command output. - #[track_caller] - pub fn assert(&mut self) -> OutputAssert { - let assert = OutputAssert::new(self.execute()); - if self.redact_output { - return assert.with_assert(test_assert()); - }; - assert - } - - /// Runs the command and asserts that it resulted in success. - #[track_caller] - pub fn assert_success(&mut self) -> OutputAssert { - self.assert().success() - } - - /// Runs the command and asserts that it failed. - #[track_caller] - pub fn assert_failure(&mut self) -> OutputAssert { - self.assert().failure() - } } fn test_assert() -> snapbox::Assert { @@ -1105,7 +947,15 @@ fn test_redactions() -> snapbox::Redactions { ("[AVG_GAS]", r"μ: \d+, ~: \d+"), ("[FILE]", r"-->.*\.sol"), ("[FILE]", r"Location(.|\n)*\.rs(.|\n)*Backtrace"), + ("[COMPILING_FILES]", r"Compiling \d+ files?"), ("[TX_HASH]", r"Transaction hash: 0x[0-9A-Fa-f]{64}"), + ("[ADDRESS]", r"Address: 0x[0-9A-Fa-f]{40}"), + ("[UPDATING_DEPENDENCIES]", r"Updating dependencies in .*"), + ("[SAVED_TRANSACTIONS]", r"Transactions saved to: .*\.json"), + ("[SAVED_SENSITIVE_VALUES]", r"Sensitive values saved to: .*\.json"), + ("[ESTIMATED_GAS_PRICE]", r"Estimated gas price:\s*(\d+(\.\d+)?)\s*gwei"), + ("[ESTIMATED_TOTAL_GAS_USED]", r"Estimated total gas used for script: \d+"), + ("[ESTIMATED_AMOUNT_REQUIRED]", r"Estimated amount required:\s*(\d+(\.\d+)?)\s*ETH"), ]; for (placeholder, re) in redactions { r.insert(placeholder, Regex::new(re).expect(re)).expect(re); @@ -1116,135 +966,17 @@ fn test_redactions() -> snapbox::Redactions { } /// Extension trait for [`Output`]. -/// -/// These function will read the path's content and assert that the process' output matches the -/// fixture. Since `forge` commands may emit colorized output depending on whether the current -/// terminal is tty, the path argument can be wrapped in [tty_fixture_path()] pub trait OutputExt { - /// Ensure the command wrote the expected data to `stdout`. - fn stdout_matches_content(&self, expected: &str); - - /// Ensure the command wrote the expected data to `stdout`. - fn stdout_matches_path(&self, expected_path: impl AsRef); - - /// Ensure the command wrote the expected data to `stderr`. - fn stderr_matches_path(&self, expected_path: impl AsRef); - - /// Returns the stderr as lossy string - fn stderr_lossy(&self) -> String; - /// Returns the stdout as lossy string fn stdout_lossy(&self) -> String; } -/// Patterns to remove from fixtures before comparing output -/// -/// This should strip everything that can vary from run to run, like elapsed time, file paths -static IGNORE_IN_FIXTURES: LazyLock = LazyLock::new(|| { - let re = &[ - // solc version - r" ?Solc(?: version)? \d+.\d+.\d+", - r" with(?: Solc)? \d+.\d+.\d+", - // solc runs - r"runs: \d+, μ: \d+, ~: \d+", - // elapsed time - r"(?:finished)? ?in .*?s(?: \(.*?s CPU time\))?", - // file paths - r"-->.*\.sol", - r"Location(.|\n)*\.rs(.|\n)*Backtrace", - // other - r"Transaction hash: 0x[0-9A-Fa-f]{64}", - ]; - Regex::new(&format!("({})", re.join("|"))).unwrap() -}); - -pub fn normalize_output(s: &str) -> String { - let s = s.replace("\r\n", "\n").replace('\\', "/"); - IGNORE_IN_FIXTURES.replace_all(&s, "").into_owned() -} - impl OutputExt for Output { - #[track_caller] - fn stdout_matches_content(&self, expected: &str) { - let out = lossy_string(&self.stdout); - similar_asserts::assert_eq!(normalize_output(&out), normalize_output(expected)); - } - - #[track_caller] - fn stdout_matches_path(&self, expected_path: impl AsRef) { - let expected = fs::read_to_string(expected_path).unwrap(); - self.stdout_matches_content(&expected); - } - - #[track_caller] - fn stderr_matches_path(&self, expected_path: impl AsRef) { - let expected = fs::read_to_string(expected_path).unwrap(); - let err = lossy_string(&self.stderr); - similar_asserts::assert_eq!(normalize_output(&err), normalize_output(&expected)); - } - - fn stderr_lossy(&self) -> String { - lossy_string(&self.stderr) - } - fn stdout_lossy(&self) -> String { lossy_string(&self.stdout) } } -/// Returns the fixture path depending on whether the current terminal is tty -/// -/// This is useful in combination with [OutputExt] -pub fn tty_fixture_path(path: impl AsRef) -> PathBuf { - let path = path.as_ref(); - if *IS_TTY { - return if let Some(ext) = path.extension().and_then(|s| s.to_str()) { - path.with_extension(format!("tty.{ext}")) - } else { - path.with_extension("tty") - } - } - path.to_path_buf() -} - -/// Return a recursive listing of all files and directories in the given -/// directory. This is useful for debugging transient and odd failures in -/// integration tests. -pub fn dir_list>(dir: P) -> Vec { - walkdir::WalkDir::new(dir) - .follow_links(true) - .into_iter() - .map(|result| result.unwrap().path().to_string_lossy().into_owned()) - .collect() -} - -fn lossy_string(bytes: &[u8]) -> String { +pub fn lossy_string(bytes: &[u8]) -> String { String::from_utf8_lossy(bytes).replace("\r\n", "\n") } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn tty_path_works() { - let path = "tests/fixture/test.stdout"; - if *IS_TTY { - assert_eq!(tty_fixture_path(path), PathBuf::from("tests/fixture/test.tty.stdout")); - } else { - assert_eq!(tty_fixture_path(path), PathBuf::from(path)); - } - } - - #[test] - fn fixture_regex_matches() { - assert!(IGNORE_IN_FIXTURES.is_match( - r" -Location: - cli/src/compile.rs:151 - -Backtrace omitted. - " - )); - } -} diff --git a/crates/verify/src/etherscan/mod.rs b/crates/verify/src/etherscan/mod.rs index 9136c200d..0a1d39c19 100644 --- a/crates/verify/src/etherscan/mod.rs +++ b/crates/verify/src/etherscan/mod.rs @@ -455,7 +455,7 @@ mod tests { use super::*; use clap::Parser; use foundry_common::fs; - use foundry_test_utils::forgetest_async; + use foundry_test_utils::{forgetest_async, str}; use tempfile::tempdir; #[test] @@ -566,7 +566,12 @@ mod tests { prj.add_source("Counter1", "contract Counter {}").unwrap(); prj.add_source("Counter2", "contract Counter {}").unwrap(); - cmd.args(["build", "--force"]).ensure_execute_success().unwrap(); + cmd.args(["build", "--force"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +"#]]); let args = VerifyArgs::parse_from([ "foundry-cli", From 9e79f54d3bba74a860aadce072adf3e1875a9da5 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Thu, 29 Aug 2024 17:09:59 +0530 Subject: [PATCH 122/184] fix(forge vb): bail when args expected but none provided (#8764) fix(forge vb): bail when args are expected but none are provided --- crates/verify/src/bytecode.rs | 4 ++++ crates/verify/src/utils.rs | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index b1e4f7e52..8fffd84d6 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -209,6 +209,10 @@ impl VerifyBytecodeArgs { check_explorer_args(source_code.clone())? }; + // This fails only when the contract expects constructor args but NONE were provided OR + // retrieved from explorer (in case of predeploys). + crate::utils::check_args_len(&artifact, &constructor_args)?; + if maybe_predeploy { if !self.json { println!( diff --git a/crates/verify/src/utils.rs b/crates/verify/src/utils.rs index d5545df67..1b7c7fada 100644 --- a/crates/verify/src/utils.rs +++ b/crates/verify/src/utils.rs @@ -307,6 +307,21 @@ pub fn check_explorer_args(source_code: ContractMetadata) -> Result Result<(), eyre::ErrReport> { + if let Some(constructor) = artifact.abi.as_ref().and_then(|abi| abi.constructor()) { + if !constructor.inputs.is_empty() && args.len() == 0 { + eyre::bail!( + "Contract expects {} constructor argument(s), but none were provided", + constructor.inputs.len() + ); + } + } + Ok(()) +} + pub async fn get_tracing_executor( fork_config: &mut Config, fork_blk_num: u64, From 41198f33b911440c410395a8af6166b4d19e998f Mon Sep 17 00:00:00 2001 From: zhiqiangxu <652732310@qq.com> Date: Thu, 29 Aug 2024 19:57:04 +0800 Subject: [PATCH 123/184] support positional `--mp` parameter for `forge test` (#8751) * support positional --mp parameter * use panic instead --- crates/forge/bin/cmd/test/mod.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index ec582f298..b0792a8cf 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -1,6 +1,6 @@ use super::{install, test::filter::ProjectPathsAwareFilter, watch::WatchArgs}; use alloy_primitives::U256; -use clap::Parser; +use clap::{Parser, ValueHint}; use eyre::Result; use forge::{ decode::decode_console_logs, @@ -32,6 +32,7 @@ use foundry_config::{ value::{Dict, Map}, Metadata, Profile, Provider, }, + filter::GlobMatcher, get_available_profiles, Config, }; use foundry_debugger::Debugger; @@ -58,6 +59,10 @@ foundry_config::merge_impl_figment_convert!(TestArgs, opts, evm_opts); #[derive(Clone, Debug, Parser)] #[command(next_help_heading = "Test options")] pub struct TestArgs { + /// The contract file you want to test, it's a shortcut for --match-path. + #[arg(value_hint = ValueHint::FilePath)] + pub path: Option, + /// Run a test in the debugger. /// /// The argument passed to this flag is the name of the test function you want to run, and it @@ -634,6 +639,13 @@ impl TestArgs { if self.rerun { filter.test_pattern = last_run_failures(config); } + if filter.path_pattern.is_some() { + if self.path.is_some() { + panic!("Can not supply both --match-path and |path|"); + } + } else { + filter.path_pattern = self.path.clone(); + } filter.merge_with_config(config) } From 98ab45eeb5c3b8d07dede2f27df96f4778d89300 Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 29 Aug 2024 14:27:14 +0200 Subject: [PATCH 124/184] feat: support mesc (#8760) --- Cargo.lock | 12 ++++++++++++ Cargo.toml | 1 + crates/config/Cargo.toml | 1 + crates/config/src/lib.rs | 25 +++++++++++++++++++++---- 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4b71c523e..99ee3dae2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3816,6 +3816,7 @@ dependencies = [ "foundry-compilers", "glob", "globset", + "mesc", "number_prefix", "path-slash", "regex", @@ -5557,6 +5558,17 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mesc" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d340f1a6bfcd3ea5075231fb04b74c8bf7cb3be25ee6480976c8500fcd2949f" +dependencies = [ + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "miette" version = "7.2.0" diff --git a/Cargo.toml b/Cargo.toml index c6c8aee07..50c33c653 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -231,6 +231,7 @@ itertools = "0.13" jsonpath_lib = "0.3" k256 = "0.13" parking_lot = "0.12" +mesc = "0.2.1" rand = "0.8" rustc-hash = "2.0" semver = "1" diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index f15598122..357830166 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -31,6 +31,7 @@ globset = "0.4" glob = "0.3" Inflector = "0.11" number_prefix = "0.4" +mesc.workspace = true regex = "1" reqwest.workspace = true semver = { workspace = true, features = ["serde"] } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 13dfc0474..d0fcc5a14 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -1075,9 +1075,18 @@ impl Config { /// Resolves the given alias to a matching rpc url /// - /// Returns: - /// - the matching, resolved url of `rpc_endpoints` if `maybe_alias` is an alias - /// - None otherwise + /// # Returns + /// + /// In order of resolution: + /// + /// - the matching, resolved url of `rpc_endpoints` if `maybe_alias` is an alias + /// - a mesc resolved url if `maybe_alias` is a known alias in mesc + /// - `None` otherwise + /// + /// # Note on mesc + /// + /// The endpoint is queried for in mesc under the `foundry` profile, allowing users to customize + /// endpoints for Foundry specifically. /// /// # Example /// @@ -1093,7 +1102,15 @@ impl Config { maybe_alias: &str, ) -> Option, UnresolvedEnvVarError>> { let mut endpoints = self.rpc_endpoints.clone().resolved(); - Some(endpoints.remove(maybe_alias)?.map(Cow::Owned)) + if let Some(endpoint) = endpoints.remove(maybe_alias) { + return Some(endpoint.map(Cow::Owned)) + } + + if let Ok(Some(endpoint)) = mesc::get_endpoint_by_query(maybe_alias, Some("foundry")) { + return Some(Ok(Cow::Owned(endpoint.url))) + } + + None } /// Returns the configured rpc, or the fallback url From 818eeb9d5018d3858238d925fa9c9ef5fcdaee47 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 30 Aug 2024 14:40:19 +0300 Subject: [PATCH 125/184] fix(fmt): write files on disk only if they're not perfect match (#8775) * fix(fmt): write files on disk only if they're not perfect match * Cleanup --- crates/fmt/src/formatter.rs | 2 +- crates/fmt/testdata/Repros/fmt.sol | 3 +-- crates/forge/bin/cmd/fmt.rs | 11 ++++++++--- .../invariant/common/InvariantPreserveState.t.sol | 5 +++-- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/crates/fmt/src/formatter.rs b/crates/fmt/src/formatter.rs index 1c5000330..a44aa697c 100644 --- a/crates/fmt/src/formatter.rs +++ b/crates/fmt/src/formatter.rs @@ -1246,7 +1246,7 @@ impl<'a, W: Write> Formatter<'a, W> { })?; write_chunk!(self, "}}")?; - return Ok(true) + return Ok(false) } // Determine writable statements by excluding statements from disabled start / end lines. diff --git a/crates/fmt/testdata/Repros/fmt.sol b/crates/fmt/testdata/Repros/fmt.sol index 0842c7956..a61a626a0 100644 --- a/crates/fmt/testdata/Repros/fmt.sol +++ b/crates/fmt/testdata/Repros/fmt.sol @@ -139,8 +139,7 @@ contract IfElseTest { number = 1; } else if (newNumber = 2) { // number = 2; - } - else { + } else { newNumber = 3; } } diff --git a/crates/forge/bin/cmd/fmt.rs b/crates/forge/bin/cmd/fmt.rs index c46704836..9fd016ac7 100644 --- a/crates/forge/bin/cmd/fmt.rs +++ b/crates/forge/bin/cmd/fmt.rs @@ -125,17 +125,22 @@ impl FmtArgs { ) })?; + let diff = TextDiff::from_lines(&source, &output); + let new_format = diff.ratio() < 1.0; if self.check || path.is_none() { if self.raw { print!("{output}"); } - let diff = TextDiff::from_lines(&source, &output); - if diff.ratio() < 1.0 { + // If new format then compute diff summary. + if new_format { return Ok(Some(format_diff_summary(&name, &diff))) } } else if let Some(path) = path { - fs::write(path, output)?; + // If new format then write it on disk. + if new_format { + fs::write(path, output)?; + } } Ok(None) }; diff --git a/testdata/default/fuzz/invariant/common/InvariantPreserveState.t.sol b/testdata/default/fuzz/invariant/common/InvariantPreserveState.t.sol index b91cda739..546980136 100644 --- a/testdata/default/fuzz/invariant/common/InvariantPreserveState.t.sol +++ b/testdata/default/fuzz/invariant/common/InvariantPreserveState.t.sol @@ -15,8 +15,9 @@ contract Handler is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); function thisFunctionReverts() external { - if (block.number < 10) {} - else revert(); + if (block.number < 10) {} else { + revert(); + } } function advanceTime(uint256 blocks) external { From d75318c9c7a1c6af5404fe96f63ca890dcdd588d Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Sat, 31 Aug 2024 17:03:00 -0700 Subject: [PATCH 126/184] Optimism Hardfork Support (#8749) optimism hardfork Co-authored-by: Arsenii Kulikov --- crates/anvil/src/cmd.rs | 50 ++++++++-- crates/anvil/src/config.rs | 24 +++-- crates/anvil/src/hardfork.rs | 141 ++++++++++++++++++++------- crates/anvil/src/lib.rs | 2 +- crates/anvil/tests/it/anvil_api.rs | 8 +- crates/anvil/tests/it/eip4844.rs | 12 +-- crates/anvil/tests/it/eip7702.rs | 4 +- crates/anvil/tests/it/optimism.rs | 26 +++-- crates/anvil/tests/it/otterscan.rs | 12 +-- crates/anvil/tests/it/traces.rs | 5 +- crates/anvil/tests/it/transaction.rs | 5 +- crates/cast/tests/cli/main.rs | 5 +- 12 files changed, 208 insertions(+), 86 deletions(-) diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index e21f12d36..1238e5906 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -1,7 +1,8 @@ use crate::{ config::{ForkChoice, DEFAULT_MNEMONIC}, eth::{backend::db::SerializableState, pool::transactions::TransactionOrder, EthApi}, - AccountGenerator, Hardfork, NodeConfig, CHAIN_ID, + hardfork::OptimismHardfork, + AccountGenerator, EthereumHardfork, NodeConfig, CHAIN_ID, }; use alloy_genesis::Genesis; use alloy_primitives::{utils::Unit, B256, U256}; @@ -79,8 +80,8 @@ pub struct NodeArgs { /// /// Choose the hardfork by name, e.g. `shanghai`, `paris`, `london`, etc... /// [default: latest] - #[arg(long, value_parser = Hardfork::from_str)] - pub hardfork: Option, + #[arg(long)] + pub hardfork: Option, /// Block time in seconds for interval mining. #[arg(short, long, visible_alias = "blockTime", value_name = "SECONDS", value_parser = duration_from_secs_f64)] @@ -196,7 +197,7 @@ const IPC_HELP: &str = "Launch an ipc server at the given path or default path = const DEFAULT_DUMP_INTERVAL: Duration = Duration::from_secs(60); impl NodeArgs { - pub fn into_node_config(self) -> NodeConfig { + pub fn into_node_config(self) -> eyre::Result { let genesis_balance = Unit::ETHER.wei().saturating_mul(U256::from(self.balance)); let compute_units_per_second = if self.evm_opts.no_rate_limit { Some(u64::MAX) @@ -204,11 +205,22 @@ impl NodeArgs { self.evm_opts.compute_units_per_second }; - NodeConfig::default() + let hardfork = match &self.hardfork { + Some(hf) => { + if self.evm_opts.optimism { + Some(OptimismHardfork::from_str(hf)?.into()) + } else { + Some(EthereumHardfork::from_str(hf)?.into()) + } + } + None => None, + }; + + Ok(NodeConfig::default() .with_gas_limit(self.evm_opts.gas_limit) .disable_block_gas_limit(self.evm_opts.disable_block_gas_limit) .with_gas_price(self.evm_opts.gas_price) - .with_hardfork(self.hardfork) + .with_hardfork(hardfork) .with_blocktime(self.block_time) .with_no_mining(self.no_mining) .with_mixed_mining(self.mixed_mining, self.block_time) @@ -255,7 +267,7 @@ impl NodeArgs { .with_alphanet(self.evm_opts.alphanet) .with_disable_default_create2_deployer(self.evm_opts.disable_default_create2_deployer) .with_slots_in_an_epoch(self.slots_in_an_epoch) - .with_memory_limit(self.evm_opts.memory_limit) + .with_memory_limit(self.evm_opts.memory_limit)) } fn account_generator(&self) -> AccountGenerator { @@ -295,7 +307,7 @@ impl NodeArgs { let dump_interval = self.state_interval.map(Duration::from_secs).unwrap_or(DEFAULT_DUMP_INTERVAL); - let (api, mut handle) = crate::try_spawn(self.into_node_config()).await?; + let (api, mut handle) = crate::try_spawn(self.into_node_config()?).await?; // sets the signal handler to gracefully shutdown. let mut fork = api.get_fork(); @@ -739,6 +751,8 @@ fn duration_from_secs_f64(s: &str) -> Result { #[cfg(test)] mod tests { + use crate::EthereumHardfork; + use super::*; use std::{env, net::Ipv4Addr}; @@ -773,9 +787,25 @@ mod tests { } #[test] - fn can_parse_hardfork() { + fn can_parse_ethereum_hardfork() { let args: NodeArgs = NodeArgs::parse_from(["anvil", "--hardfork", "berlin"]); - assert_eq!(args.hardfork, Some(Hardfork::Berlin)); + let config = args.into_node_config().unwrap(); + assert_eq!(config.hardfork, Some(EthereumHardfork::Berlin.into())); + } + + #[test] + fn can_parse_optimism_hardfork() { + let args: NodeArgs = + NodeArgs::parse_from(["anvil", "--optimism", "--hardfork", "Regolith"]); + let config = args.into_node_config().unwrap(); + assert_eq!(config.hardfork, Some(OptimismHardfork::Regolith.into())); + } + + #[test] + fn cant_parse_invalid_hardfork() { + let args: NodeArgs = NodeArgs::parse_from(["anvil", "--hardfork", "Regolith"]); + let config = args.into_node_config(); + assert!(config.is_err()); } #[test] diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index a94d5b827..d5770f703 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -11,8 +11,9 @@ use crate::{ fees::{INITIAL_BASE_FEE, INITIAL_GAS_PRICE}, pool::transactions::{PoolTransaction, TransactionOrder}, }, + hardfork::{ChainHardfork, OptimismHardfork}, mem::{self, in_memory_db::MemDb}, - FeeManager, Hardfork, PrecompileFactory, + EthereumHardfork, FeeManager, PrecompileFactory, }; use alloy_genesis::Genesis; use alloy_network::AnyNetwork; @@ -64,7 +65,6 @@ pub const DEFAULT_MNEMONIC: &str = "test test test test test test test test test /// The default IPC endpoint pub const DEFAULT_IPC_ENDPOINT: &str = if cfg!(unix) { "/tmp/anvil.ipc" } else { r"\\.\pipe\anvil.ipc" }; - /// `anvil 0.1.0 (f01b232bc 2022-04-13T23:28:39.493201+00:00)` pub const VERSION_MESSAGE: &str = concat!( env!("CARGO_PKG_VERSION"), @@ -100,7 +100,7 @@ pub struct NodeConfig { /// Default blob excess gas and price pub blob_excess_gas_and_price: Option, /// The hardfork to use - pub hardfork: Option, + pub hardfork: Option, /// Signer accounts that will be initialised with `genesis_balance` in the genesis block pub genesis_accounts: Vec, /// Native token balance of every genesis account in the genesis block @@ -475,11 +475,17 @@ impl NodeConfig { } /// Returns the hardfork to use - pub fn get_hardfork(&self) -> Hardfork { + pub fn get_hardfork(&self) -> ChainHardfork { if self.alphanet { - return Hardfork::PragueEOF; + return ChainHardfork::Ethereum(EthereumHardfork::PragueEOF); + } + if let Some(hardfork) = self.hardfork { + return hardfork; + } + if self.enable_optimism { + return OptimismHardfork::default().into(); } - self.hardfork.unwrap_or_default() + EthereumHardfork::default().into() } /// Sets a custom code size limit @@ -621,7 +627,7 @@ impl NodeConfig { /// Sets the hardfork #[must_use] - pub fn with_hardfork(mut self, hardfork: Option) -> Self { + pub fn with_hardfork(mut self, hardfork: Option) -> Self { self.hardfork = hardfork; self } @@ -1094,9 +1100,9 @@ impl NodeConfig { let chain_id = provider.get_chain_id().await.expect("Failed to fetch network chain ID"); if alloy_chains::NamedChain::Mainnet == chain_id { - let hardfork: Hardfork = fork_block_number.into(); + let hardfork: EthereumHardfork = fork_block_number.into(); env.handler_cfg.spec_id = hardfork.into(); - self.hardfork = Some(hardfork); + self.hardfork = Some(ChainHardfork::Ethereum(hardfork)); } Some(U256::from(chain_id)) } else { diff --git a/crates/anvil/src/hardfork.rs b/crates/anvil/src/hardfork.rs index de4c43c18..c8d2fdebc 100644 --- a/crates/anvil/src/hardfork.rs +++ b/crates/anvil/src/hardfork.rs @@ -1,9 +1,37 @@ use alloy_rpc_types::BlockNumberOrTag; +use eyre::bail; use foundry_evm::revm::primitives::SpecId; use std::str::FromStr; +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum ChainHardfork { + Ethereum(EthereumHardfork), + Optimism(OptimismHardfork), +} + +impl From for ChainHardfork { + fn from(value: EthereumHardfork) -> Self { + Self::Ethereum(value) + } +} + +impl From for ChainHardfork { + fn from(value: OptimismHardfork) -> Self { + Self::Optimism(value) + } +} + +impl From for SpecId { + fn from(fork: ChainHardfork) -> Self { + match fork { + ChainHardfork::Ethereum(hardfork) => hardfork.into(), + ChainHardfork::Optimism(hardfork) => hardfork.into(), + } + } +} + #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Hardfork { +pub enum EthereumHardfork { Frontier, Homestead, Dao, @@ -27,7 +55,7 @@ pub enum Hardfork { Latest, } -impl Hardfork { +impl EthereumHardfork { /// Get the first block number of the hardfork. pub fn fork_block(&self) -> u64 { match *self { @@ -53,8 +81,8 @@ impl Hardfork { } } -impl FromStr for Hardfork { - type Err = String; +impl FromStr for EthereumHardfork { + type Err = eyre::Report; fn from_str(s: &str) -> Result { let s = s.to_lowercase(); @@ -79,40 +107,40 @@ impl FromStr for Hardfork { "prague" | "18" => Self::Prague, "pragueeof" | "19" | "prague-eof" => Self::PragueEOF, "latest" => Self::Latest, - _ => return Err(format!("Unknown hardfork {s}")), + _ => bail!("Unknown hardfork {s}"), }; Ok(hardfork) } } -impl From for SpecId { - fn from(fork: Hardfork) -> Self { +impl From for SpecId { + fn from(fork: EthereumHardfork) -> Self { match fork { - Hardfork::Frontier => Self::FRONTIER, - Hardfork::Homestead => Self::HOMESTEAD, - Hardfork::Dao => Self::HOMESTEAD, - Hardfork::Tangerine => Self::TANGERINE, - Hardfork::SpuriousDragon => Self::SPURIOUS_DRAGON, - Hardfork::Byzantium => Self::BYZANTIUM, - Hardfork::Constantinople => Self::CONSTANTINOPLE, - Hardfork::Petersburg => Self::PETERSBURG, - Hardfork::Istanbul => Self::ISTANBUL, - Hardfork::Muirglacier => Self::MUIR_GLACIER, - Hardfork::Berlin => Self::BERLIN, - Hardfork::London => Self::LONDON, - Hardfork::ArrowGlacier => Self::LONDON, - Hardfork::GrayGlacier => Self::GRAY_GLACIER, - Hardfork::Paris => Self::MERGE, - Hardfork::Shanghai => Self::SHANGHAI, - Hardfork::Cancun | Hardfork::Latest => Self::CANCUN, - Hardfork::Prague => Self::PRAGUE, + EthereumHardfork::Frontier => Self::FRONTIER, + EthereumHardfork::Homestead => Self::HOMESTEAD, + EthereumHardfork::Dao => Self::HOMESTEAD, + EthereumHardfork::Tangerine => Self::TANGERINE, + EthereumHardfork::SpuriousDragon => Self::SPURIOUS_DRAGON, + EthereumHardfork::Byzantium => Self::BYZANTIUM, + EthereumHardfork::Constantinople => Self::CONSTANTINOPLE, + EthereumHardfork::Petersburg => Self::PETERSBURG, + EthereumHardfork::Istanbul => Self::ISTANBUL, + EthereumHardfork::Muirglacier => Self::MUIR_GLACIER, + EthereumHardfork::Berlin => Self::BERLIN, + EthereumHardfork::London => Self::LONDON, + EthereumHardfork::ArrowGlacier => Self::LONDON, + EthereumHardfork::GrayGlacier => Self::GRAY_GLACIER, + EthereumHardfork::Paris => Self::MERGE, + EthereumHardfork::Shanghai => Self::SHANGHAI, + EthereumHardfork::Cancun | EthereumHardfork::Latest => Self::CANCUN, + EthereumHardfork::Prague => Self::PRAGUE, // TODO: switch to latest after activation - Hardfork::PragueEOF => Self::PRAGUE_EOF, + EthereumHardfork::PragueEOF => Self::PRAGUE_EOF, } } } -impl> From for Hardfork { +impl> From for EthereumHardfork { fn from(block: T) -> Self { let num = match block.into() { BlockNumberOrTag::Earliest => 0, @@ -140,19 +168,64 @@ impl> From for Hardfork { } } +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum OptimismHardfork { + Bedrock, + Regolith, + Canyon, + Ecotone, + Fjord, + Granite, + #[default] + Latest, +} + +impl FromStr for OptimismHardfork { + type Err = eyre::Report; + + fn from_str(s: &str) -> Result { + let s = s.to_lowercase(); + let hardfork = match s.as_str() { + "bedrock" => Self::Bedrock, + "regolith" => Self::Regolith, + "canyon" => Self::Canyon, + "ecotone" => Self::Ecotone, + "fjord" => Self::Fjord, + "granite" => Self::Granite, + "latest" => Self::Latest, + _ => bail!("Unknown hardfork {s}"), + }; + Ok(hardfork) + } +} + +impl From for SpecId { + fn from(fork: OptimismHardfork) -> Self { + match fork { + OptimismHardfork::Bedrock => Self::BEDROCK, + OptimismHardfork::Regolith => Self::REGOLITH, + OptimismHardfork::Canyon => Self::CANYON, + OptimismHardfork::Ecotone => Self::ECOTONE, + OptimismHardfork::Fjord => Self::FJORD, + OptimismHardfork::Granite => Self::GRANITE, + OptimismHardfork::Latest => Self::LATEST, + } + } +} + #[cfg(test)] mod tests { - use crate::Hardfork; + use crate::EthereumHardfork; #[test] fn test_hardfork_blocks() { - let hf: Hardfork = 12_965_000u64.into(); - assert_eq!(hf, Hardfork::London); + let hf: EthereumHardfork = 12_965_000u64.into(); + assert_eq!(hf, EthereumHardfork::London); - let hf: Hardfork = 4370000u64.into(); - assert_eq!(hf, Hardfork::Byzantium); + let hf: EthereumHardfork = 4370000u64.into(); + assert_eq!(hf, EthereumHardfork::Byzantium); - let hf: Hardfork = 12244000u64.into(); - assert_eq!(hf, Hardfork::Berlin); + let hf: EthereumHardfork = 12244000u64.into(); + assert_eq!(hf, EthereumHardfork::Berlin); } } diff --git a/crates/anvil/src/lib.rs b/crates/anvil/src/lib.rs index dc7e0969f..c800131d2 100644 --- a/crates/anvil/src/lib.rs +++ b/crates/anvil/src/lib.rs @@ -48,7 +48,7 @@ mod config; pub use config::{AccountGenerator, ForkChoice, NodeConfig, CHAIN_ID, VERSION_MESSAGE}; mod hardfork; -pub use hardfork::Hardfork; +pub use hardfork::EthereumHardfork; /// ethereum related implementations pub mod eth; diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index e6289e0b0..1b94f5e43 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -13,7 +13,7 @@ use alloy_rpc_types::{ BlockId, BlockNumberOrTag, TransactionRequest, }; use alloy_serde::WithOtherFields; -use anvil::{eth::api::CLIENT_VERSION, spawn, Hardfork, NodeConfig}; +use anvil::{eth::api::CLIENT_VERSION, spawn, EthereumHardfork, NodeConfig}; use anvil_core::eth::EthRequest; use foundry_evm::revm::primitives::SpecId; use std::{ @@ -23,7 +23,8 @@ use std::{ #[tokio::test(flavor = "multi_thread")] async fn can_set_gas_price() { - let (api, handle) = spawn(NodeConfig::test().with_hardfork(Some(Hardfork::Berlin))).await; + let (api, handle) = + spawn(NodeConfig::test().with_hardfork(Some(EthereumHardfork::Berlin.into()))).await; let provider = handle.http_provider(); let gas_price = U256::from(1337); @@ -33,7 +34,8 @@ async fn can_set_gas_price() { #[tokio::test(flavor = "multi_thread")] async fn can_set_block_gas_limit() { - let (api, _) = spawn(NodeConfig::test().with_hardfork(Some(Hardfork::Berlin))).await; + let (api, _) = + spawn(NodeConfig::test().with_hardfork(Some(EthereumHardfork::Berlin.into()))).await; let block_gas_limit = U256::from(1337); assert!(api.evm_set_block_gas_limit(block_gas_limit).unwrap()); diff --git a/crates/anvil/tests/it/eip4844.rs b/crates/anvil/tests/it/eip4844.rs index 40d5a63a6..2da74da65 100644 --- a/crates/anvil/tests/it/eip4844.rs +++ b/crates/anvil/tests/it/eip4844.rs @@ -6,11 +6,11 @@ use alloy_primitives::U256; use alloy_provider::Provider; use alloy_rpc_types::{BlockId, TransactionRequest}; use alloy_serde::WithOtherFields; -use anvil::{spawn, Hardfork, NodeConfig}; +use anvil::{spawn, EthereumHardfork, NodeConfig}; #[tokio::test(flavor = "multi_thread")] async fn can_send_eip4844_transaction() { - let node_config = NodeConfig::test().with_hardfork(Some(Hardfork::Cancun)); + let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Cancun.into())); let (_api, handle) = spawn(node_config).await; let wallets = handle.dev_wallets().collect::>(); @@ -46,7 +46,7 @@ async fn can_send_eip4844_transaction() { #[tokio::test(flavor = "multi_thread")] async fn can_send_multiple_blobs_in_one_tx() { - let node_config = NodeConfig::test().with_hardfork(Some(Hardfork::Cancun)); + let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Cancun.into())); let (_api, handle) = spawn(node_config).await; let wallets = handle.dev_wallets().collect::>(); @@ -84,7 +84,7 @@ async fn can_send_multiple_blobs_in_one_tx() { #[tokio::test(flavor = "multi_thread")] async fn cannot_exceed_six_blobs() { - let node_config = NodeConfig::test().with_hardfork(Some(Hardfork::Cancun)); + let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Cancun.into())); let (_api, handle) = spawn(node_config).await; let wallets = handle.dev_wallets().collect::>(); @@ -121,7 +121,7 @@ async fn cannot_exceed_six_blobs() { #[tokio::test(flavor = "multi_thread")] async fn can_mine_blobs_when_exceeds_max_blobs() { - let node_config = NodeConfig::test().with_hardfork(Some(Hardfork::Cancun)); + let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Cancun.into())); let (api, handle) = spawn(node_config).await; api.anvil_set_auto_mine(false).await.unwrap(); @@ -194,7 +194,7 @@ async fn can_mine_blobs_when_exceeds_max_blobs() { #[tokio::test(flavor = "multi_thread")] async fn can_check_blob_fields_on_genesis() { - let node_config = NodeConfig::test().with_hardfork(Some(Hardfork::Cancun)); + let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Cancun.into())); let (_api, handle) = spawn(node_config).await; let provider = http_provider(&handle.http_endpoint()); diff --git a/crates/anvil/tests/it/eip7702.rs b/crates/anvil/tests/it/eip7702.rs index a23feceb2..828e0d351 100644 --- a/crates/anvil/tests/it/eip7702.rs +++ b/crates/anvil/tests/it/eip7702.rs @@ -6,11 +6,11 @@ use alloy_provider::Provider; use alloy_rpc_types::{Authorization, TransactionRequest}; use alloy_serde::WithOtherFields; use alloy_signer::SignerSync; -use anvil::{spawn, Hardfork, NodeConfig}; +use anvil::{spawn, EthereumHardfork, NodeConfig}; #[tokio::test(flavor = "multi_thread")] async fn can_send_eip7702_tx() { - let node_config = NodeConfig::test().with_hardfork(Some(Hardfork::Prague)); + let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Prague.into())); let (_api, handle) = spawn(node_config).await; let provider = http_provider(&handle.http_endpoint()); diff --git a/crates/anvil/tests/it/optimism.rs b/crates/anvil/tests/it/optimism.rs index f5f4a41ca..4ca74f9fe 100644 --- a/crates/anvil/tests/it/optimism.rs +++ b/crates/anvil/tests/it/optimism.rs @@ -7,7 +7,7 @@ use alloy_primitives::{b256, Address, TxHash, TxKind, U256}; use alloy_provider::Provider; use alloy_rpc_types::{optimism::OptimismTransactionFields, TransactionRequest}; use alloy_serde::WithOtherFields; -use anvil::{spawn, Hardfork, NodeConfig}; +use anvil::{spawn, EthereumHardfork, NodeConfig}; use anvil_core::eth::transaction::optimism::DepositTransaction; #[tokio::test(flavor = "multi_thread")] @@ -44,8 +44,10 @@ async fn test_deposits_not_supported_if_optimism_disabled() { #[tokio::test(flavor = "multi_thread")] async fn test_send_value_deposit_transaction() { // enable the Optimism flag - let (api, handle) = - spawn(NodeConfig::test().with_optimism(true).with_hardfork(Some(Hardfork::Paris))).await; + let (api, handle) = spawn( + NodeConfig::test().with_optimism(true).with_hardfork(Some(EthereumHardfork::Paris.into())), + ) + .await; let accounts: Vec<_> = handle.dev_wallets().collect(); let signer: EthereumWallet = accounts[0].clone().into(); @@ -92,8 +94,10 @@ async fn test_send_value_deposit_transaction() { #[tokio::test(flavor = "multi_thread")] async fn test_send_value_raw_deposit_transaction() { // enable the Optimism flag - let (api, handle) = - spawn(NodeConfig::test().with_optimism(true).with_hardfork(Some(Hardfork::Paris))).await; + let (api, handle) = spawn( + NodeConfig::test().with_optimism(true).with_hardfork(Some(EthereumHardfork::Paris.into())), + ) + .await; let accounts: Vec<_> = handle.dev_wallets().collect(); let signer: EthereumWallet = accounts[0].clone().into(); @@ -149,8 +153,10 @@ async fn test_send_value_raw_deposit_transaction() { #[tokio::test(flavor = "multi_thread")] async fn test_deposit_transaction_hash_matches_sepolia() { // enable the Optimism flag - let (_api, handle) = - spawn(NodeConfig::test().with_optimism(true).with_hardfork(Some(Hardfork::Paris))).await; + let (_api, handle) = spawn( + NodeConfig::test().with_optimism(true).with_hardfork(Some(EthereumHardfork::Paris.into())), + ) + .await; let accounts: Vec<_> = handle.dev_wallets().collect(); let signer: EthereumWallet = accounts[0].clone().into(); @@ -181,8 +187,10 @@ async fn test_deposit_transaction_hash_matches_sepolia() { #[tokio::test(flavor = "multi_thread")] async fn test_deposit_tx_checks_sufficient_funds_after_applying_deposited_value() { // enable the Optimism flag - let (_api, handle) = - spawn(NodeConfig::test().with_optimism(true).with_hardfork(Some(Hardfork::Paris))).await; + let (_api, handle) = spawn( + NodeConfig::test().with_optimism(true).with_hardfork(Some(EthereumHardfork::Paris.into())), + ) + .await; let provider = http_provider(&handle.http_endpoint()); diff --git a/crates/anvil/tests/it/otterscan.rs b/crates/anvil/tests/it/otterscan.rs index fc153f963..2f2c0e3c1 100644 --- a/crates/anvil/tests/it/otterscan.rs +++ b/crates/anvil/tests/it/otterscan.rs @@ -9,7 +9,7 @@ use alloy_rpc_types::{ }; use alloy_serde::WithOtherFields; use alloy_sol_types::{sol, SolCall, SolError, SolValue}; -use anvil::{spawn, Hardfork, NodeConfig}; +use anvil::{spawn, EthereumHardfork, NodeConfig}; use std::collections::VecDeque; #[tokio::test(flavor = "multi_thread")] @@ -118,15 +118,15 @@ async fn ots_get_internal_operations_contract_create2() { #[tokio::test(flavor = "multi_thread")] async fn ots_get_internal_operations_contract_selfdestruct_london() { - ots_get_internal_operations_contract_selfdestruct(Hardfork::London).await; + ots_get_internal_operations_contract_selfdestruct(EthereumHardfork::London).await; } #[tokio::test(flavor = "multi_thread")] async fn ots_get_internal_operations_contract_selfdestruct_cancun() { - ots_get_internal_operations_contract_selfdestruct(Hardfork::Cancun).await; + ots_get_internal_operations_contract_selfdestruct(EthereumHardfork::Cancun).await; } -async fn ots_get_internal_operations_contract_selfdestruct(hardfork: Hardfork) { +async fn ots_get_internal_operations_contract_selfdestruct(hardfork: EthereumHardfork) { sol!( #[sol(rpc, bytecode = "608080604052607f908160108239f3fe6004361015600c57600080fd5b6000803560e01c6375fc8e3c14602157600080fd5b346046578060031936011260465773dcdd539da22bffaa499dbea4d37d086dde196e75ff5b80fdfea264697066735822122080a9ad005cc408b2d4e30ca11216d8e310700fbcdf58a629d6edbb91531f9c6164736f6c63430008190033")] contract Contract { @@ -137,7 +137,7 @@ async fn ots_get_internal_operations_contract_selfdestruct(hardfork: Hardfork) { } ); - let (api, handle) = spawn(NodeConfig::test().with_hardfork(Some(hardfork))).await; + let (api, handle) = spawn(NodeConfig::test().with_hardfork(Some(hardfork.into()))).await; let provider = handle.http_provider(); let sender = handle.dev_accounts().next().unwrap(); @@ -150,7 +150,7 @@ async fn ots_get_internal_operations_contract_selfdestruct(hardfork: Hardfork) { let receipt = contract.goodbye().send().await.unwrap().get_receipt().await.unwrap(); // TODO: This is currently not supported by revm-inspectors - let (expected_to, expected_value) = if hardfork < Hardfork::Cancun { + let (expected_to, expected_value) = if hardfork < EthereumHardfork::Cancun { (address!("DcDD539DA22bfFAa499dBEa4d37d086Dde196E75"), value) } else { (Address::ZERO, U256::ZERO) diff --git a/crates/anvil/tests/it/traces.rs b/crates/anvil/tests/it/traces.rs index 9e09f8fcf..782e68d72 100644 --- a/crates/anvil/tests/it/traces.rs +++ b/crates/anvil/tests/it/traces.rs @@ -23,7 +23,7 @@ use alloy_rpc_types::{ }; use alloy_serde::WithOtherFields; use alloy_sol_types::sol; -use anvil::{spawn, Hardfork, NodeConfig}; +use anvil::{spawn, EthereumHardfork, NodeConfig}; #[tokio::test(flavor = "multi_thread")] async fn test_get_transfer_parity_traces() { @@ -75,7 +75,8 @@ sol!( #[tokio::test(flavor = "multi_thread")] async fn test_parity_suicide_trace() { - let (_api, handle) = spawn(NodeConfig::test().with_hardfork(Some(Hardfork::Shanghai))).await; + let (_api, handle) = + spawn(NodeConfig::test().with_hardfork(Some(EthereumHardfork::Shanghai.into()))).await; let provider = handle.ws_provider(); let wallets = handle.dev_wallets().collect::>(); let owner = wallets[0].address(); diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index 0737209c1..330eb7a39 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -10,7 +10,7 @@ use alloy_rpc_types::{ AccessList, AccessListItem, BlockId, BlockNumberOrTag, BlockTransactions, TransactionRequest, }; use alloy_serde::WithOtherFields; -use anvil::{spawn, Hardfork, NodeConfig}; +use anvil::{spawn, EthereumHardfork, NodeConfig}; use eyre::Ok; use futures::{future::join_all, FutureExt, StreamExt}; use std::{collections::HashSet, str::FromStr, time::Duration}; @@ -1176,7 +1176,8 @@ async fn can_call_with_high_gas_limit() { #[tokio::test(flavor = "multi_thread")] async fn test_reject_eip1559_pre_london() { - let (api, handle) = spawn(NodeConfig::test().with_hardfork(Some(Hardfork::Berlin))).await; + let (api, handle) = + spawn(NodeConfig::test().with_hardfork(Some(EthereumHardfork::Berlin.into()))).await; let provider = handle.http_provider(); let gas_limit = api.gas_limit().to::(); diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 8ece9ef65..bb52936a9 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -2,7 +2,7 @@ use alloy_chains::NamedChain; use alloy_primitives::{b256, B256}; -use anvil::{Hardfork, NodeConfig}; +use anvil::{EthereumHardfork, NodeConfig}; use foundry_test_utils::{ casttest, file, rpc::{next_http_rpc_endpoint, next_rpc_endpoint, next_ws_rpc_endpoint}, @@ -1273,7 +1273,8 @@ casttest!(block_number_hash, |_prj, cmd| { casttest!(send_eip7702, async |_prj, cmd| { let (_api, handle) = - anvil::spawn(NodeConfig::test().with_hardfork(Some(Hardfork::PragueEOF))).await; + anvil::spawn(NodeConfig::test().with_hardfork(Some(EthereumHardfork::PragueEOF.into()))) + .await; let endpoint = handle.http_endpoint(); cmd.args([ From 5881e0da10230e83c4458b52d49392c1c33c506c Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 2 Sep 2024 12:25:55 +0300 Subject: [PATCH 127/184] Fix clippy - allow elided_named_lifetimes (#8787) --- crates/common/src/ens.rs | 2 +- crates/config/src/lib.rs | 1 + crates/doc/src/preprocessor/infer_hyperlinks.rs | 2 ++ crates/forge/src/multi_runner.rs | 2 ++ 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/common/src/ens.rs b/crates/common/src/ens.rs index f84ea84ce..e120ad4c2 100644 --- a/crates/common/src/ens.rs +++ b/crates/common/src/ens.rs @@ -1,6 +1,6 @@ //! ENS Name resolving utilities. -#![allow(missing_docs)] +#![allow(missing_docs, elided_named_lifetimes)] use self::EnsResolver::EnsResolverInstance; use alloy_primitives::{address, Address, Keccak256, B256}; diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index d0fcc5a14..ef9c5f6b1 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -2,6 +2,7 @@ //! //! Foundry configuration. +#![allow(elided_named_lifetimes)] #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] diff --git a/crates/doc/src/preprocessor/infer_hyperlinks.rs b/crates/doc/src/preprocessor/infer_hyperlinks.rs index 613976ec8..5108ee374 100644 --- a/crates/doc/src/preprocessor/infer_hyperlinks.rs +++ b/crates/doc/src/preprocessor/infer_hyperlinks.rs @@ -1,3 +1,5 @@ +#![allow(elided_named_lifetimes)] + use super::{Preprocessor, PreprocessorId}; use crate::{Comments, Document, ParseItem, ParseSource}; use forge_fmt::solang_ext::SafeUnwrap; diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 4df00f794..cee986553 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -1,5 +1,7 @@ //! Forge test runner for multiple contracts. +#![allow(elided_named_lifetimes)] + use crate::{ progress::TestsProgress, result::SuiteResult, runner::LIBRARY_DEPLOYER, ContractRunner, TestFilter, TestOptions, From c835d7ea9e34d3cb05bb2ba6e73a57fe51ab5ef8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 12:00:10 +0200 Subject: [PATCH 128/184] chore(deps): weekly `cargo update` (#8782) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/alloy-rs/alloy` Updating git repository `https://github.com/bluealloy/revm` Locking 24 packages to latest compatible versions Updating aws-credential-types v1.2.0 -> v1.2.1 Updating aws-runtime v1.4.0 -> v1.4.2 Updating aws-sdk-kms v1.40.0 -> v1.41.0 Updating aws-sdk-sso v1.39.0 -> v1.40.0 Updating aws-sdk-ssooidc v1.40.0 -> v1.41.0 Updating aws-sdk-sts v1.39.0 -> v1.40.0 Updating aws-smithy-http v0.60.9 -> v0.60.10 Updating aws-smithy-runtime v1.6.3 -> v1.7.1 Updating aws-smithy-types v1.2.2 -> v1.2.4 Updating derive_builder v0.20.0 -> v0.20.1 Updating derive_builder_core v0.20.0 -> v0.20.1 Updating derive_builder_macro v0.20.0 -> v0.20.1 Updating filetime v0.2.24 -> v0.2.25 Updating indexmap v2.4.0 -> v2.5.0 Updating prost v0.13.1 -> v0.13.2 Updating prost-derive v0.13.1 -> v0.13.2 Updating prost-types v0.13.1 -> v0.13.2 Updating rustc_version v0.4.0 -> v0.4.1 Updating rustix v0.38.34 -> v0.38.35 Updating rustls-native-certs v0.7.2 -> v0.7.3 (latest: v0.8.0) Updating soldeer v0.3.1 -> v0.3.2 Updating syn v2.0.76 -> v2.0.77 Updating tokio v1.39.3 -> v1.40.0 Updating webpki-roots v0.26.3 -> v0.26.5 note: pass `--verbose` to see 146 unchanged dependencies behind latest * pin soldeer --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz --- Cargo.lock | 241 +++++++++++++++++++++++++++-------------------------- Cargo.toml | 2 +- 2 files changed, 122 insertions(+), 121 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 99ee3dae2..c353292ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -322,7 +322,7 @@ checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -557,7 +557,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -570,11 +570,11 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.4.0", + "indexmap 2.5.0", "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", "syn-solidity", "tiny-keccak", ] @@ -592,7 +592,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.76", + "syn 2.0.77", "syn-solidity", ] @@ -965,7 +965,7 @@ dependencies = [ "num-bigint", "num-traits", "paste", - "rustc_version 0.4.0", + "rustc_version 0.4.1", "zeroize", ] @@ -1100,7 +1100,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -1122,7 +1122,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -1133,7 +1133,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -1144,7 +1144,7 @@ checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" dependencies = [ "futures", "pharos", - "rustc_version 0.4.0", + "rustc_version 0.4.1", ] [[package]] @@ -1186,7 +1186,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -1227,9 +1227,9 @@ dependencies = [ [[package]] name = "aws-credential-types" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16838e6c9e12125face1c1eff1343c75e3ff540de98ff7ebd61874a89bcfeb9" +checksum = "60e8f6b615cb5fc60a98132268508ad104310f0cfb25a1c22eee76efdf9154da" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -1239,14 +1239,15 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f42c2d4218de4dcd890a109461e2f799a1a2ba3bcd2cde9af88360f5df9266c6" +checksum = "2424565416eef55906f9f8cece2072b6b6a76075e3ff81483ebe938a89a4c05f" dependencies = [ "aws-credential-types", "aws-sigv4", "aws-smithy-async", "aws-smithy-http", + "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", "aws-types", @@ -1263,9 +1264,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.40.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ebbbc319551583b9233a74b359ede7349102e779fc12371d2478e80b50d218" +checksum = "178910fefe72743b62b9c4670c14a038ebfdb265ff7feccf43827af6a8899e14" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1285,9 +1286,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.39.0" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11822090cf501c316c6f75711d77b96fba30658e3867a7762e5e2f5d32d31e81" +checksum = "e5879bec6e74b648ce12f6085e7245417bc5f6d672781028384d2e494be3eb6d" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1307,9 +1308,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.40.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78a2a06ff89176123945d1bbe865603c4d7101bea216a550bb4d2e4e9ba74d74" +checksum = "4ef4cd9362f638c22a3b959fd8df292e7e47fdf170270f86246b97109b5f2f7d" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1329,9 +1330,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.39.0" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20a91795850826a6f456f4a48eff1dfa59a0e69bdbf5b8c50518fd372106574" +checksum = "0b1e2735d2ab28b35ecbb5496c9d41857f52a0d6a0075bbf6a8af306045ea6f6" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1386,9 +1387,9 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.60.9" +version = "0.60.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9cd0ae3d97daa0a2bf377a4d8e8e1362cae590c4a1aad0d40058ebca18eb91e" +checksum = "01dbcb6e2588fd64cfb6d7529661b06466419e4c54ed1c62d6510d2d0350a728" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", @@ -1425,9 +1426,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.6.3" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0abbf454960d0db2ad12684a1640120e7557294b0ff8e2f11236290a1b293225" +checksum = "d1ce695746394772e7000b39fe073095db6d45a862d0767dd5ad0ac0d7f8eb87" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -1469,9 +1470,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.2.2" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cee7cadb433c781d3299b916fbf620fea813bf38f49db282fb6858141a05cc8" +checksum = "273dcdfd762fae3e1650b8024624e7cd50e484e37abdab73a7a706188ad34543" dependencies = [ "base64-simd", "bytes", @@ -1509,7 +1510,7 @@ dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", "aws-smithy-types", - "rustc_version 0.4.0", + "rustc_version 0.4.1", "tracing", ] @@ -2098,7 +2099,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -2546,7 +2547,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -2557,7 +2558,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -2630,38 +2631,38 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] name = "derive_builder" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0350b5cb0331628a5916d6c5c0b72e97393b8b6b03b47a9284f4e7f5a405ffd7" +checksum = "cd33f37ee6a119146a1781d3356a7c26028f83d779b2e04ecd45fdc75c76877b" dependencies = [ "derive_builder_macro", ] [[package]] name = "derive_builder_core" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d48cda787f839151732d396ac69e3473923d54312c070ee21e9effcaa8ca0b1d" +checksum = "7431fa049613920234f22c47fdc33e6cf3ee83067091ea4277a3f8c4587aae38" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] name = "derive_builder_macro" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" +checksum = "4abae7035bf79b9877b779505d8cf3749285b80c43941eda66604841889451dc" dependencies = [ "derive_builder_core", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -2673,8 +2674,8 @@ dependencies = [ "convert_case 0.4.0", "proc-macro2", "quote", - "rustc_version 0.4.0", - "syn 2.0.76", + "rustc_version 0.4.1", + "syn 2.0.77", ] [[package]] @@ -2695,7 +2696,7 @@ dependencies = [ "convert_case 0.6.0", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", "unicode-xid", ] @@ -2803,7 +2804,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -2930,7 +2931,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -3076,7 +3077,7 @@ dependencies = [ "regex", "serde", "serde_json", - "syn 2.0.76", + "syn 2.0.77", "toml 0.8.19", "walkdir", ] @@ -3104,7 +3105,7 @@ dependencies = [ "serde", "serde_json", "strum", - "syn 2.0.76", + "syn 2.0.77", "tempfile", "thiserror", "tiny-keccak", @@ -3224,9 +3225,9 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.24" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ "cfg-if", "libc", @@ -3469,7 +3470,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -3958,7 +3959,7 @@ dependencies = [ "foundry-evm-core", "foundry-evm-coverage", "foundry-evm-traces", - "indexmap 2.4.0", + "indexmap 2.5.0", "itertools 0.13.0", "parking_lot", "proptest", @@ -4038,7 +4039,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -4195,7 +4196,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -4570,7 +4571,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.4.0", + "indexmap 2.5.0", "slab", "tokio", "tokio-util", @@ -4589,7 +4590,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.1.0", - "indexmap 2.4.0", + "indexmap 2.5.0", "slab", "tokio", "tokio-util", @@ -4711,7 +4712,7 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -4876,7 +4877,7 @@ dependencies = [ "hyper 1.4.1", "hyper-util", "rustls 0.23.12", - "rustls-native-certs 0.7.2", + "rustls-native-certs 0.7.3", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -5063,9 +5064,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown 0.14.5", @@ -5126,7 +5127,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c" dependencies = [ "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -5589,7 +5590,7 @@ checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -5680,7 +5681,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -5930,7 +5931,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -6042,7 +6043,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -6228,7 +6229,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -6287,7 +6288,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -6308,7 +6309,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.4.0", + "indexmap 2.5.0", ] [[package]] @@ -6318,7 +6319,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" dependencies = [ "futures", - "rustc_version 0.4.0", + "rustc_version 0.4.1", ] [[package]] @@ -6371,7 +6372,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -6429,7 +6430,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -6548,7 +6549,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -6624,7 +6625,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", "version_check", "yansi", ] @@ -6636,7 +6637,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38ee68ae331824036479c84060534b18254c864fa73366c58d86db3b7b811619" dependencies = [ "futures", - "indexmap 2.4.0", + "indexmap 2.5.0", "nix 0.28.0", "tokio", "tracing", @@ -6693,9 +6694,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13db3d3fde688c61e2446b4d843bc27a7e8af269a69440c0308021dc92333cc" +checksum = "3b2ecbe40f08db5c006b5764a2645f7f3f141ce756412ac9e1dd6087e6d32995" dependencies = [ "bytes", "prost-derive", @@ -6703,22 +6704,22 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18bec9b0adc4eba778b33684b7ba3e7137789434769ee3ce3930463ef904cfca" +checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac" dependencies = [ "anyhow", "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] name = "prost-types" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee5168b05f49d4b0ca581206eb14a7b22fafd963efe729ac48eb03266e25cc2" +checksum = "60caa6738c7369b940c3d49246a8d1749323674c65cb13010134f5c9bad5b519" dependencies = [ "prost", ] @@ -7035,7 +7036,7 @@ dependencies = [ "pin-project-lite", "quinn", "rustls 0.23.12", - "rustls-native-certs 0.7.2", + "rustls-native-certs 0.7.3", "rustls-pemfile 2.1.3", "rustls-pki-types", "serde", @@ -7285,18 +7286,18 @@ dependencies = [ [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver 1.0.23", ] [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" dependencies = [ "bitflags 2.6.0", "errno", @@ -7346,9 +7347,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04182dffc9091a404e0fc069ea5cd60e5b866c3adf881eff99a32d048242dffa" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" dependencies = [ "openssl-probe", "rustls-pemfile 2.1.3", @@ -7540,7 +7541,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -7702,7 +7703,7 @@ checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -7713,7 +7714,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -7722,7 +7723,7 @@ version = "1.0.127" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" dependencies = [ - "indexmap 2.4.0", + "indexmap 2.5.0", "itoa", "memchr", "ryu", @@ -7757,7 +7758,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -7787,7 +7788,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.4.0", + "indexmap 2.5.0", "itoa", "ryu", "serde", @@ -7816,7 +7817,7 @@ checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -8138,7 +8139,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -8206,9 +8207,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.76" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", @@ -8224,7 +8225,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -8329,7 +8330,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -8440,9 +8441,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.3" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", @@ -8464,7 +8465,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -8578,7 +8579,7 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ - "indexmap 2.4.0", + "indexmap 2.5.0", "serde", "serde_spanned", "toml_datetime", @@ -8600,7 +8601,7 @@ version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ - "indexmap 2.4.0", + "indexmap 2.5.0", "serde", "serde_spanned", "toml_datetime", @@ -8628,7 +8629,7 @@ dependencies = [ "percent-encoding", "pin-project 1.1.5", "prost", - "rustls-native-certs 0.7.2", + "rustls-native-certs 0.7.3", "rustls-pemfile 2.1.3", "socket2", "tokio", @@ -8735,7 +8736,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -9135,7 +9136,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", "wasm-bindgen-shared", ] @@ -9169,7 +9170,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -9268,9 +9269,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.3" +version = "0.26.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" +checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a" dependencies = [ "rustls-pki-types", ] @@ -9386,7 +9387,7 @@ checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -9397,7 +9398,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -9408,7 +9409,7 @@ checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -9419,7 +9420,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -9635,7 +9636,7 @@ dependencies = [ "js-sys", "log", "pharos", - "rustc_version 0.4.0", + "rustc_version 0.4.1", "send_wrapper", "thiserror", "wasm-bindgen", @@ -9685,7 +9686,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -9705,7 +9706,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -9738,7 +9739,7 @@ dependencies = [ "crossbeam-utils", "displaydoc", "flate2", - "indexmap 2.4.0", + "indexmap 2.5.0", "memchr", "thiserror", "zopfli", diff --git a/Cargo.toml b/Cargo.toml index 50c33c653..df99f6b46 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -259,7 +259,7 @@ reqwest = { version = "0.12", default-features = false } tower = "0.4" tower-http = "0.5" # soldeer -soldeer = "0.3.1" +soldeer = "=0.3.1" proptest = "1" comfy-table = "7" From 386ca061b64f331a938f92a25b9557914b9ecdc8 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Tue, 3 Sep 2024 03:17:07 +0400 Subject: [PATCH 129/184] fix(eip7702): small fixes (#8791) * fix(eip7702): doc and small fixes * clippy --- crates/anvil/src/eth/error.rs | 15 +++++++++- crates/cast/bin/cmd/access_list.rs | 10 ++----- crates/cast/bin/cmd/call.rs | 10 ++----- crates/cast/bin/cmd/estimate.rs | 11 ++----- crates/cast/bin/cmd/mktx.rs | 8 ++---- crates/cast/bin/cmd/send.rs | 4 +-- crates/cast/bin/tx.rs | 46 ++++++++++++++---------------- 7 files changed, 48 insertions(+), 56 deletions(-) diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index b8b475deb..7349cff78 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -231,6 +231,12 @@ pub enum InvalidTransactionError { /// Thrown when there are no `blob_hashes` in the transaction. #[error("There should be at least one blob in a Blob transaction.")] EmptyBlobs, + /// Thrown when an access list is used before the berlin hard fork. + #[error("EIP-7702 authorization lists are not supported before the Prague hardfork")] + AuthorizationListNotSupported, + /// Forwards error from the revm + #[error(transparent)] + Revm(revm::primitives::InvalidTransaction), } impl From for InvalidTransactionError { @@ -263,7 +269,14 @@ impl From for InvalidTransactionError { InvalidTransaction::BlobVersionNotSupported => Self::BlobVersionNotSupported, InvalidTransaction::EmptyBlobs => Self::EmptyBlobs, InvalidTransaction::TooManyBlobs { max, have } => Self::TooManyBlobs(max, have), - _ => todo!(), + InvalidTransaction::AuthorizationListNotSupported => { + Self::AuthorizationListNotSupported + } + InvalidTransaction::AuthorizationListInvalidFields | + InvalidTransaction::InvalidAuthorizationList | + InvalidTransaction::OptimismError(_) | + InvalidTransaction::EofCrateShouldHaveToAddress | + InvalidTransaction::EmptyAuthorizationList => Self::Revm(err), } } } diff --git a/crates/cast/bin/cmd/access_list.rs b/crates/cast/bin/cmd/access_list.rs index fcaf848ac..553d9c9ad 100644 --- a/crates/cast/bin/cmd/access_list.rs +++ b/crates/cast/bin/cmd/access_list.rs @@ -1,5 +1,4 @@ use crate::tx::{CastTxBuilder, SenderKind}; -use alloy_primitives::TxKind; use alloy_rpc_types::BlockId; use cast::Cast; use clap::Parser; @@ -55,15 +54,10 @@ impl AccessListArgs { let provider = utils::get_provider(&config)?; let sender = SenderKind::from_wallet_opts(eth.wallet).await?; - let tx_kind = if let Some(to) = to { - TxKind::Call(to.resolve(&provider).await?) - } else { - TxKind::Create - }; - let (tx, _) = CastTxBuilder::new(&provider, tx, &config) .await? - .with_tx_kind(tx_kind) + .with_to(to) + .await? .with_code_sig_and_args(None, sig, args) .await? .build_raw(sender) diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index 9a553e385..5b1e1ff65 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -143,12 +143,6 @@ impl CallArgs { let sender = SenderKind::from_wallet_opts(eth.wallet).await?; let from = sender.address(); - let tx_kind = if let Some(to) = to { - TxKind::Call(to.resolve(&provider).await?) - } else { - TxKind::Create - }; - let code = if let Some(CallSubcommands::Create { code, sig: create_sig, @@ -168,7 +162,8 @@ impl CallArgs { let (tx, func) = CastTxBuilder::new(&provider, tx, &config) .await? - .with_tx_kind(tx_kind) + .with_to(to) + .await? .with_code_sig_and_args(code, sig, args) .await? .build_raw(sender) @@ -192,6 +187,7 @@ impl CallArgs { let value = tx.value.unwrap_or_default(); let input = tx.inner.input.into_input().unwrap_or_default(); + let tx_kind = tx.inner.to.expect("set by builder"); let trace = match tx_kind { TxKind::Create => { diff --git a/crates/cast/bin/cmd/estimate.rs b/crates/cast/bin/cmd/estimate.rs index 84812c8cc..315ba91dc 100644 --- a/crates/cast/bin/cmd/estimate.rs +++ b/crates/cast/bin/cmd/estimate.rs @@ -1,5 +1,5 @@ use crate::tx::{CastTxBuilder, SenderKind}; -use alloy_primitives::{TxKind, U256}; +use alloy_primitives::U256; use alloy_provider::Provider; use alloy_rpc_types::BlockId; use clap::Parser; @@ -73,12 +73,6 @@ impl EstimateArgs { let provider = utils::get_provider(&config)?; let sender = SenderKind::from_wallet_opts(eth.wallet).await?; - let tx_kind = if let Some(to) = to { - TxKind::Call(to.resolve(&provider).await?) - } else { - TxKind::Create - }; - let code = if let Some(EstimateSubcommands::Create { code, sig: create_sig, @@ -98,7 +92,8 @@ impl EstimateArgs { let (tx, _) = CastTxBuilder::new(&provider, tx, &config) .await? - .with_tx_kind(tx_kind) + .with_to(to) + .await? .with_code_sig_and_args(code, sig, args) .await? .build_raw(sender) diff --git a/crates/cast/bin/cmd/mktx.rs b/crates/cast/bin/cmd/mktx.rs index 4c7a55cb1..8a45b7363 100644 --- a/crates/cast/bin/cmd/mktx.rs +++ b/crates/cast/bin/cmd/mktx.rs @@ -6,7 +6,7 @@ use clap::Parser; use eyre::Result; use foundry_cli::{ opts::{EthereumOpts, TransactionOpts}, - utils::{self, get_provider}, + utils::get_provider, }; use foundry_common::ens::NameOrAddress; use foundry_config::Config; @@ -83,9 +83,6 @@ impl MakeTxArgs { }; let config = Config::from(ð); - let provider = utils::get_provider(&config)?; - - let tx_kind = tx::resolve_tx_kind(&provider, &code, &to).await?; // Retrieve the signer, and bail if it can't be constructed. let signer = eth.wallet.signer().await?; @@ -97,7 +94,8 @@ impl MakeTxArgs { let (tx, _) = CastTxBuilder::new(provider, tx, &config) .await? - .with_tx_kind(tx_kind) + .with_to(to) + .await? .with_code_sig_and_args(code, sig, args) .await? .with_blob_data(blob_data)? diff --git a/crates/cast/bin/cmd/send.rs b/crates/cast/bin/cmd/send.rs index 573bb2bd1..cf3582fe0 100644 --- a/crates/cast/bin/cmd/send.rs +++ b/crates/cast/bin/cmd/send.rs @@ -122,11 +122,11 @@ impl SendTxArgs { let config = Config::from(ð); let provider = utils::get_provider(&config)?; - let tx_kind = tx::resolve_tx_kind(&provider, &code, &to).await?; let builder = CastTxBuilder::new(&provider, tx, &config) .await? - .with_tx_kind(tx_kind) + .with_to(to) + .await? .with_code_sig_and_args(code, sig, args) .await? .with_blob_data(blob_data)?; diff --git a/crates/cast/bin/tx.rs b/crates/cast/bin/tx.rs index f6a9cbd53..419ad5bb6 100644 --- a/crates/cast/bin/tx.rs +++ b/crates/cast/bin/tx.rs @@ -103,29 +103,14 @@ corresponds to the sender, or let foundry automatically detect it by not specify Ok(()) } -/// Ensures the transaction is either a contract deployment or a recipient address is specified -pub async fn resolve_tx_kind, T: Transport + Clone>( - provider: &P, - code: &Option, - to: &Option, -) -> Result { - if code.is_some() { - Ok(TxKind::Create) - } else if let Some(to) = to { - Ok(TxKind::Call(to.resolve(provider).await?)) - } else { - eyre::bail!("Must specify a recipient address or contract code to deploy"); - } -} - /// Initial state. #[derive(Debug)] pub struct InitState; /// State with known [TxKind]. #[derive(Debug)] -pub struct TxKindState { - kind: TxKind, +pub struct ToState { + to: Option
, } /// State with known input for the transaction. @@ -211,8 +196,9 @@ where } /// Sets [TxKind] for this builder and changes state to [TxKindState]. - pub fn with_tx_kind(self, kind: TxKind) -> CastTxBuilder { - CastTxBuilder { + pub async fn with_to(self, to: Option) -> Result> { + let to = if let Some(to) = to { Some(to.resolve(&self.provider).await?) } else { None }; + Ok(CastTxBuilder { provider: self.provider, tx: self.tx, legacy: self.legacy, @@ -220,13 +206,13 @@ where chain: self.chain, etherscan_api_key: self.etherscan_api_key, auth: self.auth, - state: TxKindState { kind }, + state: ToState { to }, _t: self._t, - } + }) } } -impl CastTxBuilder +impl CastTxBuilder where P: Provider, T: Transport + Clone, @@ -244,7 +230,7 @@ where parse_function_args( &sig, args, - self.state.kind.to().cloned(), + self.state.to, self.chain, &self.provider, self.etherscan_api_key.as_deref(), @@ -254,7 +240,7 @@ where (Vec::new(), None) }; - let input = if let Some(code) = code { + let input = if let Some(code) = &code { let mut code = hex::decode(code)?; code.append(&mut args); code @@ -262,6 +248,16 @@ where args }; + if self.state.to.is_none() && code.is_none() { + let has_value = self.tx.value.map_or(false, |v| !v.is_zero()); + let has_auth = self.auth.is_some(); + // We only allow user to omit the recipient address if transaction is an EIP-7702 tx + // without a value. + if !has_auth || has_value { + eyre::bail!("Must specify a recipient address or contract code to deploy"); + } + } + Ok(CastTxBuilder { provider: self.provider, tx: self.tx, @@ -270,7 +266,7 @@ where chain: self.chain, etherscan_api_key: self.etherscan_api_key, auth: self.auth, - state: InputState { kind: self.state.kind, input, func }, + state: InputState { kind: self.state.to.into(), input, func }, _t: self._t, }) } From d90e997d91532d902b0d6f786ff59777e69efa3a Mon Sep 17 00:00:00 2001 From: Felipe Buiras Date: Mon, 2 Sep 2024 20:31:19 -0300 Subject: [PATCH 130/184] fix(cheatcodes): fail when trying to parse malformatted strings as addresses (#8779) * fix(cheatcodes): fail when trying to parse malformatted strings as addresses * test: check non-address and badly checksummed addresses * fix: add check for address parsing when input is one-off * fix: do not error out on non-checksummed addresses * fix: remove custom address validation on typed json parsing * fix: change error message displayed on address parsing failure Co-authored-by: Arsenii Kulikov --------- Co-authored-by: Arsenii Kulikov --- crates/cheatcodes/src/json.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/cheatcodes/src/json.rs b/crates/cheatcodes/src/json.rs index 48c14e2a3..dad879e83 100644 --- a/crates/cheatcodes/src/json.rs +++ b/crates/cheatcodes/src/json.rs @@ -569,6 +569,9 @@ pub(super) fn json_value_to_token(value: &Value) -> Result { Value::String(string) => { if let Some(mut val) = string.strip_prefix("0x") { let s; + if val.len() == 39 { + return Err(format!("Cannot parse \"{val}\" as an address. If you want to specify address, prepend zero to the value.").into()) + } if val.len() % 2 != 0 { s = format!("0{val}"); val = &s[..]; From 91c0782acc39ee00ce0f841b242201b753aac192 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 3 Sep 2024 15:54:06 +0300 Subject: [PATCH 131/184] fix(fuzz): apply inline max-test-rejects config (#8793) --- crates/forge/src/lib.rs | 6 ++++-- crates/forge/tests/it/fuzz.rs | 28 +++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/crates/forge/src/lib.rs b/crates/forge/src/lib.rs index 6ad91ab66..6c6552d05 100644 --- a/crates/forge/src/lib.rs +++ b/crates/forge/src/lib.rs @@ -115,6 +115,7 @@ impl TestOptions { .unwrap(); self.fuzzer_with_cases( fuzz_config.runs, + fuzz_config.max_test_rejects, Some(Box::new(FileFailurePersistence::Direct(failure_persist_path.leak()))), ) } @@ -128,7 +129,7 @@ impl TestOptions { /// - `test_fn` is the name of the test function declared inside the test contract. pub fn invariant_runner(&self, contract_id: &str, test_fn: &str) -> TestRunner { let invariant = self.invariant_config(contract_id, test_fn); - self.fuzzer_with_cases(invariant.runs, None) + self.fuzzer_with_cases(invariant.runs, invariant.max_assume_rejects, None) } /// Returns a "fuzz" configuration setup. Parameters are used to select tight scoped fuzz @@ -156,12 +157,13 @@ impl TestOptions { pub fn fuzzer_with_cases( &self, cases: u32, + max_global_rejects: u32, file_failure_persistence: Option>, ) -> TestRunner { let config = proptest::test_runner::Config { failure_persistence: file_failure_persistence, cases, - max_global_rejects: self.fuzz.max_test_rejects, + max_global_rejects, // Disable proptest shrink: for fuzz tests we provide single counterexample, // for invariant tests we shrink outside proptest. max_shrink_iters: 0, diff --git a/crates/forge/tests/it/fuzz.rs b/crates/forge/tests/it/fuzz.rs index f1c5edaaa..b0bd57a05 100644 --- a/crates/forge/tests/it/fuzz.rs +++ b/crates/forge/tests/it/fuzz.rs @@ -7,7 +7,7 @@ use forge::{ fuzz::CounterExample, result::{SuiteResult, TestStatus}, }; -use foundry_test_utils::Filter; +use foundry_test_utils::{forgetest_init, str, Filter}; use std::collections::BTreeMap; #[tokio::test(flavor = "multi_thread")] @@ -176,3 +176,29 @@ async fn test_scrape_bytecode() { } } } + +// tests that inline max-test-rejects config is properly applied +forgetest_init!(test_inline_max_test_rejects, |prj, cmd| { + prj.wipe_contracts(); + + prj.add_test( + "Contract.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract InlineMaxRejectsTest is Test { + /// forge-config: default.fuzz.max-test-rejects = 1 + function test_fuzz_bound(uint256 a) public { + vm.assume(a == 0); + } +} + "#, + ) + .unwrap(); + + cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" +... +[FAIL. Reason: The `vm.assume` cheatcode rejected too many inputs (1 allowed)] test_fuzz_bound(uint256) (runs: 0, [AVG_GAS]) +... +"#]]); +}); From cb109b1699f82d009574d13aa59f1585a3fbfdb2 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 3 Sep 2024 22:02:20 +0300 Subject: [PATCH 132/184] feat(cheatcodes): add vm.assumeNoRevert for fuzz tests (#8780) --- crates/cheatcodes/assets/cheatcodes.json | 20 ++++++ crates/cheatcodes/spec/src/vm.rs | 4 ++ crates/cheatcodes/src/inspector.rs | 28 +++++++-- crates/cheatcodes/src/test.rs | 16 +---- crates/cheatcodes/src/test/assume.rs | 29 +++++++++ crates/forge/tests/cli/test_cmd.rs | 79 ++++++++++++++++++++++++ testdata/cheats/Vm.sol | 1 + 7 files changed, 160 insertions(+), 17 deletions(-) create mode 100644 crates/cheatcodes/src/test/assume.rs diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 574d310ca..4517f075e 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -2971,6 +2971,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "assumeNoRevert", + "description": "Discard this run's fuzz inputs and generate new ones if next call reverted.", + "declaration": "function assumeNoRevert() external pure;", + "visibility": "external", + "mutability": "pure", + "signature": "assumeNoRevert()", + "selector": "0x285b366a", + "selectorBytes": [ + 40, + 91, + 54, + 106 + ] + }, + "group": "testing", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "blobBaseFee", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 575ccfa84..980bab066 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -678,6 +678,10 @@ interface Vm { #[cheatcode(group = Testing, safety = Safe)] function assume(bool condition) external pure; + /// Discard this run's fuzz inputs and generate new ones if next call reverted. + #[cheatcode(group = Testing, safety = Safe)] + function assumeNoRevert() external pure; + /// Writes a breakpoint to jump to in the debugger. #[cheatcode(group = Testing, safety = Safe)] function breakpoint(string calldata char) external; diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 1ff2a6e99..f5238d810 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -9,9 +9,12 @@ use crate::{ }, inspector::utils::CommonCreateInput, script::{Broadcast, ScriptWallets}, - test::expect::{ - self, ExpectedCallData, ExpectedCallTracker, ExpectedCallType, ExpectedEmit, - ExpectedRevert, ExpectedRevertKind, + test::{ + assume::AssumeNoRevert, + expect::{ + self, ExpectedCallData, ExpectedCallTracker, ExpectedCallType, ExpectedEmit, + ExpectedRevert, ExpectedRevertKind, + }, }, utils::IgnoredTraces, CheatsConfig, CheatsCtxt, DynCheatcode, Error, Result, Vm, @@ -25,7 +28,7 @@ use foundry_config::Config; use foundry_evm_core::{ abi::Vm::stopExpectSafeMemoryCall, backend::{DatabaseExt, RevertDiagnostic}, - constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS}, + constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME}, utils::new_evm_with_existing_context, InspectorExt, }; @@ -294,6 +297,9 @@ pub struct Cheatcodes { /// Expected revert information pub expected_revert: Option, + /// Assume next call can revert and discard fuzz run if it does. + pub assume_no_revert: Option, + /// Additional diagnostic for reverts pub fork_revert_diagnostic: Option, @@ -384,6 +390,7 @@ impl Cheatcodes { gas_price: Default::default(), prank: Default::default(), expected_revert: Default::default(), + assume_no_revert: Default::default(), fork_revert_diagnostic: Default::default(), accesses: Default::default(), recorded_account_diffs_stack: Default::default(), @@ -1106,6 +1113,19 @@ impl Inspector for Cheatcodes { } } + // Handle assume not revert cheatcode. + if let Some(assume_no_revert) = &self.assume_no_revert { + if ecx.journaled_state.depth() == assume_no_revert.depth && !cheatcode_call { + // Discard run if we're at the same depth as cheatcode and call reverted. + if outcome.result.is_revert() { + outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into(); + return outcome; + } + // Call didn't revert, reset `assume_no_revert` state. + self.assume_no_revert = None; + } + } + // Handle expected reverts if let Some(expected_revert) = &self.expected_revert { if ecx.journaled_state.depth() <= expected_revert.depth { diff --git a/crates/cheatcodes/src/test.rs b/crates/cheatcodes/src/test.rs index 279150dff..0945f37a8 100644 --- a/crates/cheatcodes/src/test.rs +++ b/crates/cheatcodes/src/test.rs @@ -3,25 +3,15 @@ use chrono::DateTime; use std::env; -use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Error, Result, Vm::*}; +use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_primitives::Address; use alloy_sol_types::SolValue; -use foundry_evm_core::constants::{MAGIC_ASSUME, MAGIC_SKIP}; +use foundry_evm_core::constants::MAGIC_SKIP; pub(crate) mod assert; +pub(crate) mod assume; pub(crate) mod expect; -impl Cheatcode for assumeCall { - fn apply(&self, _state: &mut Cheatcodes) -> Result { - let Self { condition } = self; - if *condition { - Ok(Default::default()) - } else { - Err(Error::from(MAGIC_ASSUME)) - } - } -} - impl Cheatcode for breakpoint_0Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { char } = self; diff --git a/crates/cheatcodes/src/test/assume.rs b/crates/cheatcodes/src/test/assume.rs new file mode 100644 index 000000000..e100eeb9d --- /dev/null +++ b/crates/cheatcodes/src/test/assume.rs @@ -0,0 +1,29 @@ +use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Error, Result}; +use foundry_evm_core::{backend::DatabaseExt, constants::MAGIC_ASSUME}; +use spec::Vm::{assumeCall, assumeNoRevertCall}; +use std::fmt::Debug; + +#[derive(Clone, Debug)] +pub struct AssumeNoRevert { + /// The call depth at which the cheatcode was added. + pub depth: u64, +} + +impl Cheatcode for assumeCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { condition } = self; + if *condition { + Ok(Default::default()) + } else { + Err(Error::from(MAGIC_ASSUME)) + } + } +} + +impl Cheatcode for assumeNoRevertCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + ccx.state.assume_no_revert = + Some(AssumeNoRevert { depth: ccx.ecx.journaled_state.depth() }); + Ok(Default::default()) + } +} diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 23ffa890d..19e5c4853 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -1834,3 +1834,82 @@ contract CounterTest is DSTest { ... "#]]); }); + +forgetest_init!(test_assume_no_revert, |prj, cmd| { + prj.wipe_contracts(); + prj.insert_ds_test(); + prj.insert_vm(); + prj.clear(); + + prj.add_source( + "Counter.t.sol", + r#"pragma solidity 0.8.24; +import {Vm} from "./Vm.sol"; +import {DSTest} from "./test.sol"; +contract CounterWithRevert { + error CountError(); + error CheckError(); + + function count(uint256 a) public pure returns (uint256) { + if (a > 1000 || a < 10) { + revert CountError(); + } + return 99999999; + } + function check(uint256 a) public pure { + if (a == 99999999) { + revert CheckError(); + } + } + function dummy() public pure {} +} + +contract CounterRevertTest is DSTest { + Vm vm = Vm(HEVM_ADDRESS); + + function test_assume_no_revert_pass(uint256 a) public { + CounterWithRevert counter = new CounterWithRevert(); + vm.assumeNoRevert(); + a = counter.count(a); + assertEq(a, 99999999); + } + function test_assume_no_revert_fail_assert(uint256 a) public { + CounterWithRevert counter = new CounterWithRevert(); + vm.assumeNoRevert(); + a = counter.count(a); + // Test should fail on next assertion. + assertEq(a, 1); + } + function test_assume_no_revert_fail_in_2nd_call(uint256 a) public { + CounterWithRevert counter = new CounterWithRevert(); + vm.assumeNoRevert(); + a = counter.count(a); + // Test should revert here (not in scope of `assumeNoRevert` cheatcode). + counter.check(a); + assertEq(a, 99999999); + } + function test_assume_no_revert_fail_in_3rd_call(uint256 a) public { + CounterWithRevert counter = new CounterWithRevert(); + vm.assumeNoRevert(); + a = counter.count(a); + // Test `assumeNoRevert` applied to non reverting call should not be available for next reverting call. + vm.assumeNoRevert(); + counter.dummy(); + // Test will revert here (not in scope of `assumeNoRevert` cheatcode). + counter.check(a); + assertEq(a, 99999999); + } +} + "#, + ) + .unwrap(); + + cmd.args(["test"]).with_no_redact().assert_failure().stdout_eq(str![[r#" +... +[FAIL. Reason: assertion failed; counterexample: [..]] test_assume_no_revert_fail_assert(uint256) [..] +[FAIL. Reason: CheckError(); counterexample: [..]] test_assume_no_revert_fail_in_2nd_call(uint256) [..] +[FAIL. Reason: CheckError(); counterexample: [..]] test_assume_no_revert_fail_in_3rd_call(uint256) [..] +[PASS] test_assume_no_revert_pass(uint256) (runs: 256, [..]) +... +"#]]); +}); diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index b929053da..5b6750237 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -144,6 +144,7 @@ interface Vm { function assertTrue(bool condition) external pure; function assertTrue(bool condition, string calldata error) external pure; function assume(bool condition) external pure; + function assumeNoRevert() external pure; function blobBaseFee(uint256 newBlobBaseFee) external; function blobhashes(bytes32[] calldata hashes) external; function breakpoint(string calldata char) external; From 143abd6a768eeb52a5785240b763d72a56987b4a Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 3 Sep 2024 23:09:26 +0200 Subject: [PATCH 133/184] chore: fix rustc lint elided_named_lifetimes (#8796) --- Cargo.lock | 4 ++-- crates/common/src/ens.rs | 2 +- crates/config/src/lib.rs | 3 +-- .../doc/src/preprocessor/infer_hyperlinks.rs | 4 +--- crates/forge/src/multi_runner.rs | 20 +++++++++---------- 5 files changed, 14 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c353292ab..5633a3251 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1127,9 +1127,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.81" +version = "0.1.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ "proc-macro2", "quote", diff --git a/crates/common/src/ens.rs b/crates/common/src/ens.rs index e120ad4c2..f84ea84ce 100644 --- a/crates/common/src/ens.rs +++ b/crates/common/src/ens.rs @@ -1,6 +1,6 @@ //! ENS Name resolving utilities. -#![allow(missing_docs, elided_named_lifetimes)] +#![allow(missing_docs)] use self::EnsResolver::EnsResolverInstance; use alloy_primitives::{address, Address, Keccak256, B256}; diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index ef9c5f6b1..4dd6b8ce4 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -2,7 +2,6 @@ //! //! Foundry configuration. -#![allow(elided_named_lifetimes)] #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] @@ -1128,7 +1127,7 @@ impl Config { pub fn get_rpc_url_or<'a>( &'a self, fallback: impl Into>, - ) -> Result, UnresolvedEnvVarError> { + ) -> Result, UnresolvedEnvVarError> { if let Some(url) = self.get_rpc_url() { url } else { diff --git a/crates/doc/src/preprocessor/infer_hyperlinks.rs b/crates/doc/src/preprocessor/infer_hyperlinks.rs index 5108ee374..2d0802789 100644 --- a/crates/doc/src/preprocessor/infer_hyperlinks.rs +++ b/crates/doc/src/preprocessor/infer_hyperlinks.rs @@ -1,5 +1,3 @@ -#![allow(elided_named_lifetimes)] - use super::{Preprocessor, PreprocessorId}; use crate::{Comments, Document, ParseItem, ParseSource}; use forge_fmt::solang_ext::SafeUnwrap; @@ -228,7 +226,7 @@ impl<'a> InlineLink<'a> { }) } - fn captures(s: &'a str) -> impl Iterator + '_ { + fn captures(s: &'a str) -> impl Iterator + 'a { RE_INLINE_LINK.captures(s).map(Self::from_capture).into_iter().flatten() } diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index cee986553..43aade0ff 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -1,7 +1,5 @@ //! Forge test runner for multiple contracts. -#![allow(elided_named_lifetimes)] - use crate::{ progress::TestsProgress, result::SuiteResult, runner::LIBRARY_DEPLOYER, ContractRunner, TestFilter, TestOptions, @@ -86,28 +84,28 @@ pub struct MultiContractRunner { impl MultiContractRunner { /// Returns an iterator over all contracts that match the filter. - pub fn matching_contracts<'a>( + pub fn matching_contracts<'a: 'b, 'b>( &'a self, - filter: &'a dyn TestFilter, - ) -> impl Iterator { + filter: &'b dyn TestFilter, + ) -> impl Iterator + 'b { self.contracts.iter().filter(|&(id, c)| matches_contract(id, &c.abi, filter)) } /// Returns an iterator over all test functions that match the filter. - pub fn matching_test_functions<'a>( + pub fn matching_test_functions<'a: 'b, 'b>( &'a self, - filter: &'a dyn TestFilter, - ) -> impl Iterator { + filter: &'b dyn TestFilter, + ) -> impl Iterator + 'b { self.matching_contracts(filter) .flat_map(|(_, c)| c.abi.functions()) .filter(|func| is_matching_test(func, filter)) } /// Returns an iterator over all test functions in contracts that match the filter. - pub fn all_test_functions<'a>( + pub fn all_test_functions<'a: 'b, 'b>( &'a self, - filter: &'a dyn TestFilter, - ) -> impl Iterator { + filter: &'b dyn TestFilter, + ) -> impl Iterator + 'b { self.contracts .iter() .filter(|(id, _)| filter.matches_path(&id.source) && filter.matches_contract(&id.name)) From 6d6d430f17ffdcab9dff75e1f00bcfbdae969fcf Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 4 Sep 2024 11:10:02 +0800 Subject: [PATCH 134/184] fix: small issues (#8800) --- crates/cast/src/lib.rs | 2 +- crates/forge/bin/cmd/selectors.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 95952bfbb..12d42c2c9 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -1043,7 +1043,7 @@ impl SimpleCast { if MAX { let mut max = U256::MAX; if n < 255 { - max &= U256::from(1).wrapping_shl(n); + max &= U256::from(1).wrapping_shl(n).wrapping_sub(U256::from(1)); } Ok(max.to_string()) } else { diff --git a/crates/forge/bin/cmd/selectors.rs b/crates/forge/bin/cmd/selectors.rs index 04d19295c..7b7bce2e5 100644 --- a/crates/forge/bin/cmd/selectors.rs +++ b/crates/forge/bin/cmd/selectors.rs @@ -330,7 +330,7 @@ impl SelectorsSubcommands { for error in abi.errors() { if error.selector().as_slice().starts_with(selector_bytes.as_slice()) { table.add_row([ - "Event", + "Error", &error.signature(), &hex::encode_prefixed(error.selector()), contract.as_str(), From d1271376d36e4cb24015bc6cde272468158e7c8d Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:05:59 +0300 Subject: [PATCH 135/184] chore: fix `test_assume_no_revert` failing test (#8802) chore: fix nondeterministic failing test --- crates/forge/tests/cli/test_cmd.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 19e5c4853..3e3335f0c 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -1841,6 +1841,12 @@ forgetest_init!(test_assume_no_revert, |prj, cmd| { prj.insert_vm(); prj.clear(); + let config = Config { + fuzz: { FuzzConfig { runs: 100, seed: Some(U256::from(100)), ..Default::default() } }, + ..Default::default() + }; + prj.write_config(config); + prj.add_source( "Counter.t.sol", r#"pragma solidity 0.8.24; @@ -1909,7 +1915,7 @@ contract CounterRevertTest is DSTest { [FAIL. Reason: assertion failed; counterexample: [..]] test_assume_no_revert_fail_assert(uint256) [..] [FAIL. Reason: CheckError(); counterexample: [..]] test_assume_no_revert_fail_in_2nd_call(uint256) [..] [FAIL. Reason: CheckError(); counterexample: [..]] test_assume_no_revert_fail_in_3rd_call(uint256) [..] -[PASS] test_assume_no_revert_pass(uint256) (runs: 256, [..]) +[PASS] test_assume_no_revert_pass(uint256) [..] ... "#]]); }); From 63f9a0201903cff0df645a56fd44b5e7cc8dd8fd Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Wed, 4 Sep 2024 19:17:10 +0530 Subject: [PATCH 136/184] fix(`forge bind`): default to alloy (#8806) * fix(forge-bind): default to alloy * nit --- crates/forge/bin/cmd/bind.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/forge/bin/cmd/bind.rs b/crates/forge/bin/cmd/bind.rs index f9e9a22a4..d5d236332 100644 --- a/crates/forge/bin/cmd/bind.rs +++ b/crates/forge/bin/cmd/bind.rs @@ -104,10 +104,10 @@ impl BindArgs { let _ = ProjectCompiler::new().compile(&project)?; } - if !self.alloy { + if self.ethers { eprintln!( - "Warning: `--ethers` (default) bindings are deprecated and will be removed in the future. \ - Consider using `--alloy` instead." + "Warning: `--ethers` bindings are deprecated and will be removed in the future. \ + Consider using `--alloy` (default) instead." ); } @@ -191,7 +191,7 @@ impl BindArgs { fn get_json_files(&self, artifacts: &Path) -> Result> { let filter = self.get_filter()?; let alloy_filter = self.get_alloy_filter()?; - let is_alloy = self.alloy; + let is_alloy = !self.ethers; Ok(json_files(artifacts) .filter_map(|path| { // Ignore the build info JSON. @@ -264,7 +264,7 @@ impl BindArgs { /// Check that the existing bindings match the expected abigen output fn check_existing_bindings(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { - if !self.alloy { + if self.ethers { return self.check_ethers(artifacts, bindings_root); } @@ -316,7 +316,7 @@ impl BindArgs { /// Generate the bindings fn generate_bindings(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { - if !self.alloy { + if self.ethers { return self.generate_ethers(artifacts, bindings_root); } From eddb33a4119d8efadc2cd7857ef2c735e9d86e2b Mon Sep 17 00:00:00 2001 From: Maxim Andreev Date: Wed, 4 Sep 2024 16:48:53 +0300 Subject: [PATCH 137/184] feat(cast/selectors): show function state mutability (#8804) --- Cargo.lock | 5 +++-- crates/cast/Cargo.toml | 2 +- crates/cast/bin/main.rs | 33 ++++++++++++++++----------------- crates/cast/src/lib.rs | 31 ++++++++++++++++++------------- 4 files changed, 38 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5633a3251..da993734f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3135,10 +3135,11 @@ dependencies = [ [[package]] name = "evmole" -version = "0.3.8" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffb1f458e6901be6a6aaa485ce3a5d81478644edde1ffbe95da114ad9c94467" +checksum = "d55794094e85dd9d2a4a44de6554015c7d0d7193a28756f2ee3432bb763003a7" dependencies = [ + "alloy-dyn-abi", "ruint", ] diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index edc3fbe8d..386c523b4 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -79,7 +79,7 @@ tempfile.workspace = true tokio = { workspace = true, features = ["macros", "signal"] } tracing.workspace = true yansi.workspace = true -evmole = "0.3.1" +evmole = "0.4.1" [target.'cfg(unix)'.dependencies] tikv-jemallocator = { workspace = true, optional = true } diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 501955827..8eaa201d6 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -305,25 +305,24 @@ async fn main() -> Result<()> { println!("{}", SimpleCast::disassemble(&bytecode)?); } CastSubcommand::Selectors { bytecode, resolve } => { - let selectors_and_args = SimpleCast::extract_selectors(&bytecode)?; - if resolve { - let selectors_it = selectors_and_args.iter().map(|r| &r.0); - let resolve_results = - decode_selectors(SelectorType::Function, selectors_it).await?; + let functions = SimpleCast::extract_functions(&bytecode)?; + let max_args_len = functions.iter().map(|r| r.1.len()).max().unwrap_or(0); + let max_mutability_len = functions.iter().map(|r| r.2.len()).max().unwrap_or(0); - let max_args_len = selectors_and_args.iter().map(|r| r.1.len()).max().unwrap_or(0); - for ((selector, arguments), func_names) in - selectors_and_args.into_iter().zip(resolve_results.into_iter()) - { - let resolved = match func_names { - Some(v) => v.join("|"), - None => String::new(), - }; - println!("{selector}\t{arguments:max_args_len$}\t{resolved}"); - } + let resolve_results = if resolve { + let selectors_it = functions.iter().map(|r| &r.0); + let ds = decode_selectors(SelectorType::Function, selectors_it).await?; + ds.into_iter().map(|v| v.unwrap_or_default().join("|")).collect() } else { - for (selector, arguments) in selectors_and_args { - println!("{selector}\t{arguments}"); + vec![] + }; + for (pos, (selector, arguments, state_mutability)) in functions.into_iter().enumerate() + { + if resolve { + let resolved = &resolve_results[pos]; + println!("{selector}\t{arguments:max_args_len$}\t{state_mutability:max_mutability_len$}\t{resolved}"); + } else { + println!("{selector}\t{arguments:max_args_len$}\t{state_mutability}"); } } } diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 12d42c2c9..c4d756ad7 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -1103,7 +1103,7 @@ impl SimpleCast { pub fn to_ascii(hex: &str) -> Result { let bytes = hex::decode(hex)?; if !bytes.iter().all(u8::is_ascii) { - return Err(eyre::eyre!("Invalid ASCII bytes")) + return Err(eyre::eyre!("Invalid ASCII bytes")); } Ok(String::from_utf8(bytes).unwrap()) } @@ -1405,7 +1405,7 @@ impl SimpleCast { let base_in = Base::unwrap_or_detect(base_in, value)?; let base_out: Base = base_out.parse()?; if base_in == base_out { - return Ok(value.to_string()) + return Ok(value.to_string()); } let mut n = NumberWithBase::parse_int(value, Some(&base_in.to_string()))?; @@ -1469,7 +1469,7 @@ impl SimpleCast { let s = if let Some(stripped) = s.strip_prefix("000000000000000000000000") { stripped } else { - return Err(eyre::eyre!("Not convertible to address, there are non-zero bytes")) + return Err(eyre::eyre!("Not convertible to address, there are non-zero bytes")); }; let lowercase_address_string = format!("0x{s}"); @@ -1925,7 +1925,7 @@ impl SimpleCast { } if optimize == 0 { let selector = get_func(signature)?.selector(); - return Ok((selector.to_string(), String::from(signature))) + return Ok((selector.to_string(), String::from(signature))); } let Some((name, params)) = signature.split_once('(') else { eyre::bail!("invalid function signature"); @@ -1947,7 +1947,7 @@ impl SimpleCast { if selector.iter().take_while(|&&byte| byte == 0).count() == optimize { found.store(true, Ordering::Relaxed); - return Some((nonce, hex::encode_prefixed(selector), input)) + return Some((nonce, hex::encode_prefixed(selector), input)); } nonce += nonce_step; @@ -1961,7 +1961,7 @@ impl SimpleCast { } } - /// Extracts function selectors and arguments from bytecode + /// Extracts function selectors, arguments and state mutability from bytecode /// /// # Example /// @@ -1969,16 +1969,21 @@ impl SimpleCast { /// use cast::SimpleCast as Cast; /// /// let bytecode = "6080604052348015600e575f80fd5b50600436106026575f3560e01c80632125b65b14602a575b5f80fd5b603a6035366004603c565b505050565b005b5f805f60608486031215604d575f80fd5b833563ffffffff81168114605f575f80fd5b925060208401356001600160a01b03811681146079575f80fd5b915060408401356001600160e01b03811681146093575f80fd5b80915050925092509256"; - /// let selectors = Cast::extract_selectors(bytecode)?; - /// assert_eq!(selectors, vec![("0x2125b65b".to_string(), "uint32,address,uint224".to_string())]); + /// let functions = Cast::extract_functions(bytecode)?; + /// assert_eq!(functions, vec![("0x2125b65b".to_string(), "uint32,address,uint224".to_string(), "pure")]); /// # Ok::<(), eyre::Report>(()) /// ``` - pub fn extract_selectors(bytecode: &str) -> Result> { + pub fn extract_functions(bytecode: &str) -> Result> { let code = hex::decode(strip_0x(bytecode))?; - let s = evmole::function_selectors(&code, 0); - - Ok(s.iter() - .map(|s| (hex::encode_prefixed(s), evmole::function_arguments(&code, s, 0))) + Ok(evmole::function_selectors(&code, 0) + .into_iter() + .map(|s| { + ( + hex::encode_prefixed(s), + evmole::function_arguments(&code, &s, 0), + evmole::function_state_mutability(&code, &s, 0), + ) + }) .collect()) } From ea3ba89e8179dc983abb7aa91a6f388c17ad3cec Mon Sep 17 00:00:00 2001 From: m4rio <92288535+mario-eth@users.noreply.github.com> Date: Wed, 4 Sep 2024 22:45:43 +0300 Subject: [PATCH 138/184] feat: update soldeer 0 3 4 (#8777) * Update to 0.3.2 including a hotfix on the integrity check * fmt * replacing assert with data assert for files * Moved to Snapbox * reverse soldeer to 0.3.1 to test * reverted to old tests to check for timeout * updated to soldeer 0.3.3 * added stdout checks * fmt * updated output * updated to 0.3.4 which contains the windows fix * deleted non-deterministic output --- Cargo.lock | 96 ++--------- Cargo.toml | 2 +- crates/forge/tests/cli/soldeer.rs | 265 +++++++++++++++++++++++------- 3 files changed, 213 insertions(+), 150 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index da993734f..846989c41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1935,8 +1935,6 @@ version = "1.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" dependencies = [ - "jobserver", - "libc", "shlex", ] @@ -2314,12 +2312,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - [[package]] name = "convert_case" version = "0.4.0" @@ -5211,15 +5203,6 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" -[[package]] -name = "jobserver" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" -dependencies = [ - "libc", -] - [[package]] name = "js-sys" version = "0.3.70" @@ -6165,17 +6148,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "password-hash" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" -dependencies = [ - "base64ct", - "rand_core", - "subtle", -] - [[package]] name = "paste" version = "1.0.15" @@ -6195,9 +6167,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ "digest 0.10.7", - "hmac", - "password-hash", - "sha2", ] [[package]] @@ -8040,17 +8009,19 @@ dependencies = [ [[package]] name = "soldeer" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6191e16cbc3b4ed14cafc642bc2b7f966eddb7c1f3a9f60549f6a1e526016127" +checksum = "b9ed763c2bb43241ca0fb6c00feea54187895b7f4eb1090654fbf82807127369" dependencies = [ "chrono", "clap", "const-hex", + "dunce", "email-address-parser", "futures", "home", "ignore", + "path-slash", "regex", "reqwest", "rpassword", @@ -8063,7 +8034,7 @@ dependencies = [ "toml_edit", "uuid 1.10.0", "yansi", - "zip 2.2.0", + "zip", "zip-extract", ] @@ -8179,7 +8150,7 @@ dependencies = [ "tempfile", "thiserror", "url", - "zip 2.2.0", + "zip", ] [[package]] @@ -9710,25 +9681,6 @@ dependencies = [ "syn 2.0.77", ] -[[package]] -name = "zip" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" -dependencies = [ - "aes", - "byteorder", - "bzip2", - "constant_time_eq", - "crc32fast", - "crossbeam-utils", - "flate2", - "hmac", - "pbkdf2 0.11.0", - "sha1", - "zstd", -] - [[package]] name = "zip" version = "2.2.0" @@ -9736,6 +9688,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc5e4288ea4057ae23afc69a4472434a87a2495cafce6632fd1c4ec9f5cf3494" dependencies = [ "arbitrary", + "bzip2", "crc32fast", "crossbeam-utils", "displaydoc", @@ -9748,13 +9701,13 @@ dependencies = [ [[package]] name = "zip-extract" -version = "0.1.3" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e109e5a291403b4c1e514d39f8a22d3f98d257e691a52bb1f16051bb1ffed63e" +checksum = "25a8c9e90f27d1435088a7b540b6cc8ae6ee525d992a695f16012d2f365b3d3c" dependencies = [ "log", "thiserror", - "zip 0.6.6", + "zip", ] [[package]] @@ -9770,32 +9723,3 @@ dependencies = [ "once_cell", "simd-adler32", ] - -[[package]] -name = "zstd" -version = "0.11.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "5.0.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.13+zstd.1.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" -dependencies = [ - "cc", - "pkg-config", -] diff --git a/Cargo.toml b/Cargo.toml index df99f6b46..9aa6ab471 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -259,7 +259,7 @@ reqwest = { version = "0.12", default-features = false } tower = "0.4" tower-http = "0.5" # soldeer -soldeer = "=0.3.1" +soldeer = "=0.3.4" proptest = "1" comfy-table = "7" diff --git a/crates/forge/tests/cli/soldeer.rs b/crates/forge/tests/cli/soldeer.rs index 21666edbb..6c57f69f3 100644 --- a/crates/forge/tests/cli/soldeer.rs +++ b/crates/forge/tests/cli/soldeer.rs @@ -7,7 +7,6 @@ use std::{ use foundry_test_utils::forgesoldeer; use std::io::Write; - forgesoldeer!(install_dependency, |prj, cmd| { let command = "install"; let dependency = "forge-std~1.8.1"; @@ -15,8 +14,18 @@ forgesoldeer!(install_dependency, |prj, cmd| { let foundry_file = prj.root().join("foundry.toml"); cmd.arg("soldeer").args([command, dependency]).assert_success().stdout_eq(str![[r#" -🦌 Running [..]oldeer install 🦌 -... +🦌 Running Soldeer install 🦌 +No config file found. If you wish to proceed, please select how you want Soldeer to be configured: +1. Using foundry.toml +2. Using soldeer.toml +(Press 1 or 2), default is foundry.toml +Started HTTP download of forge-std~1.8.1 +Dependency forge-std~1.8.1 downloaded! +Adding dependency forge-std-1.8.1 to the config file +The dependency forge-std~1.8.1 was unzipped! +Writing forge-std~1.8.1 to the lock file. +Added forge-std~1.8.1 to remappings + "#]]); // Making sure the path was created to the dependency and that foundry.toml exists @@ -27,12 +36,17 @@ forgesoldeer!(install_dependency, |prj, cmd| { // Making sure the lock contents are the right ones let path_lock_file = prj.root().join("soldeer.lock"); + // let lock_contents = r#"[[dependencies]] + // name = "forge-std" + // version = "1.8.1" + // source = "https://soldeer-revisions.s3.amazonaws.com/forge-std/v1_8_1_23-03-2024_00:05:44_forge-std-v1.8.1.zip" + // checksum = "0f7cd44f5670c31a9646d4031e70c66321cd3ed6ebac3c7278e4e57e4e5c5bd0" + // integrity = "6a52f0c34d935e508af46a6d12a3a741798252f20a66f6bbee86c23dd6ef7c8d" + // "#; let actual_lock_contents = read_file_to_string(&path_lock_file); + // assert_data_eq!(lock_contents, actual_lock_contents); assert!(actual_lock_contents.contains("forge-std")); - assert!(actual_lock_contents - .contains("0f7cd44f5670c31a9646d4031e70c66321cd3ed6ebac3c7278e4e57e4e5c5bd0")); - assert!(actual_lock_contents.contains("1.8.1")); // Making sure the foundry contents are the right ones let foundry_contents = r#"[profile.default] @@ -56,9 +70,36 @@ forgesoldeer!(install_dependency_git, |prj, cmd| { let foundry_file = prj.root().join("foundry.toml"); - cmd.arg("soldeer").args([command, dependency, git]).assert_success().stdout_eq(str![[r#" -🦌 Running [..]oldeer install 🦌 -... + cmd.arg("soldeer") + .args([command, dependency, git]) + .assert_success() + .stdout_eq(str![[r#" +🦌 Running Soldeer install 🦌 +No config file found. If you wish to proceed, please select how you want Soldeer to be configured: +1. Using foundry.toml +2. Using soldeer.toml +(Press 1 or 2), default is foundry.toml +Started GIT download of forge-std~1.8.1 +Successfully downloaded forge-std~1.8.1 the dependency via git +Dependency forge-std~1.8.1 downloaded! +Adding dependency forge-std-1.8.1 to the config file +Writing forge-std~1.8.1 to the lock file. +Added forge-std~1.8.1 to remappings + +"#]]) + .stdout_eq(str![[r#" +🦌 Running Soldeer install 🦌 +No config file found. If you wish to proceed, please select how you want Soldeer to be configured: +1. Using foundry.toml +2. Using soldeer.toml +(Press 1 or 2), default is foundry.toml +Started GIT download of forge-std~1.8.1 +Successfully downloaded forge-std~1.8.1 the dependency via git +Dependency forge-std~1.8.1 downloaded! +Adding dependency forge-std-1.8.1 to the config file +Writing forge-std~1.8.1 to the lock file. +Added forge-std~1.8.1 to remappings + "#]]); // Making sure the path was created to the dependency and that README.md exists @@ -68,11 +109,16 @@ forgesoldeer!(install_dependency_git, |prj, cmd| { // Making sure the lock contents are the right ones let path_lock_file = prj.root().join("soldeer.lock"); + // let lock_contents = r#"[[dependencies]] + // name = "forge-std" + // version = "1.8.1" + // source = "https://gitlab.com/mario4582928/Mario.git" + // checksum = "22868f426bd4dd0e682b5ec5f9bd55507664240c" + // "#; let actual_lock_contents = read_file_to_string(&path_lock_file); + // assert_data_eq!(lock_contents, actual_lock_contents); assert!(actual_lock_contents.contains("forge-std")); - assert!(actual_lock_contents.contains("22868f426bd4dd0e682b5ec5f9bd55507664240c")); - assert!(actual_lock_contents.contains("1.8.1")); // Making sure the foundry contents are the right ones let foundry_contents = r#"[profile.default] @@ -102,8 +148,18 @@ forgesoldeer!(install_dependency_git_commit, |prj, cmd| { .args([command, dependency, git, rev_flag, commit]) .assert_success() .stdout_eq(str![[r#" -🦌 Running [..]oldeer install 🦌 -... +🦌 Running Soldeer install 🦌 +No config file found. If you wish to proceed, please select how you want Soldeer to be configured: +1. Using foundry.toml +2. Using soldeer.toml +(Press 1 or 2), default is foundry.toml +Started GIT download of forge-std~1.8.1 +Successfully downloaded forge-std~1.8.1 the dependency via git +Dependency forge-std~1.8.1 downloaded! +Adding dependency forge-std-1.8.1 to the config file +Writing forge-std~1.8.1 to the lock file. +Added forge-std~1.8.1 to remappings + "#]]); // Making sure the path was created to the dependency and that README.md exists @@ -114,12 +170,16 @@ forgesoldeer!(install_dependency_git_commit, |prj, cmd| { // Making sure the lock contents are the right ones let path_lock_file = prj.root().join("soldeer.lock"); + // let lock_contents = r#"[[dependencies]] + // name = "forge-std" + // version = "1.8.1" + // source = "https://gitlab.com/mario4582928/Mario.git" + // checksum = "7a0663eaf7488732f39550be655bad6694974cb3" + // "#; let actual_lock_contents = read_file_to_string(&path_lock_file); + // assert_data_eq!(lock_contents, actual_lock_contents); assert!(actual_lock_contents.contains("forge-std")); - assert!(actual_lock_contents.contains("7a0663eaf7488732f39550be655bad6694974cb3")); - assert!(actual_lock_contents.contains("https://gitlab.com/mario4582928/Mario.git")); - assert!(actual_lock_contents.contains("1.8.1")); // Making sure the foundry contents are the right ones let foundry_contents = r#"[profile.default] @@ -157,20 +217,10 @@ mario-custom-branch = { version = "1.0", git = "https://gitlab.com/mario4582928/ eprintln!("Couldn't write to file: {e}"); } - cmd.arg("soldeer").arg(command).assert_success().stdout_eq(str![[r#" -🦌 Running [..]oldeer update 🦌 -... - -"#]]); + cmd.arg("soldeer").arg(command).assert_success(); // Making sure the path was created to the dependency and that foundry.toml exists // meaning that the dependencies were installed correctly - let path_dep_forge = - prj.root().join("dependencies").join("forge-std-1.8.1").join("foundry.toml"); - assert!(path_dep_forge.exists()); - - // Making sure the lock contents are the right ones - let path_lock_file = prj.root().join("soldeer.lock"); let dep1 = prj.root().join("dependencies").join("@tt-1.6.1"); let dep2 = prj.root().join("dependencies").join("forge-std-1.8.1"); let dep3 = prj.root().join("dependencies").join("mario-1.0"); @@ -178,13 +228,58 @@ mario-custom-branch = { version = "1.0", git = "https://gitlab.com/mario4582928/ let dep5 = prj.root().join("dependencies").join("mario-custom-tag-1.0"); let dep6 = prj.root().join("dependencies").join("mario-custom-branch-1.0"); + assert!(dep1.exists()); + assert!(dep2.exists()); + assert!(dep3.exists()); + assert!(dep4.exists()); + assert!(dep5.exists()); + assert!(dep6.exists()); + + // Making sure the lock contents are the right ones + let path_lock_file = prj.root().join("soldeer.lock"); + // let lock_contents = r#"[[dependencies]] + // name = "@tt" + // version = "1.6.1" + // source = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts/3_3_0-rc_2_22-01-2024_13:12:57_contracts.zip" + // checksum = "3aa5b07e796ce2ae54bbab3a5280912444ae75807136a513fa19ff3a314c323f" + // integrity = "24e7847580674bd0a4abf222b82fac637055141704c75a3d679f637acdcfe817" + + // [[dependencies]] + // name = "forge-std" + // version = "1.8.1" + // source = "https://soldeer-revisions.s3.amazonaws.com/forge-std/v1_8_1_23-03-2024_00:05:44_forge-std-v1.8.1.zip" + // checksum = "0f7cd44f5670c31a9646d4031e70c66321cd3ed6ebac3c7278e4e57e4e5c5bd0" + // integrity = "6a52f0c34d935e508af46a6d12a3a741798252f20a66f6bbee86c23dd6ef7c8d" + + // [[dependencies]] + // name = "mario" + // version = "1.0" + // source = "https://gitlab.com/mario4582928/Mario.git" + // checksum = "22868f426bd4dd0e682b5ec5f9bd55507664240c" + + // [[dependencies]] + // name = "mario-custom-branch" + // version = "1.0" + // source = "https://gitlab.com/mario4582928/Mario.git" + // checksum = "84c3b38dba44a4c29ec44f45a31e1e59d36aa77b" + + // [[dependencies]] + // name = "mario-custom-tag" + // version = "1.0" + // source = "https://gitlab.com/mario4582928/Mario.git" + // checksum = "a366c4b560022d12e668d6c1756c6382e2352d0f" + + // [[dependencies]] + // name = "solmate" + // version = "6.7.0" + // source = "https://soldeer-revisions.s3.amazonaws.com/solmate/6_7_0_22-01-2024_13:21:00_solmate.zip" + // checksum = "dd0f08cdaaaad1de0ac45993d4959351ba89c2d9325a0b5df5570357064f2c33" + // integrity = "ec330877af853f9d34b2b1bf692fb33c9f56450625f5c4abdcf0d3405839730e" + // "#; + + // assert_data_eq!(lock_contents, read_file_to_string(&path_lock_file)); let actual_lock_contents = read_file_to_string(&path_lock_file); - assert!(actual_lock_contents.contains("@tt")); assert!(actual_lock_contents.contains("forge-std")); - assert!(actual_lock_contents.contains("mario")); - assert!(actual_lock_contents.contains("solmate")); - assert!(actual_lock_contents.contains("mario-custom-tag")); - assert!(actual_lock_contents.contains("mario-custom-branch")); // Making sure the foundry contents are the right ones let foundry_contents = r#"[profile.default] @@ -204,12 +299,6 @@ mario-custom-branch = { version = "1.0", git = "https://gitlab.com/mario4582928/ "#; assert_data_eq!(read_file_to_string(&foundry_file), foundry_contents); - assert!(dep1.exists()); - assert!(dep2.exists()); - assert!(dep3.exists()); - assert!(dep4.exists()); - assert!(dep5.exists()); - assert!(dep6.exists()); }); forgesoldeer!(update_dependencies_simple_version, |prj, cmd| { @@ -230,8 +319,11 @@ forge-std = "1.8.1" } cmd.arg("soldeer").arg(command).assert_success().stdout_eq(str![[r#" -🦌 Running [..]oldeer update 🦌 -... +🦌 Running Soldeer update 🦌 +Started HTTP download of forge-std~1.8.1 +Dependency forge-std~1.8.1 downloaded! +The dependency forge-std~1.8.1 was unzipped! +Writing forge-std~1.8.1 to the lock file. "#]]); @@ -243,12 +335,17 @@ forge-std = "1.8.1" // Making sure the lock contents are the right ones let path_lock_file = prj.root().join("soldeer.lock"); + // let lock_contents = r#"[[dependencies]] + // name = "forge-std" + // version = "1.8.1" + // source = "https://soldeer-revisions.s3.amazonaws.com/forge-std/v1_8_1_23-03-2024_00:05:44_forge-std-v1.8.1.zip" + // checksum = "0f7cd44f5670c31a9646d4031e70c66321cd3ed6ebac3c7278e4e57e4e5c5bd0" + // integrity = "6a52f0c34d935e508af46a6d12a3a741798252f20a66f6bbee86c23dd6ef7c8d" + // "#; let actual_lock_contents = read_file_to_string(&path_lock_file); + // assert_data_eq!(lock_contents, actual_lock_contents); assert!(actual_lock_contents.contains("forge-std")); - assert!(actual_lock_contents - .contains("0f7cd44f5670c31a9646d4031e70c66321cd3ed6ebac3c7278e4e57e4e5c5bd0")); - assert!(actual_lock_contents.contains("1.8.1")); // Making sure the foundry contents are the right ones let foundry_contents = r#"[profile.default] @@ -268,20 +365,11 @@ forge-std = "1.8.1" forgesoldeer!(login, |prj, cmd| { let command = "login"; - cmd.arg("soldeer") - .arg(command) - .assert_failure() - .stderr_eq(str![[r#" -Error: -Failed to run [..] + let output = cmd.arg("soldeer").arg(command).execute(); -"#]]) - .stdout_eq(str![[r#" -🦌 Running [..]oldeer login 🦌 -... -ℹ️ If you do not have an account, please go to soldeer.xyz to create one. -📧 Please enter your email: -"#]]); + // On login, we can only check if the prompt is displayed in the stdout + let stdout = String::from_utf8(output.stdout).expect("Could not parse the output"); + assert!(stdout.contains("Please enter your email")); }); forgesoldeer!(install_dependency_with_remappings_config, |prj, cmd| { @@ -301,8 +389,20 @@ remappings_regenerate = true eprintln!("Couldn't write to file: {e}"); } - cmd.arg("soldeer").args([command, dependency]); - cmd.execute(); + cmd.arg("soldeer").args([command, dependency]).assert_success().stdout_eq(str![[r#" +🦌 Running Soldeer install 🦌 +No config file found. If you wish to proceed, please select how you want Soldeer to be configured: +1. Using foundry.toml +2. Using soldeer.toml +(Press 1 or 2), default is foundry.toml +Started HTTP download of forge-std~1.8.1 +Dependency forge-std~1.8.1 downloaded! +Adding dependency forge-std-1.8.1 to the config file +The dependency forge-std~1.8.1 was unzipped! +Writing forge-std~1.8.1 to the lock file. +Added all dependencies to remapppings + +"#]]); // Making sure the path was created to the dependency and that foundry.toml exists // meaning that the dependencies were installed correctly @@ -310,6 +410,20 @@ remappings_regenerate = true prj.root().join("dependencies").join("forge-std-1.8.1").join("foundry.toml"); assert!(path_dep_forge.exists()); + // Making sure the lock contents are the right ones + let path_lock_file = prj.root().join("soldeer.lock"); + // let lock_contents = r#"[[dependencies]] + // name = "forge-std" + // version = "1.8.1" + // source = "https://soldeer-revisions.s3.amazonaws.com/forge-std/v1_8_1_23-03-2024_00:05:44_forge-std-v1.8.1.zip" + // checksum = "0f7cd44f5670c31a9646d4031e70c66321cd3ed6ebac3c7278e4e57e4e5c5bd0" + // integrity = "6a52f0c34d935e508af46a6d12a3a741798252f20a66f6bbee86c23dd6ef7c8d" + // "#; + + let actual_lock_contents = read_file_to_string(&path_lock_file); + // assert_data_eq!(lock_contents, actual_lock_contents); + assert!(actual_lock_contents.contains("forge-std")); + // Making sure the foundry contents are the right ones let foundry_contents = r#"[profile.default] src = "src" @@ -349,8 +463,20 @@ remappings_regenerate = true eprintln!("Couldn't write to file: {e}"); } - cmd.arg("soldeer").args([command, dependency]); - cmd.execute(); + cmd.arg("soldeer").args([command, dependency]).assert_success().stdout_eq(str![[r#" +🦌 Running Soldeer install 🦌 +No config file found. If you wish to proceed, please select how you want Soldeer to be configured: +1. Using foundry.toml +2. Using soldeer.toml +(Press 1 or 2), default is foundry.toml +Started HTTP download of forge-std~1.8.1 +Dependency forge-std~1.8.1 downloaded! +Adding dependency forge-std-1.8.1 to the config file +The dependency forge-std~1.8.1 was unzipped! +Writing forge-std~1.8.1 to the lock file. +Added all dependencies to remapppings + +"#]]); // Making sure the path was created to the dependency and that foundry.toml exists // meaning that the dependencies were installed correctly @@ -358,11 +484,24 @@ remappings_regenerate = true prj.root().join("dependencies").join("forge-std-1.8.1").join("foundry.toml"); assert!(path_dep_forge.exists()); + // Making sure the lock contents are the right ones + let path_lock_file = prj.root().join("soldeer.lock"); + // let lock_contents = r#"[[dependencies]] + // name = "forge-std" + // version = "1.8.1" + // source = "https://soldeer-revisions.s3.amazonaws.com/forge-std/v1_8_1_23-03-2024_00:05:44_forge-std-v1.8.1.zip" + // checksum = "0f7cd44f5670c31a9646d4031e70c66321cd3ed6ebac3c7278e4e57e4e5c5bd0" + // integrity = "6a52f0c34d935e508af46a6d12a3a741798252f20a66f6bbee86c23dd6ef7c8d" + // "#; + + let actual_lock_contents = read_file_to_string(&path_lock_file); + // assert_data_eq!(lock_contents, actual_lock_contents); + assert!(actual_lock_contents.contains("forge-std")); + // Making sure the foundry contents are the right ones - let remappings_content = "@custom-f@forge-std-1.8.1/=dependencies/forge-std-1.8.1/\n"; + let remappings_content = r#"@custom-f@forge-std-1.8.1/=dependencies/forge-std-1.8.1/ +"#; let remappings_file = prj.root().join("remappings.txt"); - println!("ddd {:?}", read_file_to_string(&remappings_file)); - assert_data_eq!(read_file_to_string(&remappings_file), remappings_content); }); From 3f15f9a83377daac583ff87f900cb6d86ee4499f Mon Sep 17 00:00:00 2001 From: James <107906898+EdwardJES@users.noreply.github.com> Date: Thu, 5 Sep 2024 21:15:03 +1000 Subject: [PATCH 139/184] feat(anvil): 7368 reorg (#8489) * solve merge conflicts * clean up imports * address last nits * small fix * small comment fix * make comment clearer --- crates/anvil/core/src/eth/mod.rs | 81 ++++++++++- crates/anvil/core/src/eth/transaction/mod.rs | 30 ++++ crates/anvil/core/src/types.rs | 20 ++- crates/anvil/src/eth/api.rs | 131 ++++++++++++++++- crates/anvil/src/eth/backend/mem/mod.rs | 70 +++++++++ crates/anvil/src/eth/backend/mem/storage.rs | 17 +++ crates/anvil/src/eth/pool/transactions.rs | 9 +- crates/anvil/tests/it/anvil_api.rs | 145 ++++++++++++++++++- 8 files changed, 493 insertions(+), 10 deletions(-) diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index 7c86baad3..11342b817 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -1,4 +1,4 @@ -use crate::eth::subscription::SubscriptionId; +use crate::{eth::subscription::SubscriptionId, types::ReorgOptions}; use alloy_primitives::{Address, Bytes, TxHash, B256, B64, U256}; use alloy_rpc_types::{ anvil::{Forking, MineOptions}, @@ -768,6 +768,10 @@ pub enum EthRequest { serde(rename = "anvil_removePoolTransactions", with = "sequence") )] RemovePoolTransactions(Address), + + /// Reorg the chain + #[cfg_attr(feature = "serde", serde(rename = "anvil_reorg",))] + Reorg(ReorgOptions), } /// Represents ethereum JSON-RPC API @@ -1595,4 +1599,79 @@ true}]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); } + + #[test] + fn test_serde_anvil_reorg() { + // TransactionData::JSON + let s = r#" + { + "method": "anvil_reorg", + "params": [ + 5, + [ + [ + { + "from": "0x976EA74026E726554dB657fA54763abd0C3a0aa9", + "to": "0x1199bc69f16FDD6690DC40339EC445FaE1b6DD11", + "value": 100 + }, + 1 + ], + [ + { + "from": "0x976EA74026E726554dB657fA54763abd0C3a0aa9", + "to": "0x1199bc69f16FDD6690DC40339EC445FaE1b6DD11", + "value": 200 + }, + 2 + ] + ] + ] + } + "#; + let value: serde_json::Value = serde_json::from_str(s).unwrap(); + let _req = serde_json::from_value::(value).unwrap(); + // TransactionData::Raw + let s = r#" + { + "method": "anvil_reorg", + "params": [ + 5, + [ + [ + "0x19d55c67e1ba8f1bbdfed75f8ad524ebf087e4ecb848a2d19881d7a5e3d2c54e1732cb1b462da3b3fdb05bdf4c4d3c8e3c9fcebdc2ab5fa5d59a3f752888f27e1b", + 1 + ] + ] + ] + } + "#; + let value: serde_json::Value = serde_json::from_str(s).unwrap(); + let _req = serde_json::from_value::(value).unwrap(); + // TransactionData::Raw and TransactionData::JSON + let s = r#" + { + "method": "anvil_reorg", + "params": [ + 5, + [ + [ + "0x19d55c67e1ba8f1bbdfed75f8ad524ebf087e4ecb848a2d19881d7a5e3d2c54e1732cb1b462da3b3fdb05bdf4c4d3c8e3c9fcebdc2ab5fa5d59a3f752888f27e1b", + 1 + ], + [ + { + "from": "0x976EA74026E726554dB657fA54763abd0C3a0aa9", + "to": "0x1199bc69f16FDD6690DC40339EC445FaE1b6DD11", + "value": 200 + }, + 2 + ] + ] + ] + } + "#; + let value: serde_json::Value = serde_json::from_str(s).unwrap(); + let _req = serde_json::from_value::(value).unwrap(); + } } diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 4320b423a..d080f1e27 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -674,6 +674,36 @@ pub enum TypedTransaction { Deposit(DepositTransaction), } +/// This is a function that demotes TypedTransaction to TransactionRequest for greater flexibility +/// over the type. +/// +/// This function is purely for convience and specific use cases, e.g. RLP encoded transactions +/// decode to TypedTransactions where the API over TypedTransctions is quite strict. +impl TryFrom for TransactionRequest { + type Error = ConversionError; + + fn try_from(value: TypedTransaction) -> Result { + let from = value.recover().map_err(|_| ConversionError::InvalidSignature)?; + let essentials = value.essentials(); + let tx_type = value.r#type(); + Ok(Self { + from: Some(from), + to: Some(value.kind()), + gas_price: essentials.gas_price, + max_fee_per_gas: essentials.max_fee_per_gas, + max_priority_fee_per_gas: essentials.max_priority_fee_per_gas, + max_fee_per_blob_gas: essentials.max_fee_per_blob_gas, + gas: Some(essentials.gas_limit), + value: Some(essentials.value), + input: essentials.input.into(), + nonce: Some(essentials.nonce), + chain_id: essentials.chain_id, + transaction_type: tx_type, + ..Default::default() + }) + } +} + impl TypedTransaction { /// Returns true if the transaction uses dynamic fees: EIP1559 or EIP4844 pub fn is_dynamic_fee(&self) -> bool { diff --git a/crates/anvil/core/src/types.rs b/crates/anvil/core/src/types.rs index 348686abc..ca7563588 100644 --- a/crates/anvil/core/src/types.rs +++ b/crates/anvil/core/src/types.rs @@ -1,5 +1,7 @@ -use alloy_primitives::{B256, U256}; +use alloy_primitives::{Bytes, B256, U256}; +use alloy_rpc_types::TransactionRequest; +use serde::Deserialize; #[cfg(feature = "serde")] use serde::Serializer; @@ -26,3 +28,19 @@ impl serde::Serialize for Work { } } } + +/// Represents the options used in `anvil_reorg` +#[derive(Debug, Clone, Deserialize)] +pub struct ReorgOptions { + // The depth of the reorg + pub depth: u64, + // List of transaction requests and blocks pairs to be mined into the new chain + pub tx_block_pairs: Vec<(TransactionData, u64)>, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(untagged)] +pub enum TransactionData { + JSON(TransactionRequest), + Raw(Bytes), +} diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index fbd37a3f2..937cd8504 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -64,7 +64,7 @@ use anvil_core::{ }, EthRequest, }, - types::Work, + types::{ReorgOptions, TransactionData, Work}, }; use anvil_rpc::{error::RpcError, response::ResponseResult}; use foundry_common::provider::ProviderBuilder; @@ -79,7 +79,12 @@ use foundry_evm::{ }; use futures::channel::{mpsc::Receiver, oneshot}; use parking_lot::RwLock; -use std::{collections::HashSet, future::Future, sync::Arc, time::Duration}; +use std::{ + collections::{HashMap, HashSet}, + future::Future, + sync::Arc, + time::Duration, +}; /// The client version: `anvil/v{major}.{minor}.{patch}` pub const CLIENT_VERSION: &str = concat!("anvil/v", env!("CARGO_PKG_VERSION")); @@ -440,6 +445,9 @@ impl EthApi { EthRequest::RemovePoolTransactions(address) => { self.anvil_remove_pool_transactions(address).await.to_rpc_result() } + EthRequest::Reorg(reorg_options) => { + self.anvil_reorg(reorg_options).await.to_rpc_result() + } } } @@ -995,6 +1003,7 @@ impl EthApi { if data.is_empty() { return Err(BlockchainError::EmptyRawTransactionData); } + let transaction = TypedTransaction::decode_2718(&mut data) .map_err(|_| BlockchainError::FailedToDecodeSignedTransaction)?; @@ -1915,6 +1924,124 @@ impl EthApi { Ok(()) } + /// Reorg the chain to a specific depth and mine new blocks back to the cannonical height. + /// + /// e.g depth = 3 + /// A -> B -> C -> D -> E + /// A -> B -> C' -> D' -> E' + /// + /// Depth specifies the height to reorg the chain back to. Depth must not exceed the current + /// chain height, i.e. can't reorg past the genesis block. + /// + /// Optionally supply a list of transaction and block pairs that will populate the reorged + /// blocks. The maximum block number of the pairs must not exceed the specified depth. + /// + /// Handler for RPC call: `anvil_reorg` + pub async fn anvil_reorg(&self, options: ReorgOptions) -> Result<()> { + node_info!("anvil_reorg"); + let depth = options.depth; + let tx_block_pairs = options.tx_block_pairs; + + // Check reorg depth doesn't exceed current chain height + let current_height = self.backend.best_number(); + let common_height = current_height.checked_sub(depth).ok_or(BlockchainError::RpcError( + RpcError::invalid_params(format!( + "Reorg depth must not exceed current chain height: current height {current_height}, depth {depth}" + )), + ))?; + + // Get the common ancestor block + let common_block = + self.backend.get_block(common_height).ok_or(BlockchainError::BlockNotFound)?; + + // Convert the transaction requests to pool transactions if they exist, otherwise use empty + // hashmap + let block_pool_txs = if tx_block_pairs.is_empty() { + HashMap::new() + } else { + let mut pairs = tx_block_pairs; + + // Check the maximum block supplied number will not exceed the reorged chain height + if let Some((_, num)) = pairs.iter().find(|(_, num)| *num >= depth) { + return Err(BlockchainError::RpcError(RpcError::invalid_params(format!( + "Block number for reorg tx will exceed the reorged chain height. Block number {num} must not exceed (depth-1) {}", + depth-1 + )))); + } + + // Sort by block number to make it easier to manage new nonces + pairs.sort_by_key(|a| a.1); + + // Manage nonces for each signer + // address -> cumulative nonce + let mut nonces: HashMap = HashMap::new(); + + let mut txs: HashMap>> = HashMap::new(); + for pair in pairs { + let (tx_data, block_index) = pair; + + let mut tx_req = match tx_data { + TransactionData::JSON(req) => WithOtherFields::new(req), + TransactionData::Raw(bytes) => { + let mut data = bytes.as_ref(); + let decoded = TypedTransaction::decode_2718(&mut data) + .map_err(|_| BlockchainError::FailedToDecodeSignedTransaction)?; + let request = + TransactionRequest::try_from(decoded.clone()).map_err(|_| { + BlockchainError::RpcError(RpcError::invalid_params( + "Failed to convert raw transaction", + )) + })?; + WithOtherFields::new(request) + } + }; + + let from = tx_req.from.map(Ok).unwrap_or_else(|| { + self.accounts()?.first().cloned().ok_or(BlockchainError::NoSignerAvailable) + })?; + + // Get the nonce at the common block + let curr_nonce = nonces.entry(from).or_insert( + self.get_transaction_count(from, Some(common_block.header.number.into())) + .await?, + ); + + // Estimate gas + if tx_req.gas.is_none() { + if let Ok(gas) = self.estimate_gas(tx_req.clone(), None, None).await { + tx_req.gas = Some(gas.to()); + } + } + + // Build typed transaction request + let typed = self.build_typed_tx_request(tx_req, *curr_nonce)?; + + // Increment nonce + *curr_nonce += 1; + + // Handle signer and convert to pending transaction + let pending = if self.is_impersonated(from) { + let bypass_signature = self.impersonated_signature(&typed); + let transaction = sign::build_typed_transaction(typed, bypass_signature)?; + self.ensure_typed_transaction_supported(&transaction)?; + PendingTransaction::with_impersonated(transaction, from) + } else { + let transaction = self.sign_request(&from, typed)?; + self.ensure_typed_transaction_supported(&transaction)?; + PendingTransaction::new(transaction)? + }; + + let pooled = PoolTransaction::new(pending); + txs.entry(block_index).or_default().push(Arc::new(pooled)); + } + + txs + }; + + self.backend.reorg(depth, block_pool_txs, common_block).await?; + Ok(()) + } + /// Snapshot the state of the blockchain at the current block. /// /// Handler for RPC call: `evm_snapshot` diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 12ae96dab..054aa5925 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -2440,6 +2440,76 @@ impl Backend { .lock() .retain(|tx| tx.unbounded_send(notification.clone()).is_ok()); } + + /// Reorg the chain to a common height and execute blocks to build new chain. + /// + /// The state of the chain is rewound using `rewind` to the common block, including the db, + /// storage, and env. + /// + /// Finally, `do_mine_block` is called to create the new chain. + pub async fn reorg( + &self, + depth: u64, + tx_pairs: HashMap>>, + common_block: Block, + ) -> Result<(), BlockchainError> { + // Get the database at the common block + let common_state = { + let mut state = self.states.write(); + let state_db = state + .get(&common_block.header.hash_slow()) + .ok_or(BlockchainError::DataUnavailable)?; + let db_full = state_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?; + db_full.clone() + }; + + { + // Set state to common state + self.db.write().await.clear(); + for (address, acc) in common_state { + for (key, value) in acc.storage { + self.db.write().await.set_storage_at(address, key, value)?; + } + self.db.write().await.insert_account(address, acc.info); + } + } + + { + // Unwind the storage back to the common ancestor + self.blockchain + .storage + .write() + .unwind_to(common_block.header.number, common_block.header.hash_slow()); + + // Set environment back to common block + let mut env = self.env.write(); + env.block = BlockEnv { + number: U256::from(common_block.header.number), + timestamp: U256::from(common_block.header.timestamp), + gas_limit: U256::from(common_block.header.gas_limit), + difficulty: common_block.header.difficulty, + prevrandao: Some(common_block.header.mix_hash), + coinbase: env.block.coinbase, + basefee: env.block.basefee, + ..env.block.clone() + }; + self.time.reset(env.block.timestamp.to::()); + } + + // Create the new reorged chain, filling the blocks with transactions if supplied + for i in 0..depth { + let to_be_mined = tx_pairs.get(&i).cloned().unwrap_or_else(Vec::new); + let outcome = self.do_mine_block(to_be_mined).await; + node_info!( + " Mined reorg block number {}. With {} valid txs and with invalid {} txs", + outcome.block_number, + outcome.included.len(), + outcome.invalid.len() + ); + } + + Ok(()) + } } /// Get max nonce from transaction pool by address diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index aaa2f29e7..2b5886a6f 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -271,6 +271,23 @@ impl BlockchainStorage { } } + /// Unwind the chain state back to the given block in storage. + /// + /// The block identified by `block_number` and `block_hash` is __non-inclusive__, i.e. it will + /// remain in the state. + pub fn unwind_to(&mut self, block_number: u64, block_hash: B256) { + let best_num: u64 = self.best_number.try_into().unwrap_or(0); + for i in (block_number + 1)..=best_num { + if let Some(hash) = self.hashes.remove(&U64::from(i)) { + if let Some(block) = self.blocks.remove(&hash) { + self.remove_block_transactions_by_number(block.header.number); + } + } + } + self.best_hash = block_hash; + self.best_number = U64::from(block_number); + } + #[allow(unused)] pub fn empty() -> Self { Self { diff --git a/crates/anvil/src/eth/pool/transactions.rs b/crates/anvil/src/eth/pool/transactions.rs index 502ee1e78..f0987572b 100644 --- a/crates/anvil/src/eth/pool/transactions.rs +++ b/crates/anvil/src/eth/pool/transactions.rs @@ -85,6 +85,14 @@ pub struct PoolTransaction { // == impl PoolTransaction == impl PoolTransaction { + pub fn new(transaction: PendingTransaction) -> Self { + Self { + pending_transaction: transaction, + requires: vec![], + provides: vec![], + priority: TransactionPriority(0), + } + } /// Returns the hash of this transaction pub fn hash(&self) -> TxHash { *self.pending_transaction.hash() @@ -121,7 +129,6 @@ impl TryFrom for PoolTransaction { }) } } - /// A waiting pool of transaction that are pending, but not yet ready to be included in a new block. /// /// Keeps a set of transactions that are waiting for other transactions diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index 1b94f5e43..1f1c48e21 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -1,20 +1,26 @@ //! tests for custom anvil endpoints use crate::{ - abi::{Greeter, MulticallContract, BUSD}, + abi::{self, Greeter, MulticallContract, BUSD}, fork::fork_config, utils::http_provider_with_signer, }; -use alloy_network::{EthereumWallet, TransactionBuilder}; -use alloy_primitives::{address, fixed_bytes, Address, U256}; +use alloy_consensus::{SignableTransaction, TxEip1559}; +use alloy_network::{EthereumWallet, TransactionBuilder, TxSignerSync}; +use alloy_primitives::{address, fixed_bytes, Address, Bytes, TxKind, U256}; use alloy_provider::{ext::TxPoolApi, Provider}; use alloy_rpc_types::{ - anvil::{ForkedNetwork, Forking, Metadata, NodeEnvironment, NodeForkConfig, NodeInfo}, + anvil::{ + ForkedNetwork, Forking, Metadata, MineOptions, NodeEnvironment, NodeForkConfig, NodeInfo, + }, BlockId, BlockNumberOrTag, TransactionRequest, }; use alloy_serde::WithOtherFields; use anvil::{eth::api::CLIENT_VERSION, spawn, EthereumHardfork, NodeConfig}; -use anvil_core::eth::EthRequest; +use anvil_core::{ + eth::EthRequest, + types::{ReorgOptions, TransactionData}, +}; use foundry_evm::revm::primitives::SpecId; use std::{ str::FromStr, @@ -658,3 +664,132 @@ async fn can_remove_pool_transactions() { let final_txs = provider.txpool_inspect().await.unwrap(); assert_eq!(final_txs.pending.len(), 0); } + +#[tokio::test(flavor = "multi_thread")] +async fn test_reorg() { + let (api, handle) = spawn(NodeConfig::test()).await; + let provider = handle.ws_provider(); + + let accounts = handle.dev_wallets().collect::>(); + + // Test calls + // Populate chain + for i in 0..10 { + let tx = TransactionRequest::default() + .to(accounts[0].address()) + .value(U256::from(i)) + .from(accounts[1].address()); + let tx = WithOtherFields::new(tx); + api.send_transaction(tx).await.unwrap(); + + let tx = TransactionRequest::default() + .to(accounts[1].address()) + .value(U256::from(i)) + .from(accounts[2].address()); + let tx = WithOtherFields::new(tx); + api.send_transaction(tx).await.unwrap(); + } + + // Define transactions + let mut txs = vec![]; + for i in 0..3 { + let from = accounts[i].address(); + let to = accounts[i + 1].address(); + for j in 0..5 { + let tx = TransactionRequest::default().from(from).to(to).value(U256::from(j)); + txs.push((TransactionData::JSON(tx), i as u64)); + } + } + + let prev_height = provider.get_block_number().await.unwrap(); + api.anvil_reorg(ReorgOptions { depth: 7, tx_block_pairs: txs }).await.unwrap(); + + let reorged_height = provider.get_block_number().await.unwrap(); + assert_eq!(reorged_height, prev_height); + + // The first 3 reorged blocks should have 5 transactions each + for num in 14..17 { + let block = provider.get_block_by_number(num.into(), true).await.unwrap(); + let block = block.unwrap(); + assert_eq!(block.transactions.len(), 5); + } + + // Verify that historic blocks are still accessible + for num in (0..14).rev() { + let _ = provider.get_block_by_number(num.into(), true).await.unwrap(); + } + + // Send a few more transaction to verify the chain can still progress + for i in 0..3 { + let tx = TransactionRequest::default() + .to(accounts[0].address()) + .value(U256::from(i)) + .from(accounts[1].address()); + let tx = WithOtherFields::new(tx); + api.send_transaction(tx).await.unwrap(); + } + + // Test reverting code + let greeter = abi::Greeter::deploy(provider.clone(), "Reorg".to_string()).await.unwrap(); + api.anvil_reorg(ReorgOptions { depth: 5, tx_block_pairs: vec![] }).await.unwrap(); + let code = api.get_code(*greeter.address(), Some(BlockId::latest())).await.unwrap(); + assert_eq!(code, Bytes::default()); + + // Test reverting contract storage + let storage = + abi::SimpleStorage::deploy(provider.clone(), "initial value".to_string()).await.unwrap(); + api.evm_mine(Some(MineOptions::Options { timestamp: None, blocks: Some(5) })).await.unwrap(); + let _ = storage + .setValue("ReorgMe".to_string()) + .from(accounts[0].address()) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + api.anvil_reorg(ReorgOptions { depth: 3, tx_block_pairs: vec![] }).await.unwrap(); + let value = storage.getValue().call().await.unwrap()._0; + assert_eq!("initial value".to_string(), value); + + api.mine_one().await; + api.mine_one().await; + + // Test raw transaction data + let mut tx = TxEip1559 { + chain_id: api.chain_id(), + to: TxKind::Call(accounts[1].address()), + value: U256::from(100), + max_priority_fee_per_gas: 1000000000000, + max_fee_per_gas: 10000000000000, + gas_limit: 21000, + ..Default::default() + }; + let signature = accounts[5].sign_transaction_sync(&mut tx).unwrap(); + let tx = tx.into_signed(signature); + let mut encoded = vec![]; + tx.tx().encode_with_signature(tx.signature(), &mut encoded, false); + + let pre_bal = provider.get_balance(accounts[5].address()).await.unwrap(); + api.anvil_reorg(ReorgOptions { + depth: 1, + tx_block_pairs: vec![(TransactionData::Raw(encoded.into()), 0)], + }) + .await + .unwrap(); + let post_bal = provider.get_balance(accounts[5].address()).await.unwrap(); + assert_ne!(pre_bal, post_bal); + + // Test reorg depth exceeding current height + let res = api.anvil_reorg(ReorgOptions { depth: 100, tx_block_pairs: vec![] }).await; + assert!(res.is_err()); + + // Test reorg tx pairs exceeds chain length + let res = api + .anvil_reorg(ReorgOptions { + depth: 1, + tx_block_pairs: vec![(TransactionData::JSON(TransactionRequest::default()), 10)], + }) + .await; + assert!(res.is_err()); +} From de596a4db781933f0c95805bd1c8c05e65f03d4f Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 5 Sep 2024 17:00:44 +0200 Subject: [PATCH 140/184] fix: ignore nonce in call evm env (#8815) --- crates/anvil/src/eth/backend/mem/mod.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 054aa5925..a9c54ba86 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -1125,6 +1125,13 @@ impl Backend { }).await? } + /// ## EVM settings + /// + /// This modifies certain EVM settings to mirror geth's `SkipAccountChecks` when transacting requests, see also: : + /// + /// - `disable_eip3607` is set to `true` + /// - `disable_base_fee` is set to `true` + /// - `nonce` is set to `None` fn build_call_env( &self, request: WithOtherFields, @@ -1139,10 +1146,11 @@ impl Backend { gas, value, input, - nonce, access_list, blob_versioned_hashes, authorization_list, + // nonce is always ignored for calls + nonce: _, sidecar: _, chain_id: _, transaction_type: _, @@ -1190,7 +1198,8 @@ impl Backend { value: value.unwrap_or_default(), data: input.into_input().unwrap_or_default(), chain_id: None, - nonce, + // set nonce to None so that the correct nonce is chosen by the EVM + nonce: None, access_list: access_list.unwrap_or_default().into(), blob_hashes: blob_versioned_hashes.unwrap_or_default(), optimism: OptimismFields { enveloped_tx: Some(Bytes::new()), ..Default::default() }, From 4f202da4ea6d94f18cbab8cab43ff4d7e0f6aeb2 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 6 Sep 2024 02:53:30 +0200 Subject: [PATCH 141/184] chore: ignore RUSTSEC-2024-0370 (#8821) --- deny.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deny.toml b/deny.toml index 18fcd5565..77cfc79a3 100644 --- a/deny.toml +++ b/deny.toml @@ -8,6 +8,8 @@ ignore = [ # https://github.com/watchexec/watchexec/issues/852 "RUSTSEC-2024-0350", "RUSTSEC-2024-0351", + # proc-macro-error is unmaintained + "RUSTSEC-2024-0370", ] # This section is considered when running `cargo deny check bans`. From da28b3125b08044004218cadefd9de450371414c Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 6 Sep 2024 02:56:28 +0200 Subject: [PATCH 142/184] chore: improve git/project root pathfinding (#8820) * chore: improve git/project root pathfinding * chore: clippy * fix * clean * fix --- crates/cast/bin/cmd/interface.rs | 6 +- crates/cli/src/opts/build/core.rs | 2 +- crates/cli/src/opts/build/paths.rs | 10 ++- crates/cli/src/utils/mod.rs | 6 +- crates/config/src/lib.rs | 27 ++++---- crates/config/src/macros.rs | 7 +- crates/config/src/utils.rs | 103 +++++++++++++++-------------- crates/forge/bin/cmd/build.rs | 2 +- crates/forge/bin/cmd/clone.rs | 2 +- crates/forge/bin/cmd/doc/mod.rs | 7 +- crates/forge/bin/main.rs | 2 +- crates/forge/tests/cli/config.rs | 6 +- 12 files changed, 93 insertions(+), 87 deletions(-) diff --git a/crates/cast/bin/cmd/interface.rs b/crates/cast/bin/cmd/interface.rs index caf9ac5dd..b8de8d84d 100644 --- a/crates/cast/bin/cmd/interface.rs +++ b/crates/cast/bin/cmd/interface.rs @@ -6,7 +6,7 @@ use foundry_block_explorers::Client; use foundry_cli::opts::EtherscanOpts; use foundry_common::{compile::ProjectCompiler, fs}; use foundry_compilers::{info::ContractInfo, utils::canonicalize}; -use foundry_config::{find_project_root_path, load_config_with_root, Config}; +use foundry_config::{load_config_with_root, try_find_project_root, Config}; use itertools::Itertools; use serde_json::Value; use std::{ @@ -118,8 +118,8 @@ fn load_abi_from_file(path: &str, name: Option) -> Result Result> { - let root = find_project_root_path(None)?; - let config = load_config_with_root(Some(root)); + let root = try_find_project_root(None)?; + let config = load_config_with_root(Some(&root)); let project = config.project()?; let compiler = ProjectCompiler::new().quiet(true); diff --git a/crates/cli/src/opts/build/core.rs b/crates/cli/src/opts/build/core.rs index d575f9ae8..ebb4da9f1 100644 --- a/crates/cli/src/opts/build/core.rs +++ b/crates/cli/src/opts/build/core.rs @@ -146,7 +146,7 @@ impl CoreBuildArgs { /// Returns the `Project` for the current workspace /// /// This loads the `foundry_config::Config` for the current workspace (see - /// `find_project_root_path` and merges the cli `BuildArgs` into it before returning + /// `find_project_root` and merges the cli `BuildArgs` into it before returning /// [`foundry_config::Config::project()`]). pub fn project(&self) -> Result> { let config = self.try_load_config_emit_warnings()?; diff --git a/crates/cli/src/opts/build/paths.rs b/crates/cli/src/opts/build/paths.rs index 9553eedb0..aa070800a 100644 --- a/crates/cli/src/opts/build/paths.rs +++ b/crates/cli/src/opts/build/paths.rs @@ -8,7 +8,7 @@ use foundry_config::{ value::{Dict, Map, Value}, Metadata, Profile, Provider, }, - find_project_root_path, remappings_from_env_var, Config, + find_project_root, remappings_from_env_var, Config, }; use serde::Serialize; use std::path::PathBuf; @@ -66,15 +66,13 @@ pub struct ProjectPathsArgs { impl ProjectPathsArgs { /// Returns the root directory to use for configuring the project. /// - /// This will be the `--root` argument if provided, otherwise see [find_project_root_path()] + /// This will be the `--root` argument if provided, otherwise see [`find_project_root`]. /// /// # Panics /// - /// If the project root directory cannot be found: [find_project_root_path()] + /// Panics if the project root directory cannot be found. See [`find_project_root`]. pub fn project_root(&self) -> PathBuf { - self.root - .clone() - .unwrap_or_else(|| find_project_root_path(None).expect("Failed to find project root")) + self.root.clone().unwrap_or_else(|| find_project_root(None)) } /// Returns the remappings to add to the config diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index 15864b016..f5bee0a77 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -191,9 +191,9 @@ pub fn load_dotenv() { }; // we only want the .env file of the cwd and project root - // `find_project_root_path` calls `current_dir` internally so both paths are either both `Ok` or + // `find_project_root` calls `current_dir` internally so both paths are either both `Ok` or // both `Err` - if let (Ok(cwd), Ok(prj_root)) = (std::env::current_dir(), find_project_root_path(None)) { + if let (Ok(cwd), Ok(prj_root)) = (std::env::current_dir(), try_find_project_root(None)) { load(&prj_root); if cwd != prj_root { // prj root and cwd can be identical @@ -602,7 +602,7 @@ mod tests { assert!(!p.is_sol_test()); } - // loads .env from cwd and project dir, See [`find_project_root_path()`] + // loads .env from cwd and project dir, See [`find_project_root()`] #[test] fn can_load_dotenv() { let temp = tempdir().unwrap(); diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 4dd6b8ce4..34ebe62f5 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -522,7 +522,7 @@ impl Config { /// Returns the current `Config` /// - /// See `Config::figment` + /// See [`figment`](Self::figment) for more details. #[track_caller] pub fn load() -> Self { Self::from_provider(Self::figment()) @@ -530,7 +530,7 @@ impl Config { /// Returns the current `Config` with the given `providers` preset /// - /// See `Config::to_figment` + /// See [`figment`](Self::figment) for more details. #[track_caller] pub fn load_with_providers(providers: FigmentProviders) -> Self { Self::default().to_figment(providers).extract().unwrap() @@ -538,10 +538,10 @@ impl Config { /// Returns the current `Config` /// - /// See `Config::figment_with_root` + /// See [`figment_with_root`](Self::figment_with_root) for more details. #[track_caller] - pub fn load_with_root(root: impl Into) -> Self { - Self::from_provider(Self::figment_with_root(root)) + pub fn load_with_root(root: impl AsRef) -> Self { + Self::from_provider(Self::figment_with_root(root.as_ref())) } /// Extract a `Config` from `provider`, panicking if extraction fails. @@ -1407,8 +1407,8 @@ impl Config { /// /// let my_config = Config::figment_with_root(".").extract::(); /// ``` - pub fn figment_with_root(root: impl Into) -> Figment { - Self::with_root(root).into() + pub fn figment_with_root(root: impl AsRef) -> Figment { + Self::with_root(root.as_ref()).into() } /// Creates a new Config that adds additional context extracted from the provided root. @@ -1419,10 +1419,13 @@ impl Config { /// use foundry_config::Config; /// let my_config = Config::with_root("."); /// ``` - pub fn with_root(root: impl Into) -> Self { + pub fn with_root(root: impl AsRef) -> Self { + Self::_with_root(root.as_ref()) + } + + fn _with_root(root: &Path) -> Self { // autodetect paths - let root = root.into(); - let paths = ProjectPathsConfig::builder().build_with_root::<()>(&root); + let paths = ProjectPathsConfig::builder().build_with_root::<()>(root); let artifacts: PathBuf = paths.artifacts.file_name().unwrap().into(); Self { root: paths.root.into(), @@ -1432,7 +1435,7 @@ impl Config { remappings: paths .remappings .into_iter() - .map(|r| RelativeRemapping::new(r, &root)) + .map(|r| RelativeRemapping::new(r, root)) .collect(), fs_permissions: FsPermissions::new([PathPermission::read(artifacts)]), ..Self::default() @@ -1481,7 +1484,7 @@ impl Config { /// /// **Note:** the closure will only be invoked if the `foundry.toml` file exists, See /// [Self::get_config_path()] and if the closure returns `true`. - pub fn update_at(root: impl Into, f: F) -> eyre::Result<()> + pub fn update_at(root: &Path, f: F) -> eyre::Result<()> where F: FnOnce(&Self, &mut toml_edit::DocumentMut) -> bool, { diff --git a/crates/config/src/macros.rs b/crates/config/src/macros.rs index d4f3a59ba..b9261f034 100644 --- a/crates/config/src/macros.rs +++ b/crates/config/src/macros.rs @@ -61,9 +61,8 @@ macro_rules! impl_figment_convert { impl<'a> From<&'a $name> for $crate::figment::Figment { fn from(args: &'a $name) -> Self { let root = args.root.clone() - .unwrap_or_else(|| $crate::find_project_root_path(None) - .unwrap_or_else(|e| panic!("could not find project root: {e}"))); - $crate::Config::figment_with_root(root).merge(args) + .unwrap_or_else(|| $crate::find_project_root(None)); + $crate::Config::figment_with_root(&root).merge(args) } } @@ -194,7 +193,7 @@ macro_rules! impl_figment_convert_cast { ($name:ty) => { impl<'a> From<&'a $name> for $crate::figment::Figment { fn from(args: &'a $name) -> Self { - $crate::Config::with_root($crate::find_project_root_path(None).unwrap()) + $crate::Config::with_root(&$crate::find_project_root(None)) .to_figment($crate::FigmentProviders::Cast) .merge(args) } diff --git a/crates/config/src/utils.rs b/crates/config/src/utils.rs index dff36e4fd..b1434bc7a 100644 --- a/crates/config/src/utils.rs +++ b/crates/config/src/utils.rs @@ -2,7 +2,6 @@ use crate::Config; use alloy_primitives::U256; -use eyre::WrapErr; use figment::value::Value; use foundry_compilers::artifacts::{ remappings::{Remapping, RemappingError}, @@ -11,6 +10,7 @@ use foundry_compilers::artifacts::{ use revm_primitives::SpecId; use serde::{de::Error, Deserialize, Deserializer}; use std::{ + io, path::{Path, PathBuf}, str::FromStr, }; @@ -21,36 +21,31 @@ pub fn load_config() -> Config { load_config_with_root(None) } -/// Loads the config for the current project workspace or the provided root path -pub fn load_config_with_root(root: Option) -> Config { - if let Some(root) = root { - Config::load_with_root(root) - } else { - Config::load_with_root(find_project_root_path(None).unwrap()) - } - .sanitized() +/// Loads the config for the current project workspace or the provided root path. +/// +/// # Panics +/// +/// Panics if the project root cannot be found. See [`find_project_root`]. +#[track_caller] +pub fn load_config_with_root(root: Option<&Path>) -> Config { + let root = match root { + Some(root) => root, + None => &find_project_root(None), + }; + Config::load_with_root(root).sanitized() } -/// Returns the path of the top-level directory of the working git tree. If there is no working -/// tree, an error is returned. -pub fn find_git_root_path(relative_to: impl AsRef) -> eyre::Result { - let path = relative_to.as_ref(); - let path = std::process::Command::new("git") - .args(["rev-parse", "--show-toplevel"]) - .current_dir(path) - .output() - .wrap_err_with(|| { - format!("Failed detect git root path in current dir: {}", path.display()) - })? - .stdout; - let path = std::str::from_utf8(&path)?.trim_end_matches('\n'); - Ok(PathBuf::from(path)) +/// Returns the path of the top-level directory of the working git tree. +pub fn find_git_root(relative_to: &Path) -> io::Result> { + let root = + if relative_to.is_absolute() { relative_to } else { &dunce::canonicalize(relative_to)? }; + Ok(root.ancestors().find(|p| p.join(".git").is_dir()).map(Path::to_path_buf)) } -/// Returns the root path to set for the project root +/// Returns the root path to set for the project root. /// -/// traverse the dir tree up and look for a `foundry.toml` file starting at the given path or cwd, -/// but only until the root dir of the current repo so that +/// Traverse the dir tree up and look for a `foundry.toml` file starting at the given path or cwd, +/// but only until the root dir of the current repo so that: /// /// ```text /// -- foundry.toml @@ -60,29 +55,37 @@ pub fn find_git_root_path(relative_to: impl AsRef) -> eyre::Result) -> std::io::Result { - let cwd = &std::env::current_dir()?; - let cwd = path.unwrap_or(cwd); - let boundary = find_git_root_path(cwd) - .ok() - .filter(|p| !p.as_os_str().is_empty()) - .unwrap_or_else(|| cwd.clone()); - let mut cwd = cwd.as_path(); - // traverse as long as we're in the current git repo cwd - while cwd.starts_with(&boundary) { - let file_path = cwd.join(Config::FILE_NAME); - if file_path.is_file() { - return Ok(cwd.to_path_buf()) - } - if let Some(parent) = cwd.parent() { - cwd = parent; - } else { - break - } - } - // no foundry.toml found - Ok(boundary) +/// +/// will still detect `repo` as root. +/// +/// Returns `repo` or `cwd` if no `foundry.toml` is found in the tree. +/// +/// # Panics +/// +/// Panics if: +/// - `cwd` is `Some` and is not a valid directory; +/// - `cwd` is `None` and the [`std::env::current_dir`] call fails. +#[track_caller] +pub fn find_project_root(cwd: Option<&Path>) -> PathBuf { + try_find_project_root(cwd).expect("Could not find project root") +} + +/// Returns the root path to set for the project root. +/// +/// Same as [`find_project_root`], but returns an error instead of panicking. +pub fn try_find_project_root(cwd: Option<&Path>) -> io::Result { + let cwd = match cwd { + Some(path) => path, + None => &std::env::current_dir()?, + }; + let boundary = find_git_root(cwd)?; + let found = cwd + .ancestors() + // Don't look outside of the git repo if it exists. + .take_while(|p| if let Some(boundary) = &boundary { p.starts_with(boundary) } else { true }) + .find(|p| p.join(Config::FILE_NAME).is_file()) + .map(Path::to_path_buf); + Ok(found.or(boundary).unwrap_or_else(|| cwd.to_path_buf())) } /// Returns all [`Remapping`]s contained in the `remappings` str separated by newlines @@ -160,7 +163,7 @@ pub fn foundry_toml_dirs(root: impl AsRef) -> Vec { .into_iter() .filter_map(Result::ok) .filter(|e| e.file_type().is_dir()) - .filter_map(|e| foundry_compilers::utils::canonicalize(e.path()).ok()) + .filter_map(|e| dunce::canonicalize(e.path()).ok()) .filter(|p| p.join(Config::FILE_NAME).exists()) .collect() } diff --git a/crates/forge/bin/cmd/build.rs b/crates/forge/bin/cmd/build.rs index f242d3d19..53bc5bc20 100644 --- a/crates/forge/bin/cmd/build.rs +++ b/crates/forge/bin/cmd/build.rs @@ -117,7 +117,7 @@ impl BuildArgs { /// Returns the `Project` for the current workspace /// /// This loads the `foundry_config::Config` for the current workspace (see - /// [`utils::find_project_root_path`] and merges the cli `BuildArgs` into it before returning + /// [`utils::find_project_root`] and merges the cli `BuildArgs` into it before returning /// [`foundry_config::Config::project()`] pub fn project(&self) -> Result { self.args.project() diff --git a/crates/forge/bin/cmd/clone.rs b/crates/forge/bin/cmd/clone.rs index 65b3cf01d..4af586964 100644 --- a/crates/forge/bin/cmd/clone.rs +++ b/crates/forge/bin/cmd/clone.rs @@ -546,7 +546,7 @@ fn dump_sources(meta: &Metadata, root: &PathBuf, no_reorg: bool) -> Result Result { +pub fn compile_project(root: &Path, quiet: bool) -> Result { let mut config = Config::load_with_root(root).sanitized(); config.extra_output.push(ContractOutputSelection::StorageLayout); let project = config.project()?; diff --git a/crates/forge/bin/cmd/doc/mod.rs b/crates/forge/bin/cmd/doc/mod.rs index ad4375bfb..ad61facf5 100644 --- a/crates/forge/bin/cmd/doc/mod.rs +++ b/crates/forge/bin/cmd/doc/mod.rs @@ -6,7 +6,7 @@ use forge_doc::{ }; use foundry_cli::opts::GH_REPO_PREFIX_REGEX; use foundry_common::compile::ProjectCompiler; -use foundry_config::{find_project_root_path, load_config_with_root, Config}; +use foundry_config::{find_project_root, load_config_with_root, Config}; use std::{path::PathBuf, process::Command}; mod server; @@ -139,7 +139,10 @@ impl DocArgs { } pub fn config(&self) -> Result { - let root = self.root.clone().unwrap_or(find_project_root_path(None)?); + let root = match &self.root { + Some(root) => root, + None => &find_project_root(None), + }; Ok(load_config_with_root(Some(root))) } } diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs index 81ee0b867..f92d3db80 100644 --- a/crates/forge/bin/main.rs +++ b/crates/forge/bin/main.rs @@ -81,7 +81,7 @@ fn main() -> Result<()> { Ok(()) } ForgeSubcommand::Clean { root } => { - let config = utils::load_config_with_root(root); + let config = utils::load_config_with_root(root.as_deref()); let project = config.project()?; config.cleanup(&project)?; Ok(()) diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 2cb11e72f..b597ce439 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -195,7 +195,7 @@ forgetest_init!(can_override_config, |prj, cmd| { // remappings work let remappings_txt = prj.create_file("remappings.txt", "ds-test/=lib/forge-std/lib/ds-test/from-file/"); - let config = forge_utils::load_config_with_root(Some(prj.root().into())); + let config = forge_utils::load_config_with_root(Some(prj.root())); assert_eq!( format!( "ds-test/={}/", @@ -206,7 +206,7 @@ forgetest_init!(can_override_config, |prj, cmd| { // env vars work std::env::set_var("DAPP_REMAPPINGS", "ds-test/=lib/forge-std/lib/ds-test/from-env/"); - let config = forge_utils::load_config_with_root(Some(prj.root().into())); + let config = forge_utils::load_config_with_root(Some(prj.root())); assert_eq!( format!( "ds-test/={}/", @@ -283,7 +283,7 @@ Installing solmate in [..] (url: Some("https://github.com/transmissions11/solmat "remappings.txt", "solmate/=lib/solmate/src/\nsolmate-contracts/=lib/solmate/src/", ); - let config = forge_utils::load_config_with_root(Some(prj.root().into())); + let config = forge_utils::load_config_with_root(Some(prj.root())); // trailing slashes are removed on windows `to_slash_lossy` let path = prj.root().join("lib/solmate/src/").to_slash_lossy().into_owned(); #[cfg(windows)] From 3998de0bdda218b8ecd3f871ac0d67c7a46e3528 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 6 Sep 2024 02:57:27 +0200 Subject: [PATCH 143/184] chore: delay spawning tokio runtime until needed (#8819) --- Cargo.lock | 122 +++++++++++++-------------- crates/anvil/src/anvil.rs | 13 ++- crates/cast/Cargo.toml | 5 +- crates/cast/bin/{opts.rs => args.rs} | 0 crates/cast/bin/cmd/storage.rs | 2 +- crates/cast/bin/main.rs | 19 +++-- crates/chisel/bin/main.rs | 9 +- 7 files changed, 87 insertions(+), 83 deletions(-) rename crates/cast/bin/{opts.rs => args.rs} (100%) diff --git a/Cargo.lock b/Cargo.lock index 846989c41..7130c6139 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1855,65 +1855,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" -[[package]] -name = "cast" -version = "0.2.0" -dependencies = [ - "alloy-chains", - "alloy-consensus", - "alloy-contract", - "alloy-dyn-abi", - "alloy-json-abi", - "alloy-json-rpc", - "alloy-network", - "alloy-primitives", - "alloy-provider", - "alloy-rlp", - "alloy-rpc-types", - "alloy-serde", - "alloy-signer", - "alloy-signer-local", - "alloy-sol-types", - "alloy-transport", - "anvil", - "async-trait", - "aws-sdk-kms", - "chrono", - "clap", - "clap_complete", - "clap_complete_fig", - "comfy-table", - "criterion", - "dunce", - "evm-disassembler", - "evmole", - "eyre", - "foundry-block-explorers", - "foundry-cli", - "foundry-common", - "foundry-compilers", - "foundry-config", - "foundry-evm", - "foundry-test-utils", - "foundry-wallets", - "futures", - "indicatif", - "itertools 0.13.0", - "rand", - "rayon", - "regex", - "rpassword", - "semver 1.0.23", - "serde", - "serde_json", - "tempfile", - "tikv-jemallocator", - "tokio", - "tracing", - "vergen", - "yansi", -] - [[package]] name = "cast" version = "0.3.0" @@ -2368,7 +2309,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" dependencies = [ "anes", - "cast 0.3.0", + "cast", "ciborium", "clap", "criterion-plot", @@ -2395,7 +2336,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ - "cast 0.3.0", + "cast", "itertools 0.10.5", ] @@ -3527,6 +3468,65 @@ dependencies = [ "tracing", ] +[[package]] +name = "foundry-cast" +version = "0.2.0" +dependencies = [ + "alloy-chains", + "alloy-consensus", + "alloy-contract", + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-json-rpc", + "alloy-network", + "alloy-primitives", + "alloy-provider", + "alloy-rlp", + "alloy-rpc-types", + "alloy-serde", + "alloy-signer", + "alloy-signer-local", + "alloy-sol-types", + "alloy-transport", + "anvil", + "async-trait", + "aws-sdk-kms", + "chrono", + "clap", + "clap_complete", + "clap_complete_fig", + "comfy-table", + "criterion", + "dunce", + "evm-disassembler", + "evmole", + "eyre", + "foundry-block-explorers", + "foundry-cli", + "foundry-common", + "foundry-compilers", + "foundry-config", + "foundry-evm", + "foundry-test-utils", + "foundry-wallets", + "futures", + "indicatif", + "itertools 0.13.0", + "rand", + "rayon", + "regex", + "rpassword", + "semver 1.0.23", + "serde", + "serde_json", + "tempfile", + "tikv-jemallocator", + "tokio", + "tracing", + "vergen", + "yansi", +] + [[package]] name = "foundry-cheatcodes" version = "0.2.0" diff --git a/crates/anvil/src/anvil.rs b/crates/anvil/src/anvil.rs index 1aa204587..38bbbbbd6 100644 --- a/crates/anvil/src/anvil.rs +++ b/crates/anvil/src/anvil.rs @@ -33,14 +33,13 @@ pub enum AnvilSubcommand { GenerateFigSpec, } -#[tokio::main] -async fn main() -> eyre::Result<()> { +fn main() -> eyre::Result<()> { utils::load_dotenv(); - let mut app = Anvil::parse(); - app.node.evm_opts.resolve_rpc_alias(); + let mut args = Anvil::parse(); + args.node.evm_opts.resolve_rpc_alias(); - if let Some(ref cmd) = app.cmd { + if let Some(cmd) = &args.cmd { match cmd { AnvilSubcommand::Completions { shell } => { clap_complete::generate( @@ -61,9 +60,7 @@ async fn main() -> eyre::Result<()> { } let _ = fdlimit::raise_fd_limit(); - app.node.run().await?; - - Ok(()) + tokio::runtime::Builder::new_multi_thread().enable_all().build()?.block_on(args.node.run()) } #[cfg(test)] diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 386c523b4..2b2a0db90 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "cast" +name = "foundry-cast" description = "Command-line tool for performing Ethereum RPC calls" version.workspace = true @@ -13,6 +13,9 @@ repository.workspace = true [lints] workspace = true +[lib] +name = "cast" + [[bin]] name = "cast" path = "bin/main.rs" diff --git a/crates/cast/bin/opts.rs b/crates/cast/bin/args.rs similarity index 100% rename from crates/cast/bin/opts.rs rename to crates/cast/bin/args.rs diff --git a/crates/cast/bin/cmd/storage.rs b/crates/cast/bin/cmd/storage.rs index 117130943..a8ced3639 100644 --- a/crates/cast/bin/cmd/storage.rs +++ b/crates/cast/bin/cmd/storage.rs @@ -1,4 +1,4 @@ -use crate::opts::parse_slot; +use crate::args::parse_slot; use alloy_network::AnyNetwork; use alloy_primitives::{Address, B256, U256}; use alloy_provider::Provider; diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 8eaa201d6..b0f29af43 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -23,25 +23,28 @@ use foundry_common::{ use foundry_config::Config; use std::time::Instant; +pub mod args; pub mod cmd; -pub mod opts; pub mod tx; -use opts::{Cast as Opts, CastSubcommand, ToBaseArgs}; +use args::{Cast as CastArgs, CastSubcommand, ToBaseArgs}; #[cfg(all(feature = "jemalloc", unix))] #[global_allocator] static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; -#[tokio::main] -async fn main() -> Result<()> { +fn main() -> Result<()> { handler::install(); utils::load_dotenv(); utils::subscriber(); utils::enable_paint(); + let args = CastArgs::parse(); + main_args(args) +} - let opts = Opts::parse(); - match opts.cmd { +#[tokio::main] +async fn main_args(args: CastArgs) -> Result<()> { + match args.cmd { // Constants CastSubcommand::MaxInt { r#type } => { println!("{}", SimpleCast::max_int(&r#type)?); @@ -555,11 +558,11 @@ async fn main() -> Result<()> { } CastSubcommand::Wallet { command } => command.run().await?, CastSubcommand::Completions { shell } => { - generate(shell, &mut Opts::command(), "cast", &mut std::io::stdout()) + generate(shell, &mut CastArgs::command(), "cast", &mut std::io::stdout()) } CastSubcommand::GenerateFigSpec => clap_complete::generate( clap_complete_fig::Fig, - &mut Opts::command(), + &mut CastArgs::command(), "cast", &mut std::io::stdout(), ), diff --git a/crates/chisel/bin/main.rs b/crates/chisel/bin/main.rs index d2b3a726f..7a0703e85 100644 --- a/crates/chisel/bin/main.rs +++ b/crates/chisel/bin/main.rs @@ -94,15 +94,16 @@ pub enum ChiselSubcommand { ClearCache, } -#[tokio::main] -async fn main() -> eyre::Result<()> { +fn main() -> eyre::Result<()> { handler::install(); utils::subscriber(); utils::load_dotenv(); - - // Parse command args let args = Chisel::parse(); + main_args(args) +} +#[tokio::main] +async fn main_args(args: Chisel) -> eyre::Result<()> { // Keeps track of whether or not an interrupt was the last input let mut interrupt = false; From e53255b904a8533e65541b3b8d9d584a540ff4e3 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 6 Sep 2024 16:46:52 +0200 Subject: [PATCH 144/184] feat: solc 0.8.27 support (#8825) --- Cargo.lock | 4 ++-- crates/forge/tests/cli/svm.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7130c6139..aced1eab7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8135,9 +8135,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "svm-rs" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00d3230221bec82c4a79cd7af637ac29f04f369e95e476bc492f22882bb83c91" +checksum = "4aebac1b1ef2b46e2e2bdf3c09db304800f2a77c1fa902bd5231490203042be8" dependencies = [ "const-hex", "dirs 5.0.1", diff --git a/crates/forge/tests/cli/svm.rs b/crates/forge/tests/cli/svm.rs index ab8db41f8..e0c10a052 100644 --- a/crates/forge/tests/cli/svm.rs +++ b/crates/forge/tests/cli/svm.rs @@ -11,7 +11,7 @@ use svm::Platform; /// 3. svm bumped in foundry-compilers /// 4. foundry-compilers update with any breaking changes /// 5. upgrade the `LATEST_SOLC` -const LATEST_SOLC: Version = Version::new(0, 8, 26); +const LATEST_SOLC: Version = Version::new(0, 8, 27); macro_rules! ensure_svm_releases { ($($test:ident => $platform:ident),* $(,)?) => {$( From d8f6631f008c90cf880c9bf25cb8c31078f403f4 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Fri, 6 Sep 2024 20:29:13 +0530 Subject: [PATCH 145/184] chore(deps): bump alloy (#8771) * chore(deps): bump alloy * bump foundry-fork-db * bump foundry-compilers + revm * fix(fmt): rm other from `UIfmt` of `Transaction` * use patch foundry-compilers and alloy * make utils::apply_chain_and_block_specific_env_changes generic over Network * make EvmOpts generic over network * bump block explorers * use patched core version * nit * nit * squash * repatch * anvil: use WithOtherFields> * fix(anvil): otterscan * fix(anvil): TaskManager * breaking(anvil): change anvil-api Block return types to WithOtherFields> * impl UIfmt for WithOtherFields * fix cast * fix anvil tests * nit * nits * fix: UIfmt for WithOtherFields * fix(casttest): interface_no_constructor * fix UIfmt for WithOtherFields * nits + allow - evmole in deny * bump evmole * use AnyNetworkBlock * use WithOtherFields in otterscan return types * bump core to 0.8.1 * Update crates/anvil/src/eth/api.rs --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- Cargo.lock | 541 +++++++++--------- Cargo.toml | 125 ++-- crates/anvil/Cargo.toml | 1 + crates/anvil/core/src/eth/transaction/mod.rs | 5 - .../core/src/eth/transaction/optimism.rs | 38 ++ crates/anvil/src/config.rs | 23 +- crates/anvil/src/eth/api.rs | 44 +- crates/anvil/src/eth/backend/fork.rs | 67 ++- crates/anvil/src/eth/backend/info.rs | 8 +- crates/anvil/src/eth/backend/mem/mod.rs | 63 +- crates/anvil/src/eth/error.rs | 2 +- crates/anvil/src/eth/otterscan/api.rs | 46 +- crates/anvil/src/tasks/mod.rs | 8 +- crates/anvil/tests/it/abi.rs | 2 +- crates/anvil/tests/it/anvil_api.rs | 24 +- crates/anvil/tests/it/api.rs | 86 ++- crates/anvil/tests/it/fork.rs | 21 +- crates/anvil/tests/it/ipc.rs | 2 +- crates/anvil/tests/it/logs.rs | 6 +- crates/anvil/tests/it/optimism.rs | 6 +- crates/anvil/tests/it/otterscan.rs | 26 +- crates/anvil/tests/it/pubsub.rs | 8 +- crates/anvil/tests/it/traces.rs | 6 +- crates/anvil/tests/it/transaction.rs | 11 +- crates/cast/Cargo.toml | 2 +- crates/cast/bin/cmd/run.rs | 8 +- crates/cast/bin/main.rs | 15 +- crates/cast/src/lib.rs | 6 +- crates/cast/tests/cli/main.rs | 27 +- crates/common/fmt/src/ui.rs | 47 +- crates/evm/core/src/backend/mod.rs | 73 ++- crates/evm/core/src/fork/init.rs | 27 +- crates/evm/core/src/fork/multi.rs | 3 +- crates/evm/core/src/opts.rs | 4 +- crates/evm/core/src/utils.rs | 27 +- crates/forge/bin/cmd/clone.rs | 4 +- crates/forge/tests/cli/cmd.rs | 10 +- crates/forge/tests/cli/verify_bytecode.rs | 6 +- crates/test-utils/Cargo.toml | 4 + crates/test-utils/src/rpc.rs | 68 ++- crates/verify/src/bytecode.rs | 2 +- crates/verify/src/utils.rs | 4 +- 42 files changed, 822 insertions(+), 684 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aced1eab7..6793346b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,8 +85,8 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-eips", "alloy-primitives", @@ -98,8 +98,8 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -118,9 +118,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.7.7" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413902aa18a97569e60f679c23f46a18db1656d87ab4d4e49d0e1e52042f66df" +checksum = "6d1c7d5315d44b5e00ce9c6b72dc88aa8f3e49d82a742be85d6a755ef0aa28c5" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -129,7 +129,7 @@ dependencies = [ "arbitrary", "const-hex", "derive_arbitrary", - "derive_more 0.99.18", + "derive_more 1.0.0", "itoa", "proptest", "serde", @@ -137,28 +137,55 @@ dependencies = [ "winnow", ] +[[package]] +name = "alloy-eip2930" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0069cf0642457f87a01a014f6dc29d5d893cd4fd8fddf0c3cdfad1bb3ebafc41" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "arbitrary", + "rand", + "serde", +] + +[[package]] +name = "alloy-eip7702" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d319bb544ca6caeab58c39cea8921c55d924d4f68f2c60f24f914673f9a74a" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "arbitrary", + "k256", + "rand", + "serde", +] + [[package]] name = "alloy-eips" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ + "alloy-eip2930", + "alloy-eip7702", "alloy-primitives", "alloy-rlp", "alloy-serde", "arbitrary", "c-kzg", "derive_more 1.0.0", - "k256", "once_cell", - "rand", "serde", "sha2", ] [[package]] name = "alloy-genesis" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-primitives", "alloy-serde", @@ -167,9 +194,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.7.7" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc05b04ac331a9f07e3a4036ef7926e49a8bf84a99a1ccfc7e2ab55a5fcbb372" +checksum = "8129590e6f5aa3aeb33138f0faaa4cb763e4f1ca072ab98ce880a1998dca84d3" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -179,8 +206,8 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -192,8 +219,8 @@ dependencies = [ [[package]] name = "alloy-network" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-consensus", "alloy-eips", @@ -212,8 +239,8 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-primitives", "alloy-serde", @@ -222,9 +249,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.7.7" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccb3ead547f4532bc8af961649942f0b9c16ee9226e26caa3f38420651cc0bf4" +checksum = "19d1d1bdfba287209ee749a985230685c2ff8bdabe5fed6f699f72f22bb72c95" dependencies = [ "alloy-rlp", "arbitrary", @@ -232,8 +259,7 @@ dependencies = [ "cfg-if", "const-hex", "derive_arbitrary", - "derive_more 0.99.18", - "ethereum_ssz", + "derive_more 1.0.0", "getrandom", "hex-literal", "itoa", @@ -249,8 +275,8 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-chains", "alloy-consensus", @@ -287,8 +313,8 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -299,7 +325,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower", + "tower 0.4.13", "tracing", ] @@ -327,8 +353,8 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -344,15 +370,15 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower", + "tower 0.4.13", "tracing", "url", ] [[package]] name = "alloy-rpc-types" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-rpc-types-anvil", "alloy-rpc-types-engine", @@ -365,8 +391,8 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-primitives", "alloy-serde", @@ -375,8 +401,8 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-consensus", "alloy-eips", @@ -392,8 +418,8 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-consensus", "alloy-eips", @@ -410,8 +436,8 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -423,8 +449,8 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -434,8 +460,8 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-primitives", "arbitrary", @@ -445,8 +471,8 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -460,8 +486,8 @@ dependencies = [ [[package]] name = "alloy-signer-aws" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-consensus", "alloy-network", @@ -477,8 +503,8 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-consensus", "alloy-network", @@ -494,8 +520,8 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -513,8 +539,8 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-consensus", "alloy-network", @@ -532,8 +558,8 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-consensus", "alloy-network", @@ -548,13 +574,13 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.7.7" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b40397ddcdcc266f59f959770f601ce1280e699a91fc1862f29cef91707cd09" +checksum = "03f6a68cb762b6feb50cff1a4f81831b8725aa10f5a1afc26c209b7df442fffe" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", - "proc-macro-error", + "proc-macro-error2", "proc-macro2", "quote", "syn 2.0.77", @@ -562,16 +588,16 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "0.7.7" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "867a5469d61480fea08c7333ffeca52d5b621f5ca2e44f271b117ec1fc9a0525" +checksum = "1e3d38952d1f54a541c00b1b28582cd112ac02e6302437bd3dead32367a87393" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", "const-hex", "heck", "indexmap 2.5.0", - "proc-macro-error", + "proc-macro-error2", "proc-macro2", "quote", "syn 2.0.77", @@ -581,9 +607,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.7.7" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e482dc33a32b6fadbc0f599adea520bd3aaa585c141a80b404d0a3e3fa72528" +checksum = "2821c0ce9b217b17c9a52c8dc0dddadb391d0eb7593707da9f12df786510594b" dependencies = [ "alloy-json-abi", "const-hex", @@ -598,9 +624,9 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.7.7" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbcba3ca07cf7975f15d871b721fb18031eec8bce51103907f6dcce00b255d98" +checksum = "f040419382164c509aa60f281e45c6fa295cf88ccffe686bd71d65780bc8d053" dependencies = [ "serde", "winnow", @@ -608,9 +634,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.7.7" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91ca40fa20793ae9c3841b83e74569d1cc9af29a2f5237314fd3452d51e38c7" +checksum = "53be2dde91d22bc1e398e26bd42bdac441705f205a4cbaf4f3abb9273867ed39" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -621,8 +647,8 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -632,29 +658,29 @@ dependencies = [ "serde_json", "thiserror", "tokio", - "tower", + "tower 0.4.13", "tracing", "url", ] [[package]] name = "alloy-transport-http" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-json-rpc", "alloy-transport", "reqwest", "serde_json", - "tower", + "tower 0.4.13", "tracing", "url", ] [[package]] name = "alloy-transport-ipc" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -673,8 +699,8 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "0.2.1" -source = "git+https://github.com/alloy-rs/alloy?rev=511ae98#511ae9820210798d4a35f1e28a7993bb5d445cd4" +version = "0.3.1" +source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -690,13 +716,13 @@ dependencies = [ [[package]] name = "alloy-trie" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03704f265cbbb943b117ecb5055fd46e8f41e7dc8a58b1aed20bcd40ace38c15" +checksum = "398a977d774db13446b8cead8cfa9517aebf9e03fc8a1512892dc1e03e70bb04" dependencies = [ "alloy-primitives", "alloy-rlp", - "derive_more 0.99.18", + "derive_more 1.0.0", "hashbrown 0.14.5", "nybbles", "serde", @@ -838,6 +864,7 @@ dependencies = [ "hyper 1.4.1", "itertools 0.13.0", "k256", + "op-alloy-rpc-types", "parking_lot", "rand", "revm", @@ -849,7 +876,7 @@ dependencies = [ "thiserror", "tikv-jemallocator", "tokio", - "tower", + "tower 0.4.13", "tracing", "tracing-subscriber", "vergen", @@ -1264,9 +1291,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.41.0" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "178910fefe72743b62b9c4670c14a038ebfdb265ff7feccf43827af6a8899e14" +checksum = "704ab31904cf70104a3bb023079e201b1353cf132ca674b26ba6f23acbbb53c9" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1286,9 +1313,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.40.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5879bec6e74b648ce12f6085e7245417bc5f6d672781028384d2e494be3eb6d" +checksum = "af0a3f676cba2c079c9563acc9233998c8951cdbe38629a0bef3c8c1b02f3658" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1308,9 +1335,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.41.0" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ef4cd9362f638c22a3b959fd8df292e7e47fdf170270f86246b97109b5f2f7d" +checksum = "c91b6a04495547162cf52b075e3c15a17ab6608bf9c5785d3e5a5509b3f09f5c" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1330,9 +1357,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.40.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b1e2735d2ab28b35ecbb5496c9d41857f52a0d6a0075bbf6a8af306045ea6f6" +checksum = "99c56bcd6a56cab7933980a54148b476a5a69a7694e3874d9aa2a566f150447d" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1545,7 +1572,7 @@ dependencies = [ "sync_wrapper 1.0.1", "tokio", "tokio-tungstenite 0.21.0", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", "tracing", @@ -1686,19 +1713,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "bls12_381" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7bc6d6292be3a19e6379786dac800f551e5865a5bb51ebbe3064ab80433f403" -dependencies = [ - "ff", - "group", - "pairing", - "rand_core", - "subtle", -] - [[package]] name = "blst" version = "0.3.13" @@ -1752,9 +1766,9 @@ checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "bytemuck" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773d90827bc3feecfb67fab12e24de0749aad83c74b9504ecde46237b5cd24e2" +checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" [[package]] name = "byteorder" @@ -1872,9 +1886,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.15" +version = "1.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" +checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b" dependencies = [ "shlex", ] @@ -1987,9 +2001,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.16" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" dependencies = [ "clap_builder", "clap_derive", @@ -1997,9 +2011,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.15" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" dependencies = [ "anstream", "anstyle", @@ -2012,9 +2026,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.24" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7db6eca8c205649e8d3ccd05aa5042b1800a784e56bc7c43524fde8abbfa9b" +checksum = "205d5ef6d485fa47606b98b0ddc4ead26eb850aaa86abfb562a94fb3280ecba0" dependencies = [ "clap", ] @@ -2253,12 +2267,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - [[package]] name = "convert_case" version = "0.6.0" @@ -2496,9 +2504,9 @@ dependencies = [ [[package]] name = "dashmap" -version = "6.0.1" +version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804c8821570c3f8b70230c2ba75ffa5c0f9a4189b9a432b6656c536712acae28" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" dependencies = [ "cfg-if", "crossbeam-utils", @@ -2604,10 +2612,8 @@ version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ - "convert_case 0.4.0", "proc-macro2", "quote", - "rustc_version 0.4.1", "syn 2.0.77", ] @@ -2626,7 +2632,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ - "convert_case 0.6.0", + "convert_case", "proc-macro2", "quote", "syn 2.0.77", @@ -2982,17 +2988,6 @@ dependencies = [ "uint", ] -[[package]] -name = "ethereum_ssz" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d3627f83d8b87b432a5fad9934b4565260722a141a2c40f371f8080adec9425" -dependencies = [ - "ethereum-types", - "itertools 0.10.5", - "smallvec", -] - [[package]] name = "ethers-contract-abigen" version = "2.0.14" @@ -3068,12 +3063,12 @@ dependencies = [ [[package]] name = "evmole" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d55794094e85dd9d2a4a44de6554015c7d0d7193a28756f2ee3432bb763003a7" +checksum = "2dc8472e812ff8f53a76946fa70b1cc4bf75c78755beb09df4d1376764769c7d" dependencies = [ "alloy-dyn-abi", - "ruint", + "alloy-primitives", ] [[package]] @@ -3136,7 +3131,6 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ - "bitvec", "rand_core", "subtle", ] @@ -3175,7 +3169,6 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ - "arbitrary", "byteorder", "rand", "rustc-hex", @@ -3452,9 +3445,9 @@ dependencies = [ [[package]] name = "foundry-block-explorers" -version = "0.5.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1580bdb99a6a531b44ac5cda229069cacc11ae7d54faa45676e1bee9ee7da1c" +checksum = "461772f04e5457d6b6501f6aff3a615a032d01368c1cc91c13a06eff172962b6" dependencies = [ "alloy-chains", "alloy-json-abi", @@ -3658,7 +3651,7 @@ dependencies = [ "similar-asserts", "thiserror", "tokio", - "tower", + "tower 0.4.13", "tracing", "url", "walkdir", @@ -3686,9 +3679,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.10.3" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eaa24a47bb84e1db38c84f03e8c90ca81050bd20beac8bdc99aae8afd0b8784" +checksum = "3372aaa89b1653b61fb297dbc24e74ad727ff76cc4415f1a0ec5f802d24e0797" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3724,9 +3717,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" -version = "0.10.3" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3588ee6a986f89040d1158fb90459731580b404fb72b8c6c832c0ddbc95fed58" +checksum = "76c4f9ac0ed5e695bbeb48ff0758ba31ce3d0d52b752aaa466f311f48ed772c0" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3734,9 +3727,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" -version = "0.10.3" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a149c5e8c326c7bae8f73cacb28c637f4bc2e535f950eec10348494990e9636f" +checksum = "5ad6beeb057a8a58993d13841cffcb99e8aefdeb52ed9b368c85518747b467f9" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3758,9 +3751,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" -version = "0.10.3" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8645c9e7c070c81bf8c90f456416953234334f097b67445c773af98df74e27b0" +checksum = "442e5eb231aad523f0f3e26f9475ad9eab1aa2173a5991df1b7fa51f589f309f" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3773,9 +3766,9 @@ dependencies = [ [[package]] name = "foundry-compilers-core" -version = "0.10.3" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66492aeb708f3d142c078457dba5f52b04ca5031012d48903a0bcb37d205d595" +checksum = "92049644ce2745c36be16f6406592155d4be2314306af2543c7d30a32b00d6ed" dependencies = [ "alloy-primitives", "cfg-if", @@ -3993,9 +3986,9 @@ dependencies = [ [[package]] name = "foundry-fork-db" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e1217b5063138a87feb51bd9ac71857d370f06f1aa3d8c22b73aae0e49f4c3" +checksum = "c88cb03fc4bd87856fc4d0ad38fd067f85c7c6306bf794202fc50a897449837b" dependencies = [ "alloy-primitives", "alloy-provider", @@ -4043,6 +4036,7 @@ dependencies = [ "alloy-provider", "eyre", "fd-lock", + "foundry-block-explorers", "foundry-common", "foundry-compilers", "foundry-config", @@ -4051,6 +4045,7 @@ dependencies = [ "regex", "serde_json", "snapbox", + "tokio", "tracing", "tracing-subscriber", ] @@ -4230,9 +4225,9 @@ checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" [[package]] name = "gcloud-sdk" -version = "0.25.5" +version = "0.25.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77045256cd0d2075e09d62c4c9f27c2b664e2cc806d7ddf3a4293bb0c20b4728" +checksum = "6d92f38cbe5b8796d2ab3f3c5f3bc286aa778015d5c5f67e2d0cbbe5d348473f" dependencies = [ "async-trait", "bytes", @@ -4249,7 +4244,7 @@ dependencies = [ "serde_json", "tokio", "tonic", - "tower", + "tower 0.5.0", "tower-layer", "tower-util", "tracing", @@ -4258,9 +4253,9 @@ dependencies = [ [[package]] name = "generator" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "979f00864edc7516466d6b3157706e06c032f22715700ddd878228a91d02bc56" +checksum = "dbb949699c3e4df3a183b1d2142cb24277057055ed23c68ed58894f76c517223" dependencies = [ "cfg-if", "libc", @@ -4438,9 +4433,9 @@ dependencies = [ [[package]] name = "gix-path" -version = "0.10.10" +version = "0.10.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d5b8722112fa2fa87135298780bc833b0e9f6c56cc82795d209804b3a03484" +checksum = "ebfc4febd088abdcbc9f1246896e57e37b7a34f6909840045a1767c6dafac7af" dependencies = [ "bstr", "gix-trace", @@ -4498,9 +4493,9 @@ dependencies = [ [[package]] name = "gix-trace" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f924267408915fddcd558e3f37295cc7d6a3e50f8bd8b606cee0808c3915157e" +checksum = "6cae0e8661c3ff92688ce1c8b8058b3efb312aba9492bbe93661a21705ab431b" [[package]] name = "gix-utils" @@ -4861,16 +4856,16 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.2" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", "http 1.1.0", "hyper 1.4.1", "hyper-util", "rustls 0.23.12", - "rustls-native-certs 0.7.3", + "rustls-native-certs 0.8.0", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -4922,7 +4917,7 @@ dependencies = [ "pin-project-lite", "socket2", "tokio", - "tower", + "tower 0.4.13", "tower-service", "tracing", ] @@ -5291,21 +5286,6 @@ dependencies = [ "libc", ] -[[package]] -name = "kzg-rs" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9920cd4460ce3cbca19c62f3bb9a9611562478a4dc9d2c556f4a7d049c5b6b" -dependencies = [ - "bls12_381", - "glob", - "hex", - "once_cell", - "serde", - "serde_derive", - "serde_yaml", -] - [[package]] name = "lalrpop" version = "0.20.2" @@ -5967,6 +5947,37 @@ version = "11.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" +[[package]] +name = "op-alloy-consensus" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b7fbb0f5c3754c22c6ea30e100dca6aea73b747e693e27763e23ca92fb02f2f" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "derive_more 1.0.0", + "serde", + "spin", +] + +[[package]] +name = "op-alloy-rpc-types" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1fbb93dcb71aba9cd555784375011efce1fdaaea67e01972a0a9bc9eb90c626" +dependencies = [ + "alloy-network", + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "op-alloy-consensus", + "serde", + "serde_json", +] + [[package]] name = "open-fastrlp" version = "0.1.4" @@ -6084,15 +6095,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "pairing" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" -dependencies = [ - "group", -] - [[package]] name = "parity-scale-codec" version = "3.6.12" @@ -6578,6 +6580,28 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74cdd32837fa2e86ec09c8266e5aad92400ac934c6dbca83d54673b298db3e45" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "proc-macro2" version = "1.0.86" @@ -6653,13 +6677,13 @@ dependencies = [ [[package]] name = "proptest-derive" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf16337405ca084e9c78985114633b6827711d22b9e6ef6c6c0d665eb3f0b6e" +checksum = "6ff7ff745a347b87471d859a377a9a404361e7efc2a971d73424a6d183c0fc77" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.77", ] [[package]] @@ -6749,9 +6773,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.3" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b22d8e7369034b9a7132bc2008cac12f2013c8132b45e0554e6e20e2617f2156" +checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" dependencies = [ "bytes", "pin-project-lite", @@ -6767,9 +6791,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba92fb39ec7ad06ca2582c0ca834dfeadcaf06ddfc8e635c80aa7e1c05315fdd" +checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" dependencies = [ "bytes", "rand", @@ -6784,15 +6808,15 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bffec3605b73c6f1754535084a85229fa8a30f86014e6c81aeec4abb68b0285" +checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" dependencies = [ "libc", "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -6992,7 +7016,7 @@ dependencies = [ "http-body 1.0.1", "http-body-util", "hyper 1.4.1", - "hyper-rustls 0.27.2", + "hyper-rustls 0.27.3", "hyper-tls", "hyper-util", "ipnet", @@ -7030,8 +7054,9 @@ dependencies = [ [[package]] name = "revm" -version = "13.0.0" -source = "git+https://github.com/bluealloy/revm?rev=caadc71#caadc71fd28929cd751cc997b4b454d8813c434b" +version = "14.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f719e28cc6fdd086f8bc481429e587740d20ad89729cec3f5f5dd7b655474df" dependencies = [ "auto_impl", "cfg-if", @@ -7044,9 +7069,9 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.5.7" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec16f9b9d3cdaaf2f4b7ceaf004eb2c89df04e7ea29622584c0a6ec676bd0a83" +checksum = "48184032103bb23788e42e42c7c85207f5b0b8a248b09ea8f5233077f35ab56e" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -7061,8 +7086,9 @@ dependencies = [ [[package]] name = "revm-interpreter" -version = "9.0.0" -source = "git+https://github.com/bluealloy/revm?rev=caadc71#caadc71fd28929cd751cc997b4b454d8813c434b" +version = "10.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "959ecbc36802de6126852479844737f20194cf8e6718e0c30697d306a2cca916" dependencies = [ "revm-primitives", "serde", @@ -7070,8 +7096,9 @@ dependencies = [ [[package]] name = "revm-precompile" -version = "10.0.0" -source = "git+https://github.com/bluealloy/revm?rev=caadc71#caadc71fd28929cd751cc997b4b454d8813c434b" +version = "11.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e25f604cb9db593ca3013be8c00f310d6790ccb1b7d8fbbdd4660ec8888043a" dependencies = [ "aurora-engine-modexp", "blst", @@ -7089,8 +7116,9 @@ dependencies = [ [[package]] name = "revm-primitives" -version = "8.0.0" -source = "git+https://github.com/bluealloy/revm?rev=caadc71#caadc71fd28929cd751cc997b4b454d8813c434b" +version = "9.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ccb981ede47ccf87c68cebf1ba30cdbb7ec935233ea305f3dfff4c1e10ae541" dependencies = [ "alloy-eips", "alloy-primitives", @@ -7099,13 +7127,10 @@ dependencies = [ "bitvec", "c-kzg", "cfg-if", - "derive_more 0.99.18", "dyn-clone", "enumn", "hashbrown 0.14.5", "hex", - "kzg-rs", - "once_cell", "serde", ] @@ -7265,9 +7290,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.35" +version = "0.38.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" +checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36" dependencies = [ "bitflags 2.6.0", "errno", @@ -7328,6 +7353,19 @@ dependencies = [ "security-framework", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" +dependencies = [ + "openssl-probe", + "rustls-pemfile 2.1.3", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -7689,9 +7727,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.127" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "indexmap 2.5.0", "itoa", @@ -7752,19 +7790,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_yaml" -version = "0.9.34+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" -dependencies = [ - "indexmap 2.5.0", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", -] - [[package]] name = "serial_test" version = "3.1.1" @@ -7917,9 +7942,9 @@ dependencies = [ [[package]] name = "similar-asserts" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e041bb827d1bfca18f213411d51b665309f1afb37a04a5d1464530e13779fc0f" +checksum = "cfe85670573cd6f0fa97940f26e7e6601213c3b0555246c24234131f88c5709e" dependencies = [ "console", "similar", @@ -8043,6 +8068,9 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] [[package]] name = "spki" @@ -8155,9 +8183,9 @@ dependencies = [ [[package]] name = "svm-rs-builds" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fe4ebbe1038a5a517a07948c2487b3ccf79a4908953cc8f0047cf652233546" +checksum = "f2fa0f145894cb4d1c14446f08098ee5f21fc37ccbd1a7dd9dd355bbc806de3b" dependencies = [ "build_const", "const-hex", @@ -8190,9 +8218,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.7.7" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c837dc8852cb7074e46b444afb81783140dab12c58867b49fb3898fbafedf7ea" +checksum = "03133179a4b51eabc6f1aea6951626a766b8aa3af68b12d150f88095914cb447" dependencies = [ "paste", "proc-macro2", @@ -8485,9 +8513,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" dependencies = [ "futures-core", "pin-project-lite", @@ -8525,9 +8553,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", @@ -8607,7 +8635,7 @@ dependencies = [ "tokio", "tokio-rustls 0.26.0", "tokio-stream", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", "tracing", @@ -8639,6 +8667,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36b837f86b25d7c0d7988f00a54e74739be6477f2aac6201b8f429a7569991b7" +dependencies = [ + "tower-layer", + "tower-service", +] + [[package]] name = "tower-http" version = "0.5.2" @@ -8868,7 +8906,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" dependencies = [ - "arbitrary", "byteorder", "crunchy", "hex", @@ -8955,12 +8992,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" -[[package]] -name = "unsafe-libyaml" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" - [[package]] name = "untrusted" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index 9aa6ab471..fe8730a6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -157,54 +157,58 @@ foundry-wallets = { path = "crates/wallets" } foundry-linking = { path = "crates/linking" } # solc & compilation utilities -foundry-block-explorers = { version = "0.5.1", default-features = false } -foundry-compilers = { version = "0.10.3", default-features = false } -foundry-fork-db = "0.2" +foundry-block-explorers = { version = "0.7.1", default-features = false } +foundry-compilers = { version = "0.11.0", default-features = false } +foundry-fork-db = "0.3" solang-parser = "=0.3.3" ## revm # no default features to avoid c-kzg -revm = { version = "13.0.0", default-features = false } -revm-primitives = { version = "8.0.0", default-features = false } -revm-inspectors = { version = "0.5", features = ["serde"] } +revm = { version = "14.0.1", default-features = false } +revm-primitives = { version = "9.0.1", default-features = false } +revm-inspectors = { version = "0.6", features = ["serde"] } ## ethers ethers-contract-abigen = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { version = "0.2.1", default-features = false } -alloy-contract = { version = "0.2.1", default-features = false } -alloy-eips = { version = "0.2.1", default-features = false } -alloy-genesis = { version = "0.2.1", default-features = false } -alloy-json-rpc = { version = "0.2.1", default-features = false } -alloy-network = { version = "0.2.1", default-features = false } -alloy-provider = { version = "0.2.1", default-features = false } -alloy-pubsub = { version = "0.2.1", default-features = false } -alloy-rpc-client = { version = "0.2.1", default-features = false } -alloy-rpc-types = { version = "0.2.1", default-features = true } -alloy-serde = { version = "0.2.1", default-features = false } -alloy-signer = { version = "0.2.1", default-features = false } -alloy-signer-aws = { version = "0.2.1", default-features = false } -alloy-signer-gcp = { version = "0.2.1", default-features = false } -alloy-signer-ledger = { version = "0.2.1", default-features = false } -alloy-signer-local = { version = "0.2.1", default-features = false } -alloy-signer-trezor = { version = "0.2.1", default-features = false } -alloy-transport = { version = "0.2.1", default-features = false } -alloy-transport-http = { version = "0.2.1", default-features = false } -alloy-transport-ipc = { version = "0.2.1", default-features = false } -alloy-transport-ws = { version = "0.2.1", default-features = false } +alloy-consensus = { version = "0.3.1", default-features = false } +alloy-contract = { version = "0.3.1", default-features = false } +alloy-eips = { version = "0.3.1", default-features = false } +alloy-genesis = { version = "0.3.1", default-features = false } +alloy-json-rpc = { version = "0.3.1", default-features = false } +alloy-network = { version = "0.3.1", default-features = false } +alloy-provider = { version = "0.3.1", default-features = false } +alloy-pubsub = { version = "0.3.1", default-features = false } +alloy-rpc-client = { version = "0.3.1", default-features = false } +alloy-rpc-types = { version = "0.3.1", default-features = true } +alloy-serde = { version = "0.3.1", default-features = false } +alloy-signer = { version = "0.3.1", default-features = false } +alloy-signer-aws = { version = "0.3.1", default-features = false } +alloy-signer-gcp = { version = "0.3.1", default-features = false } +alloy-signer-ledger = { version = "0.3.1", default-features = false } +alloy-signer-local = { version = "0.3.1", default-features = false } +alloy-signer-trezor = { version = "0.3.1", default-features = false } +alloy-transport = { version = "0.3.1", default-features = false } +alloy-transport-http = { version = "0.3.1", default-features = false } +alloy-transport-ipc = { version = "0.3.1", default-features = false } +alloy-transport-ws = { version = "0.3.1", default-features = false } -alloy-dyn-abi = "0.7.7" -alloy-json-abi = "0.7.7" -alloy-primitives = { version = "0.7.7", features = ["getrandom", "rand"] } -alloy-sol-macro-expander = "0.7.7" -alloy-sol-macro-input = "0.7.7" -alloy-sol-types = "0.7.7" -syn-solidity = "0.7.7" +## alloy-core +alloy-dyn-abi = "0.8.1" +alloy-json-abi = "0.8.1" +alloy-primitives = { version = "0.8.1", features = ["getrandom", "rand"] } +alloy-sol-macro-expander = "0.8.1" +alloy-sol-macro-input = "0.8.1" +alloy-sol-types = "0.8.1" +syn-solidity = "0.8.1" alloy-chains = "0.1" -alloy-rlp = "0.3.3" -alloy-trie = "0.4.1" +alloy-rlp = "0.3" +alloy-trie = "0.5.0" + +## op-alloy for tests in anvil +op-alloy-rpc-types = "0.2.8" ## misc async-trait = "0.1" @@ -252,7 +256,7 @@ yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } tempfile = "3.10" tokio = "1" rayon = "1" - +evmole = "0.5" axum = "0.7" hyper = "1.0" reqwest = { version = "0.12", default-features = false } @@ -264,26 +268,27 @@ proptest = "1" comfy-table = "7" [patch.crates-io] -alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -alloy-signer-gcp = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -alloy-signer-local = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "511ae98" } -revm = { git = "https://github.com/bluealloy/revm", rev = "caadc71" } -revm-primitives = { git = "https://github.com/bluealloy/revm", rev = "caadc71" } +# https://github.com/alloy-rs/alloy/pull/1229 + https://github.com/alloy-rs/alloy/pull/1243 +alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +alloy-network-primitives = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +alloy-rpc-types-eth = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +alloy-signer-gcp = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +alloy-signer-local = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index a98787985..77faec1f1 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -122,6 +122,7 @@ alloy-pubsub.workspace = true foundry-test-utils.workspace = true similar-asserts.workspace = true tokio = { workspace = true, features = ["full"] } +op-alloy-rpc-types.workspace = true [features] default = ["cli", "jemalloc"] diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index d080f1e27..7d80450f3 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -300,7 +300,6 @@ pub fn to_alloy_transaction_with_hash_and_sender( transaction_type: None, max_fee_per_blob_gas: None, blob_versioned_hashes: None, - other: Default::default(), authorization_list: None, }, TypedTransaction::EIP2930(t) => RpcTransaction { @@ -328,7 +327,6 @@ pub fn to_alloy_transaction_with_hash_and_sender( transaction_type: Some(1), max_fee_per_blob_gas: None, blob_versioned_hashes: None, - other: Default::default(), authorization_list: None, }, TypedTransaction::EIP1559(t) => RpcTransaction { @@ -356,7 +354,6 @@ pub fn to_alloy_transaction_with_hash_and_sender( transaction_type: Some(2), max_fee_per_blob_gas: None, blob_versioned_hashes: None, - other: Default::default(), authorization_list: None, }, TypedTransaction::EIP4844(t) => RpcTransaction { @@ -384,7 +381,6 @@ pub fn to_alloy_transaction_with_hash_and_sender( transaction_type: Some(3), max_fee_per_blob_gas: Some(t.tx().tx().max_fee_per_blob_gas), blob_versioned_hashes: Some(t.tx().tx().blob_versioned_hashes.clone()), - other: Default::default(), authorization_list: None, }, TypedTransaction::EIP7702(t) => RpcTransaction { @@ -433,7 +429,6 @@ pub fn to_alloy_transaction_with_hash_and_sender( transaction_type: None, max_fee_per_blob_gas: None, blob_versioned_hashes: None, - other: Default::default(), authorization_list: None, }, } diff --git a/crates/anvil/core/src/eth/transaction/optimism.rs b/crates/anvil/core/src/eth/transaction/optimism.rs index 5c4f35481..6cc7bfa5a 100644 --- a/crates/anvil/core/src/eth/transaction/optimism.rs +++ b/crates/anvil/core/src/eth/transaction/optimism.rs @@ -186,6 +186,44 @@ impl Transaction for DepositTransactionRequest { fn gas_price(&self) -> Option { None } + + fn ty(&self) -> u8 { + 0x7E + } + + // Below fields are not found in a `DepositTransactionRequest` + + fn access_list(&self) -> Option<&alloy_rpc_types::AccessList> { + None + } + + fn authorization_list(&self) -> Option<&[revm::primitives::SignedAuthorization]> { + None + } + + fn blob_versioned_hashes(&self) -> Option<&[B256]> { + None + } + + fn effective_tip_per_gas(&self, _base_fee: u64) -> Option { + None + } + + fn max_fee_per_blob_gas(&self) -> Option { + None + } + + fn max_fee_per_gas(&self) -> u128 { + 0 + } + + fn max_priority_fee_per_gas(&self) -> Option { + None + } + + fn priority_fee_or_price(&self) -> u128 { + 0 + } } impl SignableTransaction for DepositTransactionRequest { diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index d5770f703..3145ee1b2 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -1199,7 +1199,7 @@ latest block number: {latest_block}" } } - let block_hash = block.header.hash.unwrap_or_default(); + let block_hash = block.header.hash; let chain_id = if let Some(chain_id) = self.chain_id { chain_id @@ -1218,7 +1218,7 @@ latest block number: {latest_block}" }; let override_chain_id = self.chain_id; // apply changes such as difficulty -> prevrandao and chain specifics for current chain id - apply_chain_and_block_specific_env_changes(env, &block); + apply_chain_and_block_specific_env_changes::(env, &block); let meta = BlockchainDbMeta::new(*env.env.clone(), eth_rpc_url.clone()); let block_chain_db = if self.fork_chain_id.is_some() { @@ -1289,18 +1289,19 @@ async fn derive_block_and_transactions( .ok_or(eyre::eyre!("Failed to get fork block by number"))?; // Filter out transactions that are after the fork transaction - let filtered_transactions: Vec<&Transaction> = transaction_block - .transactions - .as_transactions() - .ok_or(eyre::eyre!("Failed to get transactions from full fork block"))? - .iter() - .take_while_inclusive(|&transaction| transaction.hash != transaction_hash.0) - .collect(); + let filtered_transactions: Vec<&alloy_serde::WithOtherFields> = + transaction_block + .transactions + .as_transactions() + .ok_or(eyre::eyre!("Failed to get transactions from full fork block"))? + .iter() + .take_while_inclusive(|&transaction| transaction.hash != transaction_hash.0) + .collect(); // Convert the transactions to PoolTransactions let force_transactions = filtered_transactions .iter() - .map(|&transaction| PoolTransaction::try_from(transaction.clone())) + .map(|&transaction| PoolTransaction::try_from(transaction.clone().inner)) .collect::, _>>()?; Ok((transaction_block_number.saturating_sub(1), Some(force_transactions))) } @@ -1461,7 +1462,7 @@ async fn find_latest_fork_block, T: Transport + Clone // leeway for _ in 0..2 { if let Some(block) = provider.get_block(num.into(), false.into()).await? { - if block.header.hash.is_some() { + if !block.header.hash.is_zero() { break; } } diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 937cd8504..90cdc1006 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -34,7 +34,7 @@ use crate::{ use alloy_consensus::{transaction::eip4844::TxEip4844Variant, Account, TxEnvelope}; use alloy_dyn_abi::TypedData; use alloy_eips::eip2718::Encodable2718; -use alloy_network::eip2718::Decodable2718; +use alloy_network::{eip2718::Decodable2718, BlockResponse}; use alloy_primitives::{Address, Bytes, Parity, TxHash, TxKind, B256, B64, U256, U64}; use alloy_rpc_types::{ anvil::{ @@ -48,7 +48,7 @@ use alloy_rpc_types::{ parity::LocalizedTransactionTrace, }, txpool::{TxpoolContent, TxpoolInspect, TxpoolInspectSummary, TxpoolStatus}, - AccessList, AccessListResult, Block, BlockId, BlockNumberOrTag as BlockNumber, + AccessList, AccessListResult, AnyNetworkBlock, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, EIP1186AccountProofResponse, FeeHistory, Filter, FilteredParams, Index, Log, Transaction, }; @@ -721,7 +721,7 @@ impl EthApi { /// Returns block with given hash. /// /// Handler for ETH RPC call: `eth_getBlockByHash` - pub async fn block_by_hash(&self, hash: B256) -> Result> { + pub async fn block_by_hash(&self, hash: B256) -> Result> { node_info!("eth_getBlockByHash"); self.backend.block_by_hash(hash).await } @@ -729,7 +729,7 @@ impl EthApi { /// Returns a _full_ block with given hash. /// /// Handler for ETH RPC call: `eth_getBlockByHash` - pub async fn block_by_hash_full(&self, hash: B256) -> Result> { + pub async fn block_by_hash_full(&self, hash: B256) -> Result> { node_info!("eth_getBlockByHash"); self.backend.block_by_hash_full(hash).await } @@ -737,7 +737,7 @@ impl EthApi { /// Returns block with given number. /// /// Handler for ETH RPC call: `eth_getBlockByNumber` - pub async fn block_by_number(&self, number: BlockNumber) -> Result> { + pub async fn block_by_number(&self, number: BlockNumber) -> Result> { node_info!("eth_getBlockByNumber"); if number == BlockNumber::Pending { return Ok(Some(self.pending_block().await)); @@ -749,7 +749,10 @@ impl EthApi { /// Returns a _full_ block with given number /// /// Handler for ETH RPC call: `eth_getBlockByNumber` - pub async fn block_by_number_full(&self, number: BlockNumber) -> Result> { + pub async fn block_by_number_full( + &self, + number: BlockNumber, + ) -> Result> { node_info!("eth_getBlockByNumber"); if number == BlockNumber::Pending { return Ok(self.pending_block_full().await); @@ -778,7 +781,7 @@ impl EthApi { pub async fn block_transaction_count_by_hash(&self, hash: B256) -> Result> { node_info!("eth_getBlockTransactionCountByHash"); let block = self.backend.block_by_hash(hash).await?; - let txs = block.map(|b| match b.transactions { + let txs = block.map(|b| match b.transactions() { BlockTransactions::Full(txs) => U256::from(txs.len()), BlockTransactions::Hashes(txs) => U256::from(txs.len()), BlockTransactions::Uncle => U256::from(0), @@ -800,7 +803,7 @@ impl EthApi { return Ok(Some(U256::from(block.transactions.len()))); } let block = self.backend.block_by_number(block_number).await?; - let txs = block.map(|b| match b.transactions { + let txs = block.map(|b| match b.transactions() { BlockTransactions::Full(txs) => U256::from(txs.len()), BlockTransactions::Hashes(txs) => U256::from(txs.len()), BlockTransactions::Uncle => U256::from(0), @@ -1238,7 +1241,7 @@ impl EthApi { &self, block_hash: B256, idx: Index, - ) -> Result> { + ) -> Result> { node_info!("eth_getUncleByBlockHashAndIndex"); let number = self.backend.ensure_block_number(Some(BlockId::Hash(block_hash.into()))).await?; @@ -1258,7 +1261,7 @@ impl EthApi { &self, block_number: BlockNumber, idx: Index, - ) -> Result> { + ) -> Result> { node_info!("eth_getUncleByBlockNumberAndIndex"); let number = self.backend.ensure_block_number(Some(BlockId::Number(block_number))).await?; if let Some(fork) = self.get_fork() { @@ -2138,7 +2141,10 @@ impl EthApi { /// **Note**: This behaves exactly as [Self::evm_mine] but returns different output, for /// compatibility reasons, this is a separate call since `evm_mine` is not an anvil original. /// and `ganache` may change the `0x0` placeholder. - pub async fn evm_mine_detailed(&self, opts: Option) -> Result> { + pub async fn evm_mine_detailed( + &self, + opts: Option, + ) -> Result> { node_info!("evm_mine_detailed"); let mined_blocks = self.do_evm_mine(opts).await?; @@ -2151,7 +2157,7 @@ impl EthApi { if let Some(mut block) = self.backend.block_by_number_full(BlockNumber::Number(block_num)).await? { - let mut block_txs = match block.transactions { + let block_txs = match block.transactions_mut() { BlockTransactions::Full(txs) => txs, BlockTransactions::Hashes(_) | BlockTransactions::Uncle => unreachable!(), }; @@ -2183,7 +2189,7 @@ impl EthApi { } } } - block.transactions = BlockTransactions::Full(block_txs); + block.transactions = BlockTransactions::Full(block_txs.to_vec()); blocks.push(block); } } @@ -2634,19 +2640,19 @@ impl EthApi { } /// Returns the pending block with tx hashes - async fn pending_block(&self) -> Block { + async fn pending_block(&self) -> AnyNetworkBlock { let transactions = self.pool.ready_transactions().collect::>(); let info = self.backend.pending_block(transactions).await; self.backend.convert_block(info.block) } /// Returns the full pending block with `Transaction` objects - async fn pending_block_full(&self) -> Option { + async fn pending_block_full(&self) -> Option { let transactions = self.pool.ready_transactions().collect::>(); let BlockInfo { block, transactions, receipts: _ } = self.backend.pending_block(transactions).await; - let partial_block = self.backend.convert_block(block.clone()); + let mut partial_block = self.backend.convert_block(block.clone()); let mut block_transactions = Vec::with_capacity(block.transactions.len()); let base_fee = self.backend.base_fee(); @@ -2661,10 +2667,12 @@ impl EthApi { Some(info), Some(base_fee), ); - block_transactions.push(tx.inner); + block_transactions.push(tx); } - Some(partial_block.into_full_block(block_transactions)) + partial_block.transactions = BlockTransactions::from(block_transactions); + + Some(partial_block) } fn build_typed_tx_request( diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index 0a63ad453..c661eeaa8 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -3,6 +3,7 @@ use crate::eth::{backend::db::Db, error::BlockchainError, pool::transactions::PoolTransaction}; use alloy_consensus::Account; use alloy_eips::eip2930::AccessListResult; +use alloy_network::BlockResponse; use alloy_primitives::{Address, Bytes, StorageValue, B256, U256}; use alloy_provider::{ ext::{DebugApi, TraceApi}, @@ -14,7 +15,7 @@ use alloy_rpc_types::{ geth::{GethDebugTracingOptions, GethTrace}, parity::LocalizedTransactionTrace as Trace, }, - Block, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, + AnyNetworkBlock, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, EIP1186AccountProofResponse, FeeHistory, Filter, Log, Transaction, }; use alloy_serde::WithOtherFields; @@ -82,12 +83,12 @@ impl ClientFork { .get_block(block_number, false.into()) .await? .ok_or(BlockchainError::BlockNotFound)?; - let block_hash = block.header.hash.ok_or(BlockchainError::BlockNotFound)?; + let block_hash = block.header.hash; let timestamp = block.header.timestamp; let base_fee = block.header.base_fee_per_gas; let total_difficulty = block.header.total_difficulty.unwrap_or_default(); - let number = block.header.number.ok_or(BlockchainError::BlockNotFound)?; + let number = block.header.number; self.config.write().update_block(number, block_hash, timestamp, base_fee, total_difficulty); self.clear_cached_storage(); @@ -283,10 +284,10 @@ impl ClientFork { index: usize, ) -> Result>, TransportError> { if let Some(block) = self.block_by_number(number).await? { - match block.transactions { + match block.transactions() { BlockTransactions::Full(txs) => { if let Some(tx) = txs.get(index) { - return Ok(Some(WithOtherFields::new(tx.clone()))); + return Ok(Some(tx.clone())); } } BlockTransactions::Hashes(hashes) => { @@ -307,10 +308,10 @@ impl ClientFork { index: usize, ) -> Result>, TransportError> { if let Some(block) = self.block_by_hash(hash).await? { - match block.transactions { + match block.transactions() { BlockTransactions::Full(txs) => { if let Some(tx) = txs.get(index) { - return Ok(Some(WithOtherFields::new(tx.clone()))); + return Ok(Some(tx.clone())); } } BlockTransactions::Hashes(hashes) => { @@ -440,7 +441,10 @@ impl ClientFork { Ok(None) } - pub async fn block_by_hash(&self, hash: B256) -> Result, TransportError> { + pub async fn block_by_hash( + &self, + hash: B256, + ) -> Result, TransportError> { if let Some(mut block) = self.storage_read().blocks.get(&hash).cloned() { block.transactions.convert_to_hashes(); return Ok(Some(block)); @@ -452,7 +456,10 @@ impl ClientFork { })) } - pub async fn block_by_hash_full(&self, hash: B256) -> Result, TransportError> { + pub async fn block_by_hash_full( + &self, + hash: B256, + ) -> Result, TransportError> { if let Some(block) = self.storage_read().blocks.get(&hash).cloned() { return Ok(Some(self.convert_to_full_block(block))); } @@ -462,7 +469,7 @@ impl ClientFork { pub async fn block_by_number( &self, block_number: u64, - ) -> Result, TransportError> { + ) -> Result, TransportError> { if let Some(mut block) = self .storage_read() .hashes @@ -483,7 +490,7 @@ impl ClientFork { pub async fn block_by_number_full( &self, block_number: u64, - ) -> Result, TransportError> { + ) -> Result, TransportError> { if let Some(block) = self .storage_read() .hashes @@ -500,19 +507,17 @@ impl ClientFork { async fn fetch_full_block( &self, block_id: impl Into, - ) -> Result, TransportError> { + ) -> Result, TransportError> { if let Some(block) = self.provider().get_block(block_id.into(), true.into()).await? { - let hash = block.header.hash.unwrap(); - let block_number = block.header.number.unwrap(); + let hash = block.header.hash; + let block_number = block.header.number; let mut storage = self.storage_write(); // also insert all transactions - let block_txs = match block.clone().transactions { - BlockTransactions::Full(txs) => txs, + let block_txs = match block.transactions() { + BlockTransactions::Full(txs) => txs.to_owned(), _ => vec![], }; - storage - .transactions - .extend(block_txs.iter().map(|tx| (tx.hash, WithOtherFields::new(tx.clone())))); + storage.transactions.extend(block_txs.iter().map(|tx| (tx.hash, tx.clone()))); storage.hashes.insert(block_number, hash); storage.blocks.insert(hash, block.clone()); return Ok(Some(block)); @@ -525,7 +530,7 @@ impl ClientFork { &self, hash: B256, index: usize, - ) -> Result, TransportError> { + ) -> Result, TransportError> { if let Some(block) = self.block_by_hash(hash).await? { return self.uncles_by_block_and_index(block, index).await; } @@ -536,7 +541,7 @@ impl ClientFork { &self, number: u64, index: usize, - ) -> Result, TransportError> { + ) -> Result, TransportError> { if let Some(block) = self.block_by_number(number).await? { return self.uncles_by_block_and_index(block, index).await; } @@ -545,11 +550,11 @@ impl ClientFork { async fn uncles_by_block_and_index( &self, - block: Block, + block: AnyNetworkBlock, index: usize, - ) -> Result, TransportError> { - let block_hash = block.header.hash.expect("Missing block hash"); - let block_number = block.header.number.expect("Missing block number"); + ) -> Result, TransportError> { + let block_hash = block.header.hash; + let block_number = block.header.number; if let Some(uncles) = self.storage_read().uncles.get(&block_hash) { return Ok(uncles.get(index).cloned()); } @@ -568,7 +573,7 @@ impl ClientFork { } /// Converts a block of hashes into a full block - fn convert_to_full_block(&self, block: Block) -> Block { + fn convert_to_full_block(&self, mut block: AnyNetworkBlock) -> AnyNetworkBlock { let storage = self.storage.read(); let block_txs_len = match block.transactions { BlockTransactions::Full(ref txs) => txs.len(), @@ -579,11 +584,13 @@ impl ClientFork { let mut transactions = Vec::with_capacity(block_txs_len); for tx in block.transactions.hashes() { if let Some(tx) = storage.transactions.get(&tx).cloned() { - transactions.push(tx.inner); + transactions.push(tx); } } // TODO: fix once blocks have generic transactions - block.into_full_block(transactions) + block.inner.transactions = BlockTransactions::Full(transactions); + + block } } @@ -668,8 +675,8 @@ impl ClientForkConfig { /// This is used as a cache so repeated requests to the same data are not sent to the remote client #[derive(Clone, Debug, Default)] pub struct ForkedStorage { - pub uncles: HashMap>, - pub blocks: HashMap, + pub uncles: HashMap>, + pub blocks: HashMap, pub hashes: HashMap, pub transactions: HashMap>, pub transaction_receipts: HashMap, diff --git a/crates/anvil/src/eth/backend/info.rs b/crates/anvil/src/eth/backend/info.rs index 448dc660a..3ac359619 100644 --- a/crates/anvil/src/eth/backend/info.rs +++ b/crates/anvil/src/eth/backend/info.rs @@ -2,7 +2,8 @@ use crate::mem::Backend; use alloy_primitives::B256; -use alloy_rpc_types::Block as AlloyBlock; +use alloy_rpc_types::{Block as AlloyBlock, Transaction}; +use alloy_serde::WithOtherFields; use anvil_core::eth::{block::Block, transaction::TypedReceipt}; use std::{fmt, sync::Arc}; @@ -42,7 +43,10 @@ impl StorageInfo { } /// Returns the block with the given hash in the format of the ethereum API - pub fn eth_block(&self, hash: B256) -> Option { + pub fn eth_block( + &self, + hash: B256, + ) -> Option>>> { let block = self.block(hash)?; Some(self.backend.convert_block(block)) } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index a9c54ba86..86e8ad039 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -48,9 +48,10 @@ use alloy_rpc_types::{ }, parity::LocalizedTransactionTrace, }, - AccessList, Block as AlloyBlock, BlockId, BlockNumberOrTag as BlockNumber, - EIP1186AccountProofResponse as AccountProof, EIP1186StorageProof as StorageProof, Filter, - FilteredParams, Header as AlloyHeader, Index, Log, Transaction, TransactionReceipt, + AccessList, AnyNetworkBlock, Block as AlloyBlock, BlockId, BlockNumberOrTag as BlockNumber, + BlockTransactions, EIP1186AccountProofResponse as AccountProof, + EIP1186StorageProof as StorageProof, Filter, FilteredParams, Header as AlloyHeader, Index, Log, + Transaction, TransactionReceipt, }; use alloy_serde::WithOtherFields; use alloy_trie::{proof::ProofRetainer, HashBuilder, Nibbles}; @@ -1523,7 +1524,10 @@ impl Backend { } } - pub async fn block_by_hash(&self, hash: B256) -> Result, BlockchainError> { + pub async fn block_by_hash( + &self, + hash: B256, + ) -> Result, BlockchainError> { trace!(target: "backend", "get block by hash {:?}", hash); if let tx @ Some(_) = self.mined_block_by_hash(hash) { return Ok(tx); @@ -1539,7 +1543,7 @@ impl Backend { pub async fn block_by_hash_full( &self, hash: B256, - ) -> Result, BlockchainError> { + ) -> Result, BlockchainError> { trace!(target: "backend", "get block by hash {:?}", hash); if let tx @ Some(_) = self.get_full_block(hash) { return Ok(tx); @@ -1552,7 +1556,7 @@ impl Backend { Ok(None) } - fn mined_block_by_hash(&self, hash: B256) -> Option { + fn mined_block_by_hash(&self, hash: B256) -> Option { let block = self.blockchain.get_block_by_hash(&hash)?; Some(self.convert_block(block)) } @@ -1588,7 +1592,7 @@ impl Backend { pub async fn block_by_number( &self, number: BlockNumber, - ) -> Result, BlockchainError> { + ) -> Result, BlockchainError> { trace!(target: "backend", "get block by number {:?}", number); if let tx @ Some(_) = self.mined_block_by_number(number) { return Ok(tx); @@ -1607,7 +1611,7 @@ impl Backend { pub async fn block_by_number_full( &self, number: BlockNumber, - ) -> Result, BlockchainError> { + ) -> Result, BlockchainError> { trace!(target: "backend", "get block by number {:?}", number); if let tx @ Some(_) = self.get_full_block(number) { return Ok(tx); @@ -1660,22 +1664,24 @@ impl Backend { self.blockchain.get_block_by_hash(&hash) } - pub fn mined_block_by_number(&self, number: BlockNumber) -> Option { + pub fn mined_block_by_number(&self, number: BlockNumber) -> Option { let block = self.get_block(number)?; let mut block = self.convert_block(block); block.transactions.convert_to_hashes(); Some(block) } - pub fn get_full_block(&self, id: impl Into) -> Option { + pub fn get_full_block(&self, id: impl Into) -> Option { let block = self.get_block(id)?; let transactions = self.mined_transactions_in_block(&block)?; - let block = self.convert_block(block); - Some(block.into_full_block(transactions.into_iter().map(|t| t.inner).collect())) + let mut block = self.convert_block(block); + block.inner.transactions = BlockTransactions::Full(transactions); + + Some(block) } /// Takes a block as it's stored internally and returns the eth api conform block format. - pub fn convert_block(&self, block: Block) -> AlloyBlock { + pub fn convert_block(&self, block: Block) -> AnyNetworkBlock { let size = U256::from(alloy_rlp::encode(&block).len() as u32); let Block { header, transactions, .. } = block; @@ -1705,16 +1711,16 @@ impl Backend { parent_beacon_block_root, } = header; - let mut block = AlloyBlock { + let block = AlloyBlock { header: AlloyHeader { - hash: Some(hash), + hash, parent_hash, uncles_hash: ommers_hash, miner: beneficiary, state_root, transactions_root, receipts_root, - number: Some(number), + number, gas_used, gas_limit, extra_data: extra_data.0.into(), @@ -1737,9 +1743,10 @@ impl Backend { ), uncles: vec![], withdrawals: None, - other: Default::default(), }; + let mut block = WithOtherFields::new(block); + // If Arbitrum, apply chain specifics to converted block. if let Ok( NamedChain::Arbitrum | @@ -1749,7 +1756,7 @@ impl Backend { ) = NamedChain::try_from(self.env.read().env.cfg.chain_id) { // Block number is the best number. - block.header.number = Some(self.best_number()); + block.header.number = self.best_number(); // Set `l1BlockNumber` field. block.other.insert("l1BlockNumber".to_string(), number.into()); } @@ -1769,13 +1776,13 @@ impl Backend { let current = self.best_number(); let requested = match block_id.map(Into::into).unwrap_or(BlockId::Number(BlockNumber::Latest)) { - BlockId::Hash(hash) => self - .block_by_hash(hash.block_hash) - .await? - .ok_or(BlockchainError::BlockNotFound)? - .header - .number - .ok_or(BlockchainError::BlockNotFound)?, + BlockId::Hash(hash) => { + self.block_by_hash(hash.block_hash) + .await? + .ok_or(BlockchainError::BlockNotFound)? + .header + .number + } BlockId::Number(num) => match num { BlockNumber::Latest | BlockNumber::Pending => self.best_number(), BlockNumber::Earliest => U64::ZERO.to::(), @@ -1841,7 +1848,7 @@ impl Backend { if let Some((block_hash, block)) = self .block_by_number(BlockNumber::Number(block_number.to::())) .await? - .and_then(|block| Some((block.header.hash?, block))) + .map(|block| (block.header.hash, block)) { if let Some(state) = self.states.write().get(&block_hash) { let block = BlockEnv { @@ -2290,8 +2297,8 @@ impl Backend { number: BlockNumber, index: Index, ) -> Result>, BlockchainError> { - if let Some(hash) = self.mined_block_by_number(number).and_then(|b| b.header.hash) { - return Ok(self.mined_transaction_by_block_hash_and_index(hash, index)); + if let Some(block) = self.mined_block_by_number(number) { + return Ok(self.mined_transaction_by_block_hash_and_index(block.header.hash, index)); } if let Some(fork) = self.get_fork() { diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index 7349cff78..ada9e6c53 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -273,7 +273,7 @@ impl From for InvalidTransactionError { Self::AuthorizationListNotSupported } InvalidTransaction::AuthorizationListInvalidFields | - InvalidTransaction::InvalidAuthorizationList | + InvalidTransaction::InvalidAuthorizationList(_) | InvalidTransaction::OptimismError(_) | InvalidTransaction::EofCrateShouldHaveToAddress | InvalidTransaction::EmptyAuthorizationList => Self::Revm(err), diff --git a/crates/anvil/src/eth/otterscan/api.rs b/crates/anvil/src/eth/otterscan/api.rs index 0195cee69..798ff205b 100644 --- a/crates/anvil/src/eth/otterscan/api.rs +++ b/crates/anvil/src/eth/otterscan/api.rs @@ -3,20 +3,23 @@ use crate::eth::{ macros::node_info, EthApi, }; +use alloy_network::BlockResponse; use alloy_primitives::{Address, Bytes, B256, U256}; use alloy_rpc_types::{ trace::{ otterscan::{ BlockDetails, ContractCreator, InternalOperation, OtsBlock, OtsBlockTransactions, - OtsReceipt, OtsTransactionReceipt, TraceEntry, TransactionsWithReceipts, + OtsReceipt, OtsSlimBlock, OtsTransactionReceipt, TraceEntry, TransactionsWithReceipts, }, parity::{ Action, CallAction, CallType, CreateAction, CreateOutput, LocalizedTransactionTrace, RewardAction, TraceOutput, }, }, - Block, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, + AnyNetworkBlock, Block, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, + Transaction, }; +use alloy_serde::WithOtherFields; use itertools::Itertools; use futures::future::join_all; @@ -84,7 +87,10 @@ impl EthApi { /// /// As a faster alternative to `eth_getBlockByNumber` (by excluding uncle block /// information), which is not relevant in the context of an anvil node - pub async fn erigon_get_header_by_number(&self, number: BlockNumber) -> Result> { + pub async fn erigon_get_header_by_number( + &self, + number: BlockNumber, + ) -> Result> { node_info!("ots_getApiLevel"); self.backend.block_by_number(number).await @@ -172,7 +178,7 @@ impl EthApi { number: u64, page: usize, page_size: usize, - ) -> Result { + ) -> Result>> { node_info!("ots_getBlockTransactions"); match self.backend.block_by_number_full(number.into()).await? { @@ -346,7 +352,7 @@ impl EthApi { /// based on the existing list. /// /// Therefore we keep it simple by keeping the data in the response - pub async fn build_ots_block_details(&self, block: Block) -> Result { + pub async fn build_ots_block_details(&self, block: AnyNetworkBlock) -> Result { if block.transactions.is_uncle() { return Err(BlockchainError::DataUnavailable); } @@ -369,8 +375,18 @@ impl EthApi { .iter() .fold(0, |acc, receipt| acc + receipt.gas_used * receipt.effective_gas_price); + let Block { header, uncles, transactions, size, withdrawals } = block.inner; + + let block = OtsSlimBlock { + header, + uncles, + transaction_count: transactions.len(), + size, + withdrawals, + }; + Ok(BlockDetails { - block: block.into(), + block, total_fees: U256::from(total_fees), // issuance has no meaningful value in anvil's backend. just default to 0 issuance: Default::default(), @@ -383,20 +399,20 @@ impl EthApi { /// [`ots_getBlockTransactions`]: https://github.com/otterscan/otterscan/blob/develop/docs/custom-jsonrpc.md#ots_getblockdetails pub async fn build_ots_block_tx( &self, - mut block: Block, + mut block: AnyNetworkBlock, page: usize, page_size: usize, - ) -> Result { + ) -> Result>> { if block.transactions.is_uncle() { return Err(BlockchainError::DataUnavailable); } - block.transactions = match block.transactions { + block.transactions = match block.transactions() { BlockTransactions::Full(txs) => BlockTransactions::Full( - txs.into_iter().skip(page * page_size).take(page_size).collect(), + txs.iter().skip(page * page_size).take(page_size).cloned().collect(), ), BlockTransactions::Hashes(txs) => BlockTransactions::Hashes( - txs.into_iter().skip(page * page_size).take(page_size).collect(), + txs.iter().skip(page * page_size).take(page_size).cloned().collect(), ), BlockTransactions::Uncle => unreachable!(), }; @@ -418,9 +434,13 @@ impl EthApi { }) .collect::>>()?; - let fullblock: OtsBlock = block.into(); + let transaction_count = block.transactions().len(); + let fullblock = OtsBlock { block: block.inner, transaction_count }; + + let ots_block_txs = + OtsBlockTransactions::> { fullblock, receipts }; - Ok(OtsBlockTransactions { fullblock, receipts }) + Ok(ots_block_txs) } pub async fn build_ots_search_transactions( diff --git a/crates/anvil/src/tasks/mod.rs b/crates/anvil/src/tasks/mod.rs index 8cf13e844..f9ba36b60 100644 --- a/crates/anvil/src/tasks/mod.rs +++ b/crates/anvil/src/tasks/mod.rs @@ -6,7 +6,7 @@ use crate::{shutdown::Shutdown, tasks::block_listener::BlockListener, EthApi}; use alloy_network::AnyNetwork; use alloy_primitives::B256; use alloy_provider::Provider; -use alloy_rpc_types::{anvil::Forking, Block}; +use alloy_rpc_types::{anvil::Forking, AnyNetworkBlock}; use alloy_transport::Transport; use futures::StreamExt; use std::{fmt, future::Future}; @@ -77,7 +77,7 @@ impl TaskManager { let _ = api .anvil_reset(Some(Forking { json_rpc_url: None, - block_number: block.header.number, + block_number: Some(block.header.number), })) .await; } @@ -135,7 +135,7 @@ impl TaskManager { let _ = api .anvil_reset(Some(Forking { json_rpc_url: None, - block_number: block.header.number, + block_number: Some(block.header.number), })) .await; } @@ -149,7 +149,7 @@ impl TaskManager { where P: Provider + 'static, T: Transport + Clone, - F: Fn(Block) -> Fut + Unpin + Send + Sync + 'static, + F: Fn(AnyNetworkBlock) -> Fut + Unpin + Send + Sync + 'static, Fut: Future + Send, { let shutdown = self.on_shutdown.clone(); diff --git a/crates/anvil/tests/it/abi.rs b/crates/anvil/tests/it/abi.rs index 3356b02f1..9bd3b84e9 100644 --- a/crates/anvil/tests/it/abi.rs +++ b/crates/anvil/tests/it/abi.rs @@ -16,7 +16,7 @@ sol!( sol!( #[sol(rpc)] - MulticallContract, + Multicall, "test-data/multicall.json" ); diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index 1f1c48e21..0e8001853 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -1,7 +1,7 @@ //! tests for custom anvil endpoints use crate::{ - abi::{self, Greeter, MulticallContract, BUSD}, + abi::{self, Greeter, Multicall, BUSD}, fork::fork_config, utils::http_provider_with_signer, }; @@ -298,13 +298,13 @@ async fn test_set_next_timestamp() { let block = provider.get_block(BlockId::default(), false.into()).await.unwrap().unwrap(); - assert_eq!(block.header.number.unwrap(), 1); + assert_eq!(block.header.number, 1); assert_eq!(block.header.timestamp, next_timestamp.as_secs()); api.evm_mine(None).await.unwrap(); let next = provider.get_block(BlockId::default(), false.into()).await.unwrap().unwrap(); - assert_eq!(next.header.number.unwrap(), 2); + assert_eq!(next.header.number, 2); assert!(next.header.timestamp > block.header.timestamp); } @@ -447,7 +447,7 @@ async fn can_get_node_info() { let expected_node_info = NodeInfo { current_block_number: 0_u64, current_block_timestamp: 1, - current_block_hash: block.header.hash.unwrap(), + current_block_hash: block.header.hash, hard_fork: hard_fork.to_string(), transaction_order: "fees".to_owned(), environment: NodeEnvironment { @@ -480,7 +480,7 @@ async fn can_get_metadata() { provider.get_block(BlockId::from(block_number), false.into()).await.unwrap().unwrap(); let expected_metadata = Metadata { - latest_block_hash: block.header.hash.unwrap(), + latest_block_hash: block.header.hash, latest_block_number: block_number, chain_id, client_version: CLIENT_VERSION.to_string(), @@ -506,7 +506,7 @@ async fn can_get_metadata_on_fork() { provider.get_block(BlockId::from(block_number), false.into()).await.unwrap().unwrap(); let expected_metadata = Metadata { - latest_block_hash: block.header.hash.unwrap(), + latest_block_hash: block.header.hash, latest_block_number: block_number, chain_id, client_version: CLIENT_VERSION.to_string(), @@ -514,7 +514,7 @@ async fn can_get_metadata_on_fork() { forked_network: Some(ForkedNetwork { chain_id, fork_block_number: block_number, - fork_block_hash: block.header.hash.unwrap(), + fork_block_hash: block.header.hash, }), snapshots: Default::default(), }; @@ -618,21 +618,21 @@ async fn test_fork_revert_call_latest_block_timestamp() { api.evm_revert(snapshot_id).await.unwrap(); let multicall_contract = - MulticallContract::new(address!("eefba1e63905ef1d7acba5a8513c70307c1ce441"), &provider); + Multicall::new(address!("eefba1e63905ef1d7acba5a8513c70307c1ce441"), &provider); - let MulticallContract::getCurrentBlockTimestampReturn { timestamp } = + let Multicall::getCurrentBlockTimestampReturn { timestamp } = multicall_contract.getCurrentBlockTimestamp().call().await.unwrap(); assert_eq!(timestamp, U256::from(latest_block.header.timestamp)); - let MulticallContract::getCurrentBlockDifficultyReturn { difficulty } = + let Multicall::getCurrentBlockDifficultyReturn { difficulty } = multicall_contract.getCurrentBlockDifficulty().call().await.unwrap(); assert_eq!(difficulty, U256::from(latest_block.header.difficulty)); - let MulticallContract::getCurrentBlockGasLimitReturn { gaslimit } = + let Multicall::getCurrentBlockGasLimitReturn { gaslimit } = multicall_contract.getCurrentBlockGasLimit().call().await.unwrap(); assert_eq!(gaslimit, U256::from(latest_block.header.gas_limit)); - let MulticallContract::getCurrentBlockCoinbaseReturn { coinbase } = + let Multicall::getCurrentBlockCoinbaseReturn { coinbase } = multicall_contract.getCurrentBlockCoinbase().call().await.unwrap(); assert_eq!(coinbase, latest_block.header.miner); } diff --git a/crates/anvil/tests/it/api.rs b/crates/anvil/tests/it/api.rs index 8843efddc..f9aaf0dba 100644 --- a/crates/anvil/tests/it/api.rs +++ b/crates/anvil/tests/it/api.rs @@ -1,7 +1,7 @@ //! general eth api tests use crate::{ - abi::{MulticallContract, SimpleStorage}, + abi::{Multicall, SimpleStorage}, utils::{connect_pubsub_with_wallet, http_provider_with_signer}, }; use alloy_network::{EthereumWallet, TransactionBuilder}; @@ -118,11 +118,8 @@ async fn can_get_block_by_number() { let block = provider.get_block(BlockId::number(1), true.into()).await.unwrap().unwrap(); assert_eq!(block.transactions.len(), 1); - let block = provider - .get_block(BlockId::hash(block.header.hash.unwrap()), true.into()) - .await - .unwrap() - .unwrap(); + let block = + provider.get_block(BlockId::hash(block.header.hash), true.into()).await.unwrap().unwrap(); assert_eq!(block.transactions.len(), 1); } @@ -138,7 +135,7 @@ async fn can_get_pending_block() { let provider = connect_pubsub_with_wallet(&handle.http_endpoint(), signer).await; let block = provider.get_block(BlockId::pending(), false.into()).await.unwrap().unwrap(); - assert_eq!(block.header.number.unwrap(), 1); + assert_eq!(block.header.number, 1); let num = provider.get_block_number().await.unwrap(); assert_eq!(num, 0); @@ -153,12 +150,12 @@ async fn can_get_pending_block() { assert_eq!(num, 0); let block = provider.get_block(BlockId::pending(), false.into()).await.unwrap().unwrap(); - assert_eq!(block.header.number.unwrap(), 1); + assert_eq!(block.header.number, 1); assert_eq!(block.transactions.len(), 1); assert_eq!(block.transactions, BlockTransactions::Hashes(vec![*pending.tx_hash()])); let block = provider.get_block(BlockId::pending(), true.into()).await.unwrap().unwrap(); - assert_eq!(block.header.number.unwrap(), 1); + assert_eq!(block.header.number, 1); assert_eq!(block.transactions.len(), 1); } @@ -210,7 +207,7 @@ async fn can_call_on_pending_block() { api.anvil_set_auto_mine(false).await.unwrap(); - let _contract_pending = MulticallContract::deploy_builder(&provider) + let _contract_pending = Multicall::deploy_builder(&provider) .from(wallet.address()) .send() .await @@ -219,13 +216,13 @@ async fn can_call_on_pending_block() { .await .unwrap(); let contract_address = sender.create(0); - let contract = MulticallContract::new(contract_address, &provider); + let contract = Multicall::new(contract_address, &provider); let num = provider.get_block_number().await.unwrap(); assert_eq!(num, 0); // Ensure that we can get the block_number from the pending contract - let MulticallContract::aggregateReturn { blockNumber: ret_block_number, .. } = + let Multicall::aggregateReturn { blockNumber: ret_block_number, .. } = contract.aggregate(vec![]).block(BlockId::pending()).call().await.unwrap(); assert_eq!(ret_block_number, U256::from(1)); @@ -244,31 +241,28 @@ async fn can_call_on_pending_block() { let block_number = BlockNumberOrTag::Number(anvil_block_number as u64); let block = api.block_by_number(block_number).await.unwrap().unwrap(); - let MulticallContract::getCurrentBlockTimestampReturn { timestamp: ret_timestamp, .. } = - contract - .getCurrentBlockTimestamp() - .block(BlockId::number(anvil_block_number as u64)) - .call() - .await - .unwrap(); + let Multicall::getCurrentBlockTimestampReturn { timestamp: ret_timestamp, .. } = contract + .getCurrentBlockTimestamp() + .block(BlockId::number(anvil_block_number as u64)) + .call() + .await + .unwrap(); assert_eq!(block.header.timestamp, ret_timestamp.to::()); - let MulticallContract::getCurrentBlockGasLimitReturn { gaslimit: ret_gas_limit, .. } = - contract - .getCurrentBlockGasLimit() - .block(BlockId::number(anvil_block_number as u64)) - .call() - .await - .unwrap(); + let Multicall::getCurrentBlockGasLimitReturn { gaslimit: ret_gas_limit, .. } = contract + .getCurrentBlockGasLimit() + .block(BlockId::number(anvil_block_number as u64)) + .call() + .await + .unwrap(); assert_eq!(block.header.gas_limit, ret_gas_limit.to::()); - let MulticallContract::getCurrentBlockCoinbaseReturn { coinbase: ret_coinbase, .. } = - contract - .getCurrentBlockCoinbase() - .block(BlockId::number(anvil_block_number as u64)) - .call() - .await - .unwrap(); + let Multicall::getCurrentBlockCoinbaseReturn { coinbase: ret_coinbase, .. } = contract + .getCurrentBlockCoinbase() + .block(BlockId::number(anvil_block_number as u64)) + .call() + .await + .unwrap(); assert_eq!(block.header.miner, ret_coinbase); } } @@ -316,7 +310,7 @@ async fn can_call_with_state_override() { api.anvil_set_auto_mine(true).await.unwrap(); - let multicall_contract = MulticallContract::deploy(&provider).await.unwrap(); + let multicall_contract = Multicall::deploy(&provider).await.unwrap(); let init_value = "toto".to_string(); @@ -383,18 +377,12 @@ async fn can_mine_while_mining() { let total_blocks = 200; - let block_number = api - .block_by_number(BlockNumberOrTag::Latest) - .await - .unwrap() - .unwrap() - .header - .number - .unwrap(); + let block_number = + api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap().header.number; assert_eq!(block_number, 0); let block = api.block_by_number(BlockNumberOrTag::Number(block_number)).await.unwrap().unwrap(); - assert_eq!(block.header.number.unwrap(), 0); + assert_eq!(block.header.number, 0); let result = join!( api.anvil_mine(Some(U256::from(total_blocks / 2)), None), @@ -404,16 +392,10 @@ async fn can_mine_while_mining() { result.1.unwrap(); tokio::time::sleep(Duration::from_millis(100)).await; - let block_number = api - .block_by_number(BlockNumberOrTag::Latest) - .await - .unwrap() - .unwrap() - .header - .number - .unwrap(); + let block_number = + api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap().header.number; assert_eq!(block_number, total_blocks); let block = api.block_by_number(BlockNumberOrTag::Number(block_number)).await.unwrap().unwrap(); - assert_eq!(block.header.number.unwrap(), total_blocks); + assert_eq!(block.header.number, total_blocks); } diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 884bc0605..672d3b72f 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -734,7 +734,7 @@ async fn test_fork_init_base_fee() { let block = provider.get_block(BlockId::latest(), false.into()).await.unwrap().unwrap(); // - assert_eq!(block.header.number.unwrap(), 13184859u64); + assert_eq!(block.header.number, 13184859u64); let init_base_fee = block.header.base_fee_per_gas.unwrap(); assert_eq!(init_base_fee, 63739886069u128); @@ -850,7 +850,7 @@ async fn test_fork_uncles_fetch() { let count = provider.get_uncle_count(block_with_uncles.into()).await.unwrap(); assert_eq!(count as usize, block.uncles.len()); - let hash = BlockId::hash(block.header.hash.unwrap()); + let hash = BlockId::hash(block.header.hash); let count = provider.get_uncle_count(hash).await.unwrap(); assert_eq!(count as usize, block.uncles.len()); @@ -861,15 +861,15 @@ async fn test_fork_uncles_fetch() { .await .unwrap() .unwrap(); - assert_eq!(*uncle_hash, uncle.header.hash.unwrap()); + assert_eq!(*uncle_hash, uncle.header.hash); // Try with block hash let uncle = provider - .get_uncle(BlockId::hash(block.header.hash.unwrap()), uncle_idx as u64) + .get_uncle(BlockId::hash(block.header.hash), uncle_idx as u64) .await .unwrap() .unwrap(); - assert_eq!(*uncle_hash, uncle.header.hash.unwrap()); + assert_eq!(*uncle_hash, uncle.header.hash); } } @@ -905,11 +905,8 @@ async fn test_fork_block_transaction_count() { api.block_transaction_count_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); assert_eq!(latest_txs.to::(), 1); let latest_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); - let latest_txs = api - .block_transaction_count_by_hash(latest_block.header.hash.unwrap()) - .await - .unwrap() - .unwrap(); + let latest_txs = + api.block_transaction_count_by_hash(latest_block.header.hash).await.unwrap().unwrap(); assert_eq!(latest_txs.to::(), 1); // check txs count on an older block: 420000 has 3 txs on mainnet @@ -1173,7 +1170,7 @@ async fn test_arbitrum_fork_block_number() { // test block by number API call returns proper block number and `l1BlockNumber` is set let block_by_number = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); - assert_eq!(block_by_number.header.number.unwrap(), initial_block_number + 1); + assert_eq!(block_by_number.header.number, initial_block_number + 1); assert!(block_by_number.other.get("l1BlockNumber").is_some()); // revert to recorded snapshot and check block number @@ -1283,7 +1280,7 @@ async fn test_immutable_fork_transaction_hash() { let tx = api .backend .mined_block_by_number(BlockNumberOrTag::Number(fork_block_number)) - .and_then(|b| b.header.hash) + .map(|b| b.header.hash) .and_then(|hash| { api.backend.mined_transaction_by_block_hash_and_index(hash, expected.1.into()) }) diff --git a/crates/anvil/tests/it/ipc.rs b/crates/anvil/tests/it/ipc.rs index 786217ecd..4f13f8aaf 100644 --- a/crates/anvil/tests/it/ipc.rs +++ b/crates/anvil/tests/it/ipc.rs @@ -54,7 +54,7 @@ async fn test_sub_new_heads_ipc() { let blocks = provider.subscribe_blocks().await.unwrap().into_stream(); let blocks = blocks.take(3).collect::>().await; - let block_numbers = blocks.into_iter().map(|b| b.header.number.unwrap()).collect::>(); + let block_numbers = blocks.into_iter().map(|b| b.header.number).collect::>(); assert_eq!(block_numbers, vec![1, 2, 3]); } diff --git a/crates/anvil/tests/it/logs.rs b/crates/anvil/tests/it/logs.rs index 40d032d7c..1ce4ac64f 100644 --- a/crates/anvil/tests/it/logs.rs +++ b/crates/anvil/tests/it/logs.rs @@ -60,8 +60,7 @@ async fn get_past_events() { .unwrap() .unwrap() .header - .hash - .unwrap(); + .hash; let filter = Filter::new() .address(simple_storage_address) @@ -197,8 +196,7 @@ async fn watch_events() { .unwrap() .unwrap() .header - .hash - .unwrap(); + .hash; assert_eq!(log.1.block_hash.unwrap(), hash); } } diff --git a/crates/anvil/tests/it/optimism.rs b/crates/anvil/tests/it/optimism.rs index 4ca74f9fe..6446caf9c 100644 --- a/crates/anvil/tests/it/optimism.rs +++ b/crates/anvil/tests/it/optimism.rs @@ -5,10 +5,11 @@ use alloy_eips::eip2718::Encodable2718; use alloy_network::{EthereumWallet, TransactionBuilder}; use alloy_primitives::{b256, Address, TxHash, TxKind, U256}; use alloy_provider::Provider; -use alloy_rpc_types::{optimism::OptimismTransactionFields, TransactionRequest}; +use alloy_rpc_types::TransactionRequest; use alloy_serde::WithOtherFields; use anvil::{spawn, EthereumHardfork, NodeConfig}; use anvil_core::eth::transaction::optimism::DepositTransaction; +use op_alloy_rpc_types::OptimismTransactionFields; #[tokio::test(flavor = "multi_thread")] async fn test_deposits_not_supported_if_optimism_disabled() { @@ -32,6 +33,7 @@ async fn test_deposits_not_supported_if_optimism_disabled() { )), mint: Some(0), is_system_tx: Some(true), + deposit_receipt_version: None, } .into(), }; @@ -72,6 +74,7 @@ async fn test_send_value_deposit_transaction() { )), mint: Some(0), is_system_tx: Some(true), + deposit_receipt_version: None, } .into(), }; @@ -126,6 +129,7 @@ async fn test_send_value_raw_deposit_transaction() { )), mint: Some(0), is_system_tx: Some(true), + deposit_receipt_version: None, } .into(), }; diff --git a/crates/anvil/tests/it/otterscan.rs b/crates/anvil/tests/it/otterscan.rs index 2f2c0e3c1..55474079f 100644 --- a/crates/anvil/tests/it/otterscan.rs +++ b/crates/anvil/tests/it/otterscan.rs @@ -1,6 +1,6 @@ //! Tests for otterscan endpoints. -use crate::abi::MulticallContract; +use crate::abi::Multicall; use alloy_primitives::{address, Address, Bytes, U256}; use alloy_provider::Provider; use alloy_rpc_types::{ @@ -18,10 +18,10 @@ async fn erigon_get_header_by_number() { api.mine_one().await; let res0 = api.erigon_get_header_by_number(0.into()).await.unwrap().unwrap(); - assert_eq!(res0.header.number, Some(0)); + assert_eq!(res0.header.number, 0); let res1 = api.erigon_get_header_by_number(1.into()).await.unwrap().unwrap(); - assert_eq!(res1.header.number, Some(1)); + assert_eq!(res1.header.number, 1); } #[tokio::test(flavor = "multi_thread")] @@ -37,13 +37,8 @@ async fn ots_get_internal_operations_contract_deploy() { let provider = handle.http_provider(); let sender = handle.dev_accounts().next().unwrap(); - let contract_receipt = MulticallContract::deploy_builder(&provider) - .send() - .await - .unwrap() - .get_receipt() - .await - .unwrap(); + let contract_receipt = + Multicall::deploy_builder(&provider).send().await.unwrap().get_receipt().await.unwrap(); let res = api.ots_get_internal_operations(contract_receipt.transaction_hash).await.unwrap(); assert_eq!( @@ -181,7 +176,7 @@ async fn ots_has_code() { // no code in the address before deploying assert!(!api.ots_has_code(contract_address, BlockNumberOrTag::Number(1)).await.unwrap()); - let contract_builder = MulticallContract::deploy_builder(&provider); + let contract_builder = Multicall::deploy_builder(&provider); let contract_receipt = contract_builder.send().await.unwrap().get_receipt().await.unwrap(); let num = provider.get_block_number().await.unwrap(); @@ -501,13 +496,8 @@ async fn ots_get_contract_creator() { let provider = handle.http_provider(); let sender = handle.dev_accounts().next().unwrap(); - let receipt = MulticallContract::deploy_builder(&provider) - .send() - .await - .unwrap() - .get_receipt() - .await - .unwrap(); + let receipt = + Multicall::deploy_builder(&provider).send().await.unwrap().get_receipt().await.unwrap(); let contract_address = receipt.contract_address.unwrap(); let creator = api.ots_get_contract_creator(contract_address).await.unwrap().unwrap(); diff --git a/crates/anvil/tests/it/pubsub.rs b/crates/anvil/tests/it/pubsub.rs index fec22ca41..a343f7c70 100644 --- a/crates/anvil/tests/it/pubsub.rs +++ b/crates/anvil/tests/it/pubsub.rs @@ -23,7 +23,7 @@ async fn test_sub_new_heads() { api.anvil_set_interval_mining(1).unwrap(); let blocks = blocks.into_stream().take(3).collect::>().await; - let block_numbers = blocks.into_iter().map(|b| b.header.number.unwrap()).collect::>(); + let block_numbers = blocks.into_iter().map(|b| b.header.number).collect::>(); assert_eq!(block_numbers, vec![1, 2, 3]); } @@ -233,7 +233,7 @@ async fn test_subscriptions() { let (_api, handle) = spawn(NodeConfig::test().with_blocktime(Some(std::time::Duration::from_secs(1)))).await; let provider = connect_pubsub(&handle.ws_endpoint()).await; - let sub_id: U256 = provider.raw_request("eth_subscribe".into(), ["newHeads"]).await.unwrap(); + let sub_id = provider.raw_request("eth_subscribe".into(), ["newHeads"]).await.unwrap(); let stream: Subscription = provider.get_subscription(sub_id).await.unwrap(); let blocks = stream .into_stream() @@ -241,7 +241,7 @@ async fn test_subscriptions() { .collect::>() .await .into_iter() - .map(|b| b.header.number.unwrap()) + .map(|b| b.header.number) .collect::>(); assert_eq!(blocks, vec![1, 2, 3]) @@ -261,7 +261,7 @@ async fn test_sub_new_heads_fast() { let mut block_numbers = Vec::new(); for _ in 0..num { api.mine_one().await; - let block_number = blocks.next().await.unwrap().header.number.unwrap(); + let block_number = blocks.next().await.unwrap().header.number; block_numbers.push(block_number); } diff --git a/crates/anvil/tests/it/traces.rs b/crates/anvil/tests/it/traces.rs index 782e68d72..5c9d87ca1 100644 --- a/crates/anvil/tests/it/traces.rs +++ b/crates/anvil/tests/it/traces.rs @@ -1,5 +1,5 @@ use crate::{ - abi::{MulticallContract, SimpleStorage}, + abi::{Multicall, SimpleStorage}, fork::fork_config, utils::http_provider_with_signer, }; @@ -155,7 +155,7 @@ async fn test_call_tracer_debug_trace_call() { let deployer: EthereumWallet = wallets[0].clone().into(); let provider = http_provider_with_signer(&handle.http_endpoint(), deployer); - let multicall_contract = MulticallContract::deploy(&provider).await.unwrap(); + let multicall_contract = Multicall::deploy(&provider).await.unwrap(); let simple_storage_contract = SimpleStorage::deploy(&provider, "init value".to_string()).await.unwrap(); @@ -163,7 +163,7 @@ async fn test_call_tracer_debug_trace_call() { let set_value = simple_storage_contract.setValue("bar".to_string()); let set_value_calldata = set_value.calldata(); - let internal_call_tx_builder = multicall_contract.aggregate(vec![MulticallContract::Call { + let internal_call_tx_builder = multicall_contract.aggregate(vec![Multicall::Call { target: *simple_storage_contract.address(), callData: set_value_calldata.to_owned(), }]); diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index 330eb7a39..0827bbac1 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -1,5 +1,5 @@ use crate::{ - abi::{Greeter, MulticallContract, SimpleStorage}, + abi::{Greeter, Multicall, SimpleStorage}, utils::{connect_pubsub, http_provider_with_signer}, }; use alloy_network::{EthereumWallet, TransactionBuilder}; @@ -477,7 +477,7 @@ async fn get_blocktimestamp_works() { let (api, handle) = spawn(NodeConfig::test()).await; let provider = handle.http_provider(); - let contract = MulticallContract::deploy(provider.clone()).await.unwrap(); + let contract = Multicall::deploy(provider.clone()).await.unwrap(); let timestamp = contract.getCurrentBlockTimestamp().call().await.unwrap().timestamp; @@ -557,8 +557,7 @@ async fn call_past_state() { .unwrap() .unwrap() .header - .hash - .unwrap(); + .hash; let value = contract.getValue().block(BlockId::Hash(hash.into())).call().await.unwrap(); assert_eq!(value._0, "initial value"); } @@ -996,7 +995,7 @@ async fn test_tx_access_list() { let sender = Address::random(); let other_acc = Address::random(); - let multicall = MulticallContract::deploy(provider.clone()).await.unwrap(); + let multicall = Multicall::deploy(provider.clone()).await.unwrap(); let simple_storage = SimpleStorage::deploy(provider.clone(), "foo".to_string()).await.unwrap(); // when calling `setValue` on SimpleStorage, both the `lastSender` and `_value` storages are @@ -1044,7 +1043,7 @@ async fn test_tx_access_list() { // With a subcall to another contract, the AccessList should be the same as when calling the // subcontract directly (given that the proxy contract doesn't read/write any state) - let subcall_tx = multicall.aggregate(vec![MulticallContract::Call { + let subcall_tx = multicall.aggregate(vec![Multicall::Call { target: *simple_storage.address(), callData: set_value_calldata.to_owned(), }]); diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 2b2a0db90..46f8eac95 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -82,7 +82,7 @@ tempfile.workspace = true tokio = { workspace = true, features = ["macros", "signal"] } tracing.workspace = true yansi.workspace = true -evmole = "0.4.1" +evmole.workspace = true [target.'cfg(unix)'.dependencies] tikv-jemallocator = { workspace = true, optional = true } diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index 7a5e7980c..b67c0ed11 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -176,11 +176,11 @@ impl RunArgs { let pb = init_progress(block.transactions.len() as u64, "tx"); pb.set_position(0); - let BlockTransactions::Full(txs) = block.transactions else { + let BlockTransactions::Full(ref txs) = block.transactions else { return Err(eyre::eyre!("Could not get block txs")) }; - for (index, tx) in txs.into_iter().enumerate() { + for (index, tx) in txs.iter().enumerate() { // System transactions such as on L2s don't contain any pricing info so // we skip them otherwise this would cause // reverts @@ -194,7 +194,7 @@ impl RunArgs { break; } - configure_tx_env(&mut env, &tx); + configure_tx_env(&mut env, &tx.inner); if let Some(to) = tx.to { trace!(tx=?tx.hash,?to, "executing previous call transaction"); @@ -231,7 +231,7 @@ impl RunArgs { let result = { executor.set_trace_printer(self.trace_printer); - configure_tx_env(&mut env, &tx); + configure_tx_env(&mut env, &tx.inner); if let Some(to) = tx.to { trace!(tx=?tx.hash, to=?to, "executing call transaction"); diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index b0f29af43..28009dbdc 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -258,13 +258,14 @@ async fn main_args(args: CastArgs) -> Result<()> { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; let number = match block { - Some(id) => provider - .get_block(id, false.into()) - .await? - .ok_or_else(|| eyre::eyre!("block {id:?} not found"))? - .header - .number - .ok_or_else(|| eyre::eyre!("block {id:?} has no block number"))?, + Some(id) => { + provider + .get_block(id, false.into()) + .await? + .ok_or_else(|| eyre::eyre!("block {id:?} not found"))? + .header + .number + } None => Cast::new(provider).block_number().await?, }; println!("{number}"); diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index c4d756ad7..03355213d 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -872,7 +872,7 @@ where BlockId::Hash(hash) => { let block = self.provider.get_block_by_hash(hash.block_hash, false.into()).await?; - Ok(block.map(|block| block.header.number.unwrap()).map(BlockNumberOrTag::from)) + Ok(block.map(|block| block.header.number).map(BlockNumberOrTag::from)) } }, None => Ok(None), @@ -937,7 +937,7 @@ where Either::Right(futures::future::pending()) } => { if let (Some(block), Some(to_block)) = (block, to_block_number) { - if block.header.number.map_or(false, |bn| bn > to_block) { + if block.header.number > to_block { break; } } @@ -1981,7 +1981,7 @@ impl SimpleCast { ( hex::encode_prefixed(s), evmole::function_arguments(&code, &s, 0), - evmole::function_state_mutability(&code, &s, 0), + evmole::function_state_mutability(&code, &s, 0).as_json_str(), ) }) .collect()) diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index bb52936a9..01fdaef86 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -5,7 +5,10 @@ use alloy_primitives::{b256, B256}; use anvil::{EthereumHardfork, NodeConfig}; use foundry_test_utils::{ casttest, file, - rpc::{next_http_rpc_endpoint, next_rpc_endpoint, next_ws_rpc_endpoint}, + rpc::{ + next_http_rpc_endpoint, next_mainnet_etherscan_api_key, next_rpc_endpoint, + next_ws_rpc_endpoint, + }, str, util::OutputExt, }; @@ -1115,20 +1118,22 @@ casttest!(interface_no_constructor, |prj, cmd| { let path = prj.root().join("interface.json"); fs::write(&path, interface).unwrap(); // Call `cast find-block` - cmd.args(["interface"]).arg(&path).assert_success().stdout_eq(str![[ + cmd.arg("interface").arg(&path).assert_success().stdout_eq(str![[ r#"// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.4; -interface Interface { +library IIntegrationManager { type SpendAssetsHandleType is uint8; +} +interface Interface { function getIntegrationManager() external view returns (address integrationManager_); function lend(address _vaultProxy, bytes memory, bytes memory _assetData) external; function parseAssetsForAction(address, bytes4 _selector, bytes memory _actionData) external view returns ( - SpendAssetsHandleType spendAssetsHandleType_, + IIntegrationManager.SpendAssetsHandleType spendAssetsHandleType_, address[] memory spendAssets_, uint256[] memory spendAssetAmounts_, address[] memory incomingAssets_, @@ -1144,11 +1149,15 @@ interface Interface { // tests that fetches WETH interface from etherscan // casttest!(fetch_weth_interface_from_etherscan, |_prj, cmd| { - let weth_address = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"; - let api_key = "ZUB97R31KSYX7NYVW6224Q6EYY6U56H591"; - cmd.args(["interface", "--etherscan-api-key", api_key, weth_address]) - .assert_success() - .stdout_eq(str![[r#"// SPDX-License-Identifier: UNLICENSED + cmd.args([ + "interface", + "--etherscan-api-key", + &next_mainnet_etherscan_api_key(), + "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + ]) + .assert_success() + .stdout_eq(str![[r#" +// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.4; interface WETH9 { diff --git a/crates/common/fmt/src/ui.rs b/crates/common/fmt/src/ui.rs index f13e0f021..64c520769 100644 --- a/crates/common/fmt/src/ui.rs +++ b/crates/common/fmt/src/ui.rs @@ -3,10 +3,10 @@ use alloy_consensus::{AnyReceiptEnvelope, Eip658Value, Receipt, ReceiptWithBloom, TxType}; use alloy_primitives::{hex, Address, Bloom, Bytes, FixedBytes, Uint, B256, I256, U256, U64}; use alloy_rpc_types::{ - AccessListItem, AnyTransactionReceipt, Block, BlockTransactions, Log, Transaction, - TransactionReceipt, + AccessListItem, AnyNetworkBlock, AnyTransactionReceipt, Block, BlockTransactions, Log, + Transaction, TransactionReceipt, }; -use alloy_serde::OtherFields; +use alloy_serde::{OtherFields, WithOtherFields}; use serde::Deserialize; /// length of the name column for pretty formatting `{:>20}{value}` @@ -267,7 +267,7 @@ transactionIndex: {}", } } -impl UIfmt for Block { +impl UIfmt for Block { fn pretty(&self) -> String { format!( " @@ -338,7 +338,7 @@ to {} transactionIndex {} type {} value {} -yParity {}{}", +yParity {}", self.access_list.as_deref().map(Vec::as_slice).unwrap_or(&[]).pretty(), self.block_hash.pretty(), self.block_number.pretty(), @@ -356,7 +356,6 @@ yParity {}{}", self.transaction_type.unwrap(), self.value.pretty(), self.signature.map(|s| s.v).pretty(), - self.other.pretty() ), Some(2) => format!( " @@ -377,7 +376,7 @@ to {} transactionIndex {} type {} value {} -yParity {}{}", +yParity {}", self.access_list.as_deref().map(Vec::as_slice).unwrap_or(&[]).pretty(), self.block_hash.pretty(), self.block_number.pretty(), @@ -396,7 +395,6 @@ yParity {}{}", self.transaction_type.unwrap(), self.value.pretty(), self.signature.map(|s| s.v).pretty(), - self.other.pretty() ), Some(3) => format!( " @@ -419,7 +417,7 @@ to {} transactionIndex {} type {} value {} -yParity {}{}", +yParity {}", self.access_list.as_deref().map(Vec::as_slice).unwrap_or(&[]).pretty(), self.blob_versioned_hashes.as_deref().unwrap_or(&[]).pretty(), self.block_hash.pretty(), @@ -440,7 +438,6 @@ yParity {}{}", self.transaction_type.unwrap(), self.value.pretty(), self.signature.map(|s| s.v).pretty(), - self.other.pretty() ), Some(4) => format!( " @@ -462,7 +459,7 @@ to {} transactionIndex {} type {} value {} -yParity {}{}", +yParity {}", self.access_list.as_deref().map(Vec::as_slice).unwrap_or(&[]).pretty(), self.authorization_list .as_ref() @@ -485,7 +482,6 @@ yParity {}{}", self.transaction_type.unwrap(), self.value.pretty(), self.signature.map(|s| s.v).pretty(), - self.other.pretty() ), _ => format!( " @@ -502,7 +498,7 @@ s {} to {} transactionIndex {} v {} -value {}{}", +value {}", self.block_hash.pretty(), self.block_number.pretty(), self.from.pretty(), @@ -517,12 +513,17 @@ value {}{}", self.transaction_index.pretty(), self.signature.map(|s| s.v).pretty(), self.value.pretty(), - self.other.pretty() ), } } } +impl UIfmt for WithOtherFields { + fn pretty(&self) -> String { + format!("{}{}", self.inner.pretty(), self.other.pretty()) + } +} + /// Various numerical ethereum types used for pretty printing #[derive(Clone, Debug, Deserialize)] #[serde(untagged)] @@ -570,17 +571,12 @@ pub fn get_pretty_tx_attr(transaction: &Transaction, attr: &str) -> Option Some(transaction.transaction_index.pretty()), "v" => transaction.signature.map(|s| s.v.pretty()), "value" => Some(transaction.value.pretty()), - other => { - if let Some(value) = transaction.other.get(other) { - return Some(value.to_string().trim_matches('"').to_string()) - } - None - } + _ => None, } } /// Returns the `UiFmt::pretty()` formatted attribute of the given block -pub fn get_pretty_block_attr(block: &Block, attr: &str) -> Option { +pub fn get_pretty_block_attr(block: &AnyNetworkBlock, attr: &str) -> Option { match attr { "baseFeePerGas" | "base_fee_per_gas" => Some(block.header.base_fee_per_gas.pretty()), "difficulty" => Some(block.header.difficulty.pretty()), @@ -611,7 +607,7 @@ pub fn get_pretty_block_attr(block: &Block, attr: &str) -> Option { } } -fn pretty_block_basics(block: &Block) -> String { +fn pretty_block_basics(block: &Block) -> String { format!( " baseFeePerGas {} @@ -633,7 +629,7 @@ size {} stateRoot {} timestamp {} ({}) withdrawalsRoot {} -totalDifficulty {}{}", +totalDifficulty {}", block.header.base_fee_per_gas.pretty(), block.header.difficulty.pretty(), block.header.extra_data.pretty(), @@ -657,7 +653,6 @@ totalDifficulty {}{}", .to_rfc2822(), block.header.withdrawals_root.pretty(), block.header.total_difficulty.pretty(), - block.other.pretty() ) } @@ -711,7 +706,7 @@ mod tests { } "#; - let tx: Transaction = serde_json::from_str(s).unwrap(); + let tx: WithOtherFields = serde_json::from_str(s).unwrap(); assert_eq!(tx.pretty().trim(), r" blockHash 0x02b853cf50bc1c335b70790f93d5a390a35a166bea9c895e685cc866e4961cae @@ -1058,7 +1053,7 @@ value 0".to_string(); } ); - let block: Block = serde_json::from_value(json).unwrap(); + let block: AnyNetworkBlock = serde_json::from_value(json).unwrap(); assert_eq!(None, get_pretty_block_attr(&block, "")); assert_eq!(Some("7".to_string()), get_pretty_block_attr(&block, "baseFeePerGas")); diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index be3b0135e..e98ac00c5 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -9,9 +9,7 @@ use crate::{ }; use alloy_genesis::GenesisAccount; use alloy_primitives::{keccak256, uint, Address, TxKind, B256, U256}; -use alloy_rpc_types::{ - Block, BlockNumberOrTag, BlockTransactions, Transaction, TransactionRequest, -}; +use alloy_rpc_types::{Block, BlockNumberOrTag, Transaction, TransactionRequest}; use alloy_serde::WithOtherFields; use eyre::Context; use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE}; @@ -828,7 +826,7 @@ impl Backend { &self, id: LocalForkId, transaction: B256, - ) -> eyre::Result<(u64, Block)> { + ) -> eyre::Result<(u64, Block>)> { let fork = self.inner.get_fork_by_id(id)?; let tx = fork.db.db.get_transaction(transaction)?; @@ -839,12 +837,13 @@ impl Backend { // we need to subtract 1 here because we want the state before the transaction // was mined let fork_block = tx_block - 1; - Ok((fork_block, block)) + Ok((fork_block, block.inner)) } else { let block = fork.db.db.get_full_block(BlockNumberOrTag::Latest)?; - let number = - block.header.number.ok_or_else(|| BackendError::msg("missing block number"))?; + let number = block.header.number; + + let block = block.inner; Ok((number, block)) } @@ -869,33 +868,31 @@ impl Backend { let fork = self.inner.get_fork_by_id_mut(id)?; let full_block = fork.db.db.get_full_block(env.block.number.to::())?; - if let BlockTransactions::Full(txs) = full_block.transactions { - for tx in txs.into_iter() { - // System transactions such as on L2s don't contain any pricing info so we skip them - // otherwise this would cause reverts - if is_known_system_sender(tx.from) || - tx.transaction_type == Some(SYSTEM_TRANSACTION_TYPE) - { - trace!(tx=?tx.hash, "skipping system transaction"); - continue; - } + for tx in full_block.transactions.clone().into_transactions() { + // System transactions such as on L2s don't contain any pricing info so we skip them + // otherwise this would cause reverts + if is_known_system_sender(tx.from) || + tx.transaction_type == Some(SYSTEM_TRANSACTION_TYPE) + { + trace!(tx=?tx.hash, "skipping system transaction"); + continue; + } - if tx.hash == tx_hash { - // found the target transaction - return Ok(Some(tx)) - } - trace!(tx=?tx.hash, "committing transaction"); - - commit_transaction( - WithOtherFields::new(tx), - env.clone(), - journaled_state, - fork, - &fork_id, - &persistent_accounts, - &mut NoOpInspector, - )?; + if tx.hash == tx_hash { + // found the target transaction + return Ok(Some(tx.inner)) } + trace!(tx=?tx.hash, "committing transaction"); + + commit_transaction( + tx, + env.clone(), + journaled_state, + fork, + &fork_id, + &persistent_accounts, + &mut NoOpInspector, + )?; } Ok(None) @@ -1210,7 +1207,7 @@ impl DatabaseExt for Backend { // roll the fork to the transaction's block or latest if it's pending self.roll_fork(Some(id), fork_block, env, journaled_state)?; - update_env_block(env, fork_block, &block); + update_env_block(env, &block); // replay all transactions that came before let env = env.clone(); @@ -1240,10 +1237,10 @@ impl DatabaseExt for Backend { // This is a bit ambiguous because the user wants to transact an arbitrary transaction in the current context, but we're assuming the user wants to transact the transaction as it was mined. Usually this is used in a combination of a fork at the transaction's parent transaction in the block and then the transaction is transacted: // So we modify the env to match the transaction's block - let (fork_block, block) = + let (_fork_block, block) = self.get_block_number_and_block_for_transaction(id, transaction)?; let mut env = env.clone(); - update_env_block(&mut env, fork_block, &block); + update_env_block(&mut env, &block); let env = self.env_with_handler_cfg(env); let fork = self.inner.get_fork_by_id_mut(id)?; @@ -1895,14 +1892,14 @@ fn is_contract_in_state(journaled_state: &JournaledState, acc: Address) -> bool } /// Updates the env's block with the block's data -fn update_env_block(env: &mut Env, fork_block: u64, block: &Block) { +fn update_env_block(env: &mut Env, block: &Block) { env.block.timestamp = U256::from(block.header.timestamp); env.block.coinbase = block.header.miner; env.block.difficulty = block.header.difficulty; env.block.prevrandao = Some(block.header.mix_hash.unwrap_or_default()); env.block.basefee = U256::from(block.header.base_fee_per_gas.unwrap_or_default()); env.block.gas_limit = U256::from(block.header.gas_limit); - env.block.number = U256::from(block.header.number.unwrap_or(fork_block)); + env.block.number = U256::from(block.header.number); } /// Executes the given transaction and commits state changes to the database _and_ the journaled @@ -1916,7 +1913,7 @@ fn commit_transaction>( persistent_accounts: &HashSet
, inspector: I, ) -> eyre::Result<()> { - configure_tx_env(&mut env.env, &tx); + configure_tx_env(&mut env.env, &tx.inner); let now = Instant::now(); let res = { diff --git a/crates/evm/core/src/fork/init.rs b/crates/evm/core/src/fork/init.rs index f60b99cb9..b32fece5a 100644 --- a/crates/evm/core/src/fork/init.rs +++ b/crates/evm/core/src/fork/init.rs @@ -1,7 +1,10 @@ use crate::utils::apply_chain_and_block_specific_env_changes; use alloy_primitives::{Address, U256}; -use alloy_provider::{Network, Provider}; -use alloy_rpc_types::{Block, BlockNumberOrTag}; +use alloy_provider::{ + network::{BlockResponse, HeaderResponse}, + Network, Provider, +}; +use alloy_rpc_types::BlockNumberOrTag; use alloy_transport::Transport; use eyre::WrapErr; use foundry_common::NON_ARCHIVE_NODE_WARNING; @@ -18,7 +21,7 @@ pub async fn environment>( pin_block: Option, origin: Address, disable_block_gas_limit: bool, -) -> eyre::Result<(Env, Block)> { +) -> eyre::Result<(Env, N::BlockResponse)> { let block_number = if let Some(pin_block) = pin_block { pin_block } else { @@ -61,25 +64,25 @@ pub async fn environment>( let mut env = Env { cfg, block: BlockEnv { - number: U256::from(block.header.number.expect("block number not found")), - timestamp: U256::from(block.header.timestamp), - coinbase: block.header.miner, - difficulty: block.header.difficulty, - prevrandao: Some(block.header.mix_hash.unwrap_or_default()), - basefee: U256::from(block.header.base_fee_per_gas.unwrap_or_default()), - gas_limit: U256::from(block.header.gas_limit), + number: U256::from(block.header().number()), + timestamp: U256::from(block.header().timestamp()), + coinbase: block.header().coinbase(), + difficulty: block.header().difficulty(), + prevrandao: block.header().mix_hash(), + basefee: U256::from(block.header().base_fee_per_gas().unwrap_or_default()), + gas_limit: U256::from(block.header().gas_limit()), ..Default::default() }, tx: TxEnv { caller: origin, gas_price: U256::from(gas_price.unwrap_or(fork_gas_price)), chain_id: Some(override_chain_id.unwrap_or(rpc_chain_id)), - gas_limit: block.header.gas_limit as u64, + gas_limit: block.header().gas_limit() as u64, ..Default::default() }, }; - apply_chain_and_block_specific_env_changes(&mut env, &block); + apply_chain_and_block_specific_env_changes::(&mut env, &block); Ok((env, block)) } diff --git a/crates/evm/core/src/fork/multi.rs b/crates/evm/core/src/fork/multi.rs index 001c65469..a39b543ea 100644 --- a/crates/evm/core/src/fork/multi.rs +++ b/crates/evm/core/src/fork/multi.rs @@ -5,6 +5,7 @@ use super::CreateFork; use alloy_primitives::U256; +use alloy_provider::network::{BlockResponse, HeaderResponse}; use alloy_transport::layers::RetryBackoffService; use foundry_common::provider::{ runtime_transport::RuntimeTransport, ProviderBuilder, RetryProvider, @@ -524,7 +525,7 @@ async fn create_fork(mut fork: CreateFork) -> eyre::Result<(ForkId, CreatedFork, // We need to use the block number from the block because the env's number can be different on // some L2s (e.g. Arbitrum). - let number = block.header.number.unwrap_or(meta.block_env.number.to()); + let number = block.header().number(); // Determine the cache path if caching is enabled. let cache_path = if fork.enable_caching { diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index f9e674e09..7a41d0756 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -2,7 +2,7 @@ use super::fork::environment; use crate::fork::CreateFork; use alloy_primitives::{Address, B256, U256}; use alloy_provider::Provider; -use alloy_rpc_types::Block; +use alloy_rpc_types::AnyNetworkBlock; use eyre::WrapErr; use foundry_common::{provider::ProviderBuilder, ALCHEMY_FREE_TIER_CUPS}; use foundry_config::{Chain, Config}; @@ -86,7 +86,7 @@ impl EvmOpts { pub async fn fork_evm_env( &self, fork_url: impl AsRef, - ) -> eyre::Result<(revm::primitives::Env, Block)> { + ) -> eyre::Result<(revm::primitives::Env, AnyNetworkBlock)> { let fork_url = fork_url.as_ref(); let provider = ProviderBuilder::new(fork_url) .compute_units_per_second(self.get_compute_units_per_second()) diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index 21284e780..27db37eaf 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -2,7 +2,11 @@ pub use crate::ic::*; use crate::{constants::DEFAULT_CREATE2_DEPLOYER, precompiles::ALPHANET_P256, InspectorExt}; use alloy_json_abi::{Function, JsonAbi}; use alloy_primitives::{Address, Selector, TxKind, U256}; -use alloy_rpc_types::{Block, Transaction}; +use alloy_provider::{ + network::{BlockResponse, HeaderResponse}, + Network, +}; +use alloy_rpc_types::Transaction; use foundry_config::NamedChain; use revm::{ db::WrapDatabaseRef, @@ -24,9 +28,12 @@ pub use revm::primitives::EvmState as StateChangeset; /// - applies chain specifics: on Arbitrum `block.number` is the L1 block /// /// Should be called with proper chain id (retrieved from provider if not provided). -pub fn apply_chain_and_block_specific_env_changes(env: &mut revm::primitives::Env, block: &Block) { +pub fn apply_chain_and_block_specific_env_changes( + env: &mut revm::primitives::Env, + block: &N::BlockResponse, +) { if let Ok(chain) = NamedChain::try_from(env.cfg.chain_id) { - let block_number = block.header.number.unwrap_or_default(); + let block_number = block.header().number(); match chain { NamedChain::Mainnet => { @@ -43,10 +50,14 @@ pub fn apply_chain_and_block_specific_env_changes(env: &mut revm::primitives::En NamedChain::ArbitrumTestnet => { // on arbitrum `block.number` is the L1 block which is included in the // `l1BlockNumber` field - if let Some(l1_block_number) = block.other.get("l1BlockNumber").cloned() { - if let Ok(l1_block_number) = serde_json::from_value::(l1_block_number) { - env.block.number = l1_block_number; - } + if let Some(l1_block_number) = block + .other_fields() + .and_then(|other| other.get("l1BlockNumber").cloned()) + .and_then(|l1_block_number| { + serde_json::from_value::(l1_block_number).ok() + }) + { + env.block.number = l1_block_number; } } _ => {} @@ -54,7 +65,7 @@ pub fn apply_chain_and_block_specific_env_changes(env: &mut revm::primitives::En } // if difficulty is `0` we assume it's past merge - if block.header.difficulty.is_zero() { + if block.header().difficulty().is_zero() { env.block.difficulty = env.block.prevrandao.unwrap_or_default().into(); } } diff --git a/crates/forge/bin/cmd/clone.rs b/crates/forge/bin/cmd/clone.rs index 4af586964..98ef95d40 100644 --- a/crates/forge/bin/cmd/clone.rs +++ b/crates/forge/bin/cmd/clone.rs @@ -612,7 +612,7 @@ mod tests { use super::*; use alloy_primitives::hex; use foundry_compilers::Artifact; - use foundry_test_utils::rpc::next_etherscan_api_key; + use foundry_test_utils::rpc::next_mainnet_etherscan_api_key; use std::collections::BTreeMap; fn assert_successful_compilation(root: &PathBuf) -> ProjectCompileOutput { @@ -690,7 +690,7 @@ mod tests { // create folder if not exists std::fs::create_dir_all(&data_folder).unwrap(); // create metadata.json and creation_data.json - let client = Client::new(Chain::mainnet(), next_etherscan_api_key()).unwrap(); + let client = Client::new(Chain::mainnet(), next_mainnet_etherscan_api_key()).unwrap(); let meta = client.contract_source_code(address).await.unwrap(); // dump json let json = serde_json::to_string_pretty(&meta).unwrap(); diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 1a8d2cb7e..4e860bc64 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -7,7 +7,7 @@ use foundry_config::{ }; use foundry_test_utils::{ foundry_compilers::PathStyle, - rpc::next_etherscan_api_key, + rpc::next_mainnet_etherscan_api_key, util::{pretty_err, read_string, OutputExt, TestCommand}, }; use semver::Version; @@ -538,7 +538,7 @@ forgetest!(can_clone, |prj, cmd| { cmd.args([ "clone", "--etherscan-api-key", - next_etherscan_api_key().as_str(), + next_mainnet_etherscan_api_key().as_str(), "0x044b75f554b886A065b9567891e45c79542d7357", ]) .arg(prj.root()) @@ -567,7 +567,7 @@ forgetest!(can_clone_quiet, |prj, cmd| { cmd.args([ "clone", "--etherscan-api-key", - next_etherscan_api_key().as_str(), + next_mainnet_etherscan_api_key().as_str(), "--quiet", "0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec", ]) @@ -585,7 +585,7 @@ forgetest!(can_clone_no_remappings_txt, |prj, cmd| { cmd.args([ "clone", "--etherscan-api-key", - next_etherscan_api_key().as_str(), + next_mainnet_etherscan_api_key().as_str(), "--no-remappings-txt", "0x33e690aEa97E4Ef25F0d140F1bf044d663091DAf", ]) @@ -620,7 +620,7 @@ forgetest!(can_clone_keep_directory_structure, |prj, cmd| { .args([ "clone", "--etherscan-api-key", - next_etherscan_api_key().as_str(), + next_mainnet_etherscan_api_key().as_str(), "--keep-directory-structure", "0x33e690aEa97E4Ef25F0d140F1bf044d663091DAf", ]) diff --git a/crates/forge/tests/cli/verify_bytecode.rs b/crates/forge/tests/cli/verify_bytecode.rs index 2b28570e7..7e615b565 100644 --- a/crates/forge/tests/cli/verify_bytecode.rs +++ b/crates/forge/tests/cli/verify_bytecode.rs @@ -2,7 +2,7 @@ use foundry_compilers::artifacts::{BytecodeHash, EvmVersion}; use foundry_config::Config; use foundry_test_utils::{ forgetest_async, - rpc::{next_etherscan_api_key, next_http_archive_rpc_endpoint}, + rpc::{next_http_archive_rpc_endpoint, next_mainnet_etherscan_api_key}, util::OutputExt, TestCommand, TestProject, }; @@ -19,7 +19,7 @@ fn test_verify_bytecode( verifier_url: &str, expected_matches: (&str, &str), ) { - let etherscan_key = next_etherscan_api_key(); + let etherscan_key = next_mainnet_etherscan_api_key(); let rpc_url = next_http_archive_rpc_endpoint(); // fetch and flatten source code @@ -73,7 +73,7 @@ fn test_verify_bytecode_with_ignore( ignore: &str, chain: &str, ) { - let etherscan_key = next_etherscan_api_key(); + let etherscan_key = next_mainnet_etherscan_api_key(); let rpc_url = next_http_archive_rpc_endpoint(); // fetch and flatten source code diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index ca0f60d0d..6e5e900ce 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -32,6 +32,10 @@ tracing-subscriber = { workspace = true, features = ["env-filter"] } rand.workspace = true snapbox = { version = "0.6.9", features = ["json", "regex"] } +[dev-dependencies] +tokio.workspace = true +foundry-block-explorers.workspace = true + [features] # feature for integration tests that test external projects external-integration-tests = [] diff --git a/crates/test-utils/src/rpc.rs b/crates/test-utils/src/rpc.rs index 96415af3f..305c8a1c8 100644 --- a/crates/test-utils/src/rpc.rs +++ b/crates/test-utils/src/rpc.rs @@ -51,9 +51,7 @@ static ALCHEMY_KEYS: LazyLock> = LazyLock::new(|| { "UVatYU2Ax0rX6bDiqddeTRDdcCxzdpoE", "bVjX9v-FpmUhf5R_oHIgwJx2kXvYPRbx", ]; - keys.shuffle(&mut rand::thread_rng()); - keys }); @@ -69,19 +67,18 @@ static ETHERSCAN_MAINNET_KEYS: LazyLock> = LazyLock::new(|| { "C7I2G4JTA5EPYS42Z8IZFEIMQNI5GXIJEV", "A15KZUMZXXCK1P25Y1VP1WGIVBBHIZDS74", "3IA6ASNQXN8WKN7PNFX7T72S9YG56X9FPG", + "ZUB97R31KSYX7NYVW6224Q6EYY6U56H591", + // Optimism + // "JQNGFHINKS1W7Y5FRXU4SPBYF43J3NYK46", ]; - keys.shuffle(&mut rand::thread_rng()); - keys }); -/// counts how many times a rpc endpoint was requested for _mainnet_ -static NEXT_RPC_ENDPOINT: AtomicUsize = AtomicUsize::new(0); - -// returns the current value of the atomic counter and increments it +/// Returns the next index to use. fn next() -> usize { - NEXT_RPC_ENDPOINT.fetch_add(1, Ordering::SeqCst) + static NEXT_INDEX: AtomicUsize = AtomicUsize::new(0); + NEXT_INDEX.fetch_add(1, Ordering::SeqCst) } fn num_keys() -> usize { @@ -125,7 +122,7 @@ pub fn next_ws_archive_rpc_endpoint() -> String { } /// Returns the next etherscan api key -pub fn next_etherscan_api_key() -> String { +pub fn next_mainnet_etherscan_api_key() -> String { let idx = next() % ETHERSCAN_MAINNET_KEYS.len(); ETHERSCAN_MAINNET_KEYS[idx].to_string() } @@ -178,15 +175,48 @@ fn next_url(is_ws: bool, chain: NamedChain) -> String { #[cfg(test)] mod tests { use super::*; - use std::collections::HashSet; - - #[test] - #[ignore] - fn can_rotate_unique() { - let mut keys = HashSet::new(); - for _ in 0..100 { - keys.insert(next_http_rpc_endpoint()); + use alloy_primitives::address; + use foundry_config::Chain; + + #[tokio::test] + #[ignore = "run manually"] + async fn test_etherscan_keys() { + let address = address!("dAC17F958D2ee523a2206206994597C13D831ec7"); + let mut first_abi = None; + let mut failed = Vec::new(); + for (i, &key) in ETHERSCAN_MAINNET_KEYS.iter().enumerate() { + eprintln!("trying key {i} ({key})"); + + let client = foundry_block_explorers::Client::builder() + .chain(Chain::mainnet()) + .unwrap() + .with_api_key(key) + .build() + .unwrap(); + + let mut fail = |e: &str| { + eprintln!("key {i} ({key}) failed: {e}"); + failed.push(key); + }; + + let abi = match client.contract_abi(address).await { + Ok(abi) => abi, + Err(e) => { + fail(&e.to_string()); + continue; + } + }; + + if let Some(first_abi) = &first_abi { + if abi != *first_abi { + fail("abi mismatch"); + } + } else { + first_abi = Some(abi); + } + } + if !failed.is_empty() { + panic!("failed keys: {failed:#?}"); } - assert_eq!(keys.len(), num_keys()); } } diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index 8fffd84d6..e08cf3a5a 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -459,7 +459,7 @@ impl VerifyBytecodeArgs { transaction.input = Bytes::from(local_bytecode_vec); } - configure_tx_env(&mut env, &transaction); + configure_tx_env(&mut env, &transaction.inner); let fork_address = crate::utils::deploy_contract( &mut executor, diff --git a/crates/verify/src/utils.rs b/crates/verify/src/utils.rs index 1b7c7fada..aaf8a0e01 100644 --- a/crates/verify/src/utils.rs +++ b/crates/verify/src/utils.rs @@ -2,7 +2,7 @@ use crate::{bytecode::VerifyBytecodeArgs, types::VerificationType}; use alloy_dyn_abi::DynSolValue; use alloy_primitives::{Address, Bytes, U256}; use alloy_provider::Provider; -use alloy_rpc_types::{Block, BlockId, Transaction}; +use alloy_rpc_types::{AnyNetworkBlock, BlockId, Transaction}; use clap::ValueEnum; use eyre::{OptionExt, Result}; use foundry_block_explorers::{ @@ -346,7 +346,7 @@ pub async fn get_tracing_executor( Ok((env, executor)) } -pub fn configure_env_block(env: &mut Env, block: &Block) { +pub fn configure_env_block(env: &mut Env, block: &AnyNetworkBlock) { env.block.timestamp = U256::from(block.header.timestamp); env.block.coinbase = block.header.miner; env.block.difficulty = block.header.difficulty; From 872e2f3fa622480e863576db06fa6d67a6ba87ce Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Fri, 6 Sep 2024 20:36:28 +0530 Subject: [PATCH 146/184] fix(`anvil`): reset from fork to another fork (#8768) fix(anvil): reset from fork to another fork --- crates/anvil/src/config.rs | 1 - crates/anvil/src/eth/backend/mem/mod.rs | 64 +++++++++++++++---------- crates/anvil/tests/it/fork.rs | 34 ++++++++++++- 3 files changed, 72 insertions(+), 27 deletions(-) diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 3145ee1b2..cafdf1695 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -1075,7 +1075,6 @@ impl NodeConfig { let provider = Arc::new( ProviderBuilder::new(ð_rpc_url) .timeout(self.fork_request_timeout) - // .timeout_retry(self.fork_request_retries) .initial_backoff(self.fork_retry_backoff.as_millis() as u64) .compute_units_per_second(self.compute_units_per_second) .max_retry(self.fork_request_retries) diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 86e8ad039..dd10c672f 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -30,7 +30,7 @@ use crate::{ storage::{BlockchainStorage, InMemoryBlockStates, MinedBlockOutcome}, }, revm::{db::DatabaseRef, primitives::AccountInfo}, - NodeConfig, PrecompileFactory, + ForkChoice, NodeConfig, PrecompileFactory, }; use alloy_consensus::{Account, Header, Receipt, ReceiptWithBloom}; use alloy_eips::eip4844::MAX_BLOBS_PER_BLOCK; @@ -427,37 +427,51 @@ impl Backend { .ok_or(BlockchainError::BlockNotFound)?; // update all settings related to the forked block { - let mut env = self.env.write(); - env.cfg.chain_id = fork.chain_id(); - - env.block = BlockEnv { - number: U256::from(fork_block_number), - timestamp: U256::from(fork_block.header.timestamp), - gas_limit: U256::from(fork_block.header.gas_limit), - difficulty: fork_block.header.difficulty, - prevrandao: Some(fork_block.header.mix_hash.unwrap_or_default()), - // Keep previous `coinbase` and `basefee` value - coinbase: env.block.coinbase, - basefee: env.block.basefee, - ..env.block.clone() - }; + if let Some(fork_url) = forking.json_rpc_url { + // Set the fork block number + let mut node_config = self.node_config.write().await; + node_config.fork_choice = Some(ForkChoice::Block(fork_block_number)); - self.time.reset(env.block.timestamp.to::()); + let mut env = self.env.read().clone(); + let (forked_db, client_fork_config) = + node_config.setup_fork_db_config(fork_url, &mut env, &self.fees).await; - // this is the base fee of the current block, but we need the base fee of - // the next block - let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas( - fork_block.header.gas_used, - fork_block.header.gas_limit, - fork_block.header.base_fee_per_gas.unwrap_or_default(), - ); + *self.db.write().await = Box::new(forked_db); + let fork = ClientFork::new(client_fork_config, Arc::clone(&self.db)); + *self.fork.write() = Some(fork); + *self.env.write() = env; + } else { + let mut env = self.env.write(); + env.cfg.chain_id = fork.chain_id(); + env.block = BlockEnv { + number: U256::from(fork_block_number), + timestamp: U256::from(fork_block.header.timestamp), + gas_limit: U256::from(fork_block.header.gas_limit), + difficulty: fork_block.header.difficulty, + prevrandao: Some(fork_block.header.mix_hash.unwrap_or_default()), + // Keep previous `coinbase` and `basefee` value + coinbase: env.block.coinbase, + basefee: env.block.basefee, + ..env.block.clone() + }; - self.fees.set_base_fee(next_block_base_fee); + // this is the base fee of the current block, but we need the base fee of + // the next block + let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas( + fork_block.header.gas_used, + fork_block.header.gas_limit, + fork_block.header.base_fee_per_gas.unwrap_or_default(), + ); + + self.fees.set_base_fee(next_block_base_fee); + } + + // reset the time to the timestamp of the forked block + self.time.reset(fork_block.header.timestamp); // also reset the total difficulty self.blockchain.storage.write().total_difficulty = fork.total_difficulty(); } - // reset storage *self.blockchain.storage.write() = BlockchainStorage::forked( fork.block_number(), diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 672d3b72f..d8465ec5c 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -4,6 +4,7 @@ use crate::{ abi::{Greeter, ERC721}, utils::{http_provider, http_provider_with_signer}, }; +use alloy_chains::NamedChain; use alloy_network::{EthereumWallet, ReceiptResponse, TransactionBuilder}; use alloy_primitives::{address, b256, bytes, Address, Bytes, TxHash, TxKind, U256}; use alloy_provider::Provider; @@ -17,7 +18,7 @@ use alloy_signer_local::PrivateKeySigner; use anvil::{eth::EthApi, spawn, NodeConfig, NodeHandle}; use foundry_common::provider::get_http_provider; use foundry_config::Config; -use foundry_test_utils::rpc::{self, next_http_rpc_endpoint}; +use foundry_test_utils::rpc::{self, next_http_rpc_endpoint, next_rpc_endpoint}; use futures::StreamExt; use std::{sync::Arc, thread::sleep, time::Duration}; @@ -476,6 +477,37 @@ async fn can_reset_properly() { assert!(fork_tx_provider.get_transaction_by_hash(tx.transaction_hash).await.unwrap().is_none()) } +// Ref: +#[tokio::test(flavor = "multi_thread")] +async fn can_reset_fork_to_new_fork() { + let eth_rpc_url = next_rpc_endpoint(NamedChain::Mainnet); + let (api, handle) = spawn(NodeConfig::test().with_eth_rpc_url(Some(eth_rpc_url))).await; + let provider = handle.http_provider(); + + let op = address!("C0d3c0d3c0D3c0D3C0d3C0D3C0D3c0d3c0d30007"); // L2CrossDomainMessenger - Dead on mainnet. + + let tx = TransactionRequest::default().with_to(op).with_input("0x54fd4d50"); + + let tx = WithOtherFields::new(tx); + + let mainnet_call_output = provider.call(&tx).await.unwrap(); + + assert_eq!(mainnet_call_output, Bytes::new()); // 0x + + let optimism = next_rpc_endpoint(NamedChain::Optimism); + + api.anvil_reset(Some(Forking { + json_rpc_url: Some(optimism.to_string()), + block_number: Some(124659890), + })) + .await + .unwrap(); + + let code = provider.get_code_at(op).await.unwrap(); + + assert_ne!(code, Bytes::new()); +} + #[tokio::test(flavor = "multi_thread")] async fn test_fork_timestamp() { let start = std::time::Instant::now(); From 8a51b89f838e0a17afb6443d8cd008130d0cd47a Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Sat, 7 Sep 2024 07:02:57 +0300 Subject: [PATCH 147/184] fix(cheatcodes): do not account already matched emit events when fill or check (#8824) * fix(6643): do not account already matched events when fill or check * Move test as repro test --- crates/cheatcodes/src/test/expect.rs | 49 ++++++++++++------- crates/forge/tests/it/repros.rs | 3 ++ testdata/default/repros/Issue6643.t.sol | 65 +++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 18 deletions(-) create mode 100644 testdata/default/repros/Issue6643.t.sol diff --git a/crates/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs index 838990415..df9c480e3 100644 --- a/crates/cheatcodes/src/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -464,14 +464,16 @@ fn expect_emit( address: Option
, anonymous: bool, ) -> Result { - state.expected_emits.push_back(ExpectedEmit { - depth, - checks, - address, - found: false, - log: None, - anonymous, - }); + let expected_emit = ExpectedEmit { depth, checks, address, found: false, log: None, anonymous }; + if let Some(found_emit_pos) = state.expected_emits.iter().position(|emit| emit.found) { + // The order of emits already found (back of queue) should not be modified, hence push any + // new emit before first found emit. + state.expected_emits.insert(found_emit_pos, expected_emit); + } else { + // If no expected emits then push new one at the back of queue. + state.expected_emits.push_back(expected_emit); + } + Ok(Default::default()) } @@ -494,15 +496,25 @@ pub(crate) fn handle_expect_emit( return } - // If there's anything to fill, we need to pop back. - // Otherwise, if there are any events that are unmatched, we try to match to match them - // in the order declared, so we start popping from the front (like a queue). - let mut event_to_fill_or_check = - if state.expected_emits.iter().any(|expected| expected.log.is_none()) { - state.expected_emits.pop_back() - } else { - state.expected_emits.pop_front() - } + let should_fill_logs = state.expected_emits.iter().any(|expected| expected.log.is_none()); + let index_to_fill_or_check = if should_fill_logs { + // If there's anything to fill, we start with the last event to match in the queue + // (without taking into account events already matched). + state + .expected_emits + .iter() + .position(|emit| emit.found) + .unwrap_or(state.expected_emits.len()) + .saturating_sub(1) + } else { + // Otherwise, if all expected logs are filled, we start to check any unmatched event + // in the declared order, so we start from the front (like a queue). + 0 + }; + + let mut event_to_fill_or_check = state + .expected_emits + .remove(index_to_fill_or_check) .expect("we should have an emit to fill or check"); let Some(expected) = &event_to_fill_or_check.log else { @@ -510,7 +522,8 @@ pub(crate) fn handle_expect_emit( // filled. if event_to_fill_or_check.anonymous || log.topics().first().is_some() { event_to_fill_or_check.log = Some(log.data.clone()); - state.expected_emits.push_back(event_to_fill_or_check); + // If we only filled the expected log then we put it back at the same position. + state.expected_emits.insert(index_to_fill_or_check, event_to_fill_or_check); } else { interpreter.instruction_result = InstructionResult::Revert; interpreter.next_action = InterpreterAction::Return { diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index 3cce4bed2..73310f011 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -378,3 +378,6 @@ test_repro!(8383, false, None, |res| { // https://github.com/foundry-rs/foundry/issues/1543 test_repro!(1543); + +// https://github.com/foundry-rs/foundry/issues/6643 +test_repro!(6643); diff --git a/testdata/default/repros/Issue6643.t.sol b/testdata/default/repros/Issue6643.t.sol new file mode 100644 index 000000000..36b684c13 --- /dev/null +++ b/testdata/default/repros/Issue6643.t.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract Counter { + event TestEvent(uint256 n); + event AnotherTestEvent(uint256 n); + + constructor() { + emit TestEvent(1); + } + + function f() external { + emit TestEvent(2); + } + + function g() external { + emit AnotherTestEvent(1); + this.f(); + emit AnotherTestEvent(2); + } +} + +// https://github.com/foundry-rs/foundry/issues/6643 +contract Issue6643Test is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + Counter public counter; + + event TestEvent(uint256 n); + event AnotherTestEvent(uint256 n); + + function setUp() public { + counter = new Counter(); + } + + function test_Bug1() public { + // part1 + vm.expectEmit(); + emit TestEvent(1); + new Counter(); + // part2 + vm.expectEmit(); + emit TestEvent(2); + counter.f(); + // part3 + vm.expectEmit(); + emit AnotherTestEvent(1); + vm.expectEmit(); + emit TestEvent(2); + vm.expectEmit(); + emit AnotherTestEvent(2); + counter.g(); + } + + function test_Bug2() public { + vm.expectEmit(); + emit TestEvent(1); + new Counter(); + vm.expectEmit(); + emit TestEvent(1); + new Counter(); + } +} From 27d008f2e5c4e383bfad37e7970aa5895ea22bdf Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sat, 7 Sep 2024 13:23:58 +0200 Subject: [PATCH 148/184] ci: update docker build (#8829) --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index aba0b3986..cbdf08b25 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,7 +19,7 @@ COPY . . RUN git update-index --force-write-index RUN --mount=type=cache,target=/root/.cargo/registry --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/opt/foundry/target \ - source $HOME/.profile && cargo build --release --features cast/aws-kms,forge/aws-kms \ + source $HOME/.profile && cargo build --release --features foundry-cast/aws-kms,forge/aws-kms \ && mkdir out \ && mv target/release/forge out/forge \ && mv target/release/cast out/cast \ From 4d377409faf5e681f88b5b3a2de640c9f9631ae9 Mon Sep 17 00:00:00 2001 From: Sabnock <24715302+Sabnock01@users.noreply.github.com> Date: Sat, 7 Sep 2024 08:33:15 -0500 Subject: [PATCH 149/184] feat: add `codehash` and `storage-root` to `cast` (#8828) * feat: add codehash and storage-root to cast * fix: use eth_getProof * fix: nits --- crates/cast/bin/args.rs | 42 ++++++++++++++++++++++++++ crates/cast/bin/main.rs | 12 ++++++++ crates/cast/src/lib.rs | 66 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+) diff --git a/crates/cast/bin/args.rs b/crates/cast/bin/args.rs index 727458505..fdb6a1133 100644 --- a/crates/cast/bin/args.rs +++ b/crates/cast/bin/args.rs @@ -831,6 +831,48 @@ pub enum CastSubcommand { rpc: RpcOpts, }, + /// Get the codehash for an account. + #[command()] + Codehash { + /// The block height to query at. + /// + /// Can also be the tags earliest, finalized, safe, latest, or pending. + #[arg(long, short = 'B')] + block: Option, + + /// The address to get the codehash for. + #[arg(value_parser = NameOrAddress::from_str)] + who: NameOrAddress, + + /// The storage slot numbers (hex or decimal). + #[arg(value_parser = parse_slot)] + slots: Vec, + + #[command(flatten)] + rpc: RpcOpts, + }, + + /// Get the storage root for an account. + #[command(visible_alias = "sr")] + StorageRoot { + /// The block height to query at. + /// + /// Can also be the tags earliest, finalized, safe, latest, or pending. + #[arg(long, short = 'B')] + block: Option, + + /// The address to get the storage root for. + #[arg(value_parser = NameOrAddress::from_str)] + who: NameOrAddress, + + /// The storage slot numbers (hex or decimal). + #[arg(value_parser = parse_slot)] + slots: Vec, + + #[command(flatten)] + rpc: RpcOpts, + }, + /// Get the source code of a contract from Etherscan. #[command(visible_aliases = &["et", "src"])] EtherscanSource { diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 28009dbdc..27a052fe6 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -362,6 +362,18 @@ async fn main_args(args: CastArgs) -> Result<()> { let who = who.resolve(&provider).await?; println!("{}", Cast::new(provider).nonce(who, block).await?); } + CastSubcommand::Codehash { block, who, slots, rpc } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + let who = who.resolve(&provider).await?; + println!("{}", Cast::new(provider).codehash(who, slots, block).await?); + } + CastSubcommand::StorageRoot { block, who, slots, rpc } => { + let config = Config::from(&rpc); + let provider = utils::get_provider(&config)?; + let who = who.resolve(&provider).await?; + println!("{}", Cast::new(provider).storage_root(who, slots, block).await?); + } CastSubcommand::Proof { address, slots, rpc, block } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 03355213d..73ab5b28b 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -494,6 +494,72 @@ where Ok(self.provider.get_transaction_count(who).block_id(block.unwrap_or_default()).await?) } + /// #Example + /// + /// ``` + /// use alloy_primitives::{Address, FixedBytes}; + /// use alloy_provider::{network::AnyNetwork, ProviderBuilder, RootProvider}; + /// use cast::Cast; + /// use std::str::FromStr; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = + /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("http://localhost:8545").await?; + /// let cast = Cast::new(provider); + /// let addr = Address::from_str("0x7eD52863829AB99354F3a0503A622e82AcD5F7d3")?; + /// let slots = vec![FixedBytes::from_str("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")?]; + /// let codehash = cast.codehash(addr, slots, None).await?; + /// println!("{}", codehash); + /// # Ok(()) + /// # } + pub async fn codehash( + &self, + who: Address, + slots: Vec, + block: Option, + ) -> Result { + Ok(self + .provider + .get_proof(who, slots) + .block_id(block.unwrap_or_default()) + .await? + .code_hash + .to_string()) + } + + /// #Example + /// + /// ``` + /// use alloy_primitives::{Address, FixedBytes}; + /// use alloy_provider::{network::AnyNetwork, ProviderBuilder, RootProvider}; + /// use cast::Cast; + /// use std::str::FromStr; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = + /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("http://localhost:8545").await?; + /// let cast = Cast::new(provider); + /// let addr = Address::from_str("0x7eD52863829AB99354F3a0503A622e82AcD5F7d3")?; + /// let slots = vec![FixedBytes::from_str("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")?]; + /// let storage_root = cast.storage_root(addr, slots, None).await?; + /// println!("{}", storage_root); + /// # Ok(()) + /// # } + pub async fn storage_root( + &self, + who: Address, + slots: Vec, + block: Option, + ) -> Result { + Ok(self + .provider + .get_proof(who, slots) + .block_id(block.unwrap_or_default()) + .await? + .storage_hash + .to_string()) + } + /// # Example /// /// ``` From 96105b4d240681c336e063eac0e250cc51a84414 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 8 Sep 2024 03:11:09 +0200 Subject: [PATCH 150/184] chore(deps): weekly `cargo update` (#8832) --- Cargo.lock | 98 +++++++++++++++++++++++++++--------------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6793346b5..6172e6ea1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,9 +74,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-chains" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb07629a5d0645d29f68d2fb6f4d0cf15c89ec0965be915f303967180929743f" +checksum = "2b4f201b0ac8f81315fbdc55269965a8ddadbc04ab47fa65a1a468f9a40f7a5f" dependencies = [ "num_enum", "serde", @@ -118,9 +118,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1c7d5315d44b5e00ce9c6b72dc88aa8f3e49d82a742be85d6a755ef0aa28c5" +checksum = "b03f58cfae4d41b624effe1f11624ee40499449174b20a6d6505fd72ef0d547d" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -194,9 +194,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8129590e6f5aa3aeb33138f0faaa4cb763e4f1ca072ab98ce880a1998dca84d3" +checksum = "a28ecae8b5315daecd0075084eb47f4374b3037777346ca52fc8d9c327693f02" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -249,9 +249,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d1d1bdfba287209ee749a985230685c2ff8bdabe5fed6f699f72f22bb72c95" +checksum = "ccb865df835f851b367ae439d6c82b117ded971628c8888b24fed411a290e38a" dependencies = [ "alloy-rlp", "arbitrary", @@ -574,9 +574,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03f6a68cb762b6feb50cff1a4f81831b8725aa10f5a1afc26c209b7df442fffe" +checksum = "e2dc5201ca0018afb7a3e0cd8bd15f7ca6aca924333b5f3bb87463b41d0c4ef2" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -588,9 +588,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e3d38952d1f54a541c00b1b28582cd112ac02e6302437bd3dead32367a87393" +checksum = "155f63dc6945885aa4532601800201fddfaa3b20901fda8e8c2570327242fe0e" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -607,9 +607,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2821c0ce9b217b17c9a52c8dc0dddadb391d0eb7593707da9f12df786510594b" +checksum = "847700aa9cb59d3c7b290b2d05976cd8d76b64d73bb63116a9533132d995586b" dependencies = [ "alloy-json-abi", "const-hex", @@ -624,9 +624,9 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f040419382164c509aa60f281e45c6fa295cf88ccffe686bd71d65780bc8d053" +checksum = "3a6b5d462d4520bd9ed70d8364c6280aeff13baa46ea26be1ddd33538dbbe6ac" dependencies = [ "serde", "winnow", @@ -634,9 +634,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53be2dde91d22bc1e398e26bd42bdac441705f205a4cbaf4f3abb9273867ed39" +checksum = "83665e5607725a7a1aab3cb0dea708f4a05e70776954ec7f0a9461439175c957" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -935,9 +935,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8" [[package]] name = "arbitrary" @@ -1886,9 +1886,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.16" +version = "1.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b" +checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" dependencies = [ "shlex", ] @@ -2294,9 +2294,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -5144,9 +5144,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" [[package]] name = "is-terminal" @@ -6123,9 +6123,9 @@ dependencies = [ [[package]] name = "parking" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" @@ -6231,9 +6231,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.11" +version = "2.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" +checksum = "9c73c26c01b8c87956cea613c907c9d6ecffd8d18a2a5908e5de0adfaa185cea" dependencies = [ "memchr", "thiserror", @@ -6242,9 +6242,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.11" +version = "2.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" +checksum = "664d22978e2815783adbdd2c588b455b1bd625299ce36b2a99881ac9627e6d8d" dependencies = [ "pest", "pest_generator", @@ -6252,9 +6252,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.11" +version = "2.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" +checksum = "a2d5487022d5d33f4c30d91c22afa240ce2a644e87fe08caad974d4eab6badbe" dependencies = [ "pest", "pest_meta", @@ -6265,9 +6265,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.7.11" +version = "2.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" +checksum = "0091754bbd0ea592c4deb3a122ce8ecbb0753b738aa82bc055fcc2eccc8d8174" dependencies = [ "once_cell", "pest", @@ -6592,9 +6592,9 @@ dependencies = [ [[package]] name = "proc-macro-error2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74cdd32837fa2e86ec09c8266e5aad92400ac934c6dbca83d54673b298db3e45" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" dependencies = [ "proc-macro-error-attr2", "proc-macro2", @@ -7521,11 +7521,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -7608,9 +7608,9 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.29.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0cc0f1cf93f4969faf3ea1c7d8a9faed25918d96affa959720823dfe86d4f3" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ "rand", "secp256k1-sys", @@ -7696,18 +7696,18 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -8218,9 +8218,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03133179a4b51eabc6f1aea6951626a766b8aa3af68b12d150f88095914cb447" +checksum = "f1e1355d44af21638c8e05d45097db6cb5ec2aa3e970c51cb2901605cf3344fa" dependencies = [ "paste", "proc-macro2", From 0079a1146b79a4aeda58b0258215bedb1f92700b Mon Sep 17 00:00:00 2001 From: soham Date: Mon, 9 Sep 2024 20:34:29 +0530 Subject: [PATCH 151/184] Support for Flamegraph (#8640) * first pass bump revm-inspectors * fix: bug while processing call node * handle contract creation in flamegraph * store in tmp file and open file * enable decode_internal * remove pub from internal method * use temp_dir * ref: combine fst code into single file * remove redundant option * fix: handle non-empty step_exits * some docs * revert revm-inspectors version change * switch to flamegraph and flamechart boolean flags * Update crates/evm/traces/src/folded_stack_trace.rs Co-authored-by: Arsenii Kulikov * Update crates/evm/traces/src/folded_stack_trace.rs Co-authored-by: Arsenii Kulikov * save to cache dir and gracefully handle opener outcome * disable default features in inferno * fixes * license --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Arsenii Kulikov --- Cargo.lock | 44 ++- crates/evm/traces/src/folded_stack_trace.rs | 301 ++++++++++++++++++++ crates/evm/traces/src/lib.rs | 2 + crates/forge/Cargo.toml | 1 + crates/forge/bin/cmd/test/mod.rs | 101 +++++-- crates/forge/src/result.rs | 12 + deny.toml | 1 + 7 files changed, 443 insertions(+), 19 deletions(-) create mode 100644 crates/evm/traces/src/folded_stack_trace.rs diff --git a/Cargo.lock b/Cargo.lock index 6172e6ea1..011a7af0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2837,7 +2837,7 @@ dependencies = [ "console_error_panic_hook", "pest", "pest_derive", - "quick-xml", + "quick-xml 0.18.1", "wasm-bindgen", ] @@ -3264,6 +3264,7 @@ dependencies = [ "humantime-serde", "hyper 1.4.1", "indicatif", + "inferno", "itertools 0.13.0", "mockall", "opener", @@ -5073,6 +5074,23 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "inferno" +version = "0.11.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "232929e1d75fe899576a3d5c7416ad0d88dbfbb3c3d6aa00873a7408a50ddb88" +dependencies = [ + "ahash", + "is-terminal", + "itoa", + "log", + "num-format", + "once_cell", + "quick-xml 0.26.0", + "rgb", + "str_stack", +] + [[package]] name = "inlinable_string" version = "0.1.15" @@ -6771,6 +6789,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "quick-xml" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" +dependencies = [ + "memchr", +] + [[package]] name = "quinn" version = "0.11.5" @@ -7144,6 +7171,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "rgb" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f86ae463694029097b846d8f99fd5536740602ae00022c0c50c5600720b2f71" +dependencies = [ + "bytemuck", +] + [[package]] name = "ring" version = "0.17.8" @@ -8088,6 +8124,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "str_stack" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" + [[package]] name = "string_cache" version = "0.8.7" diff --git a/crates/evm/traces/src/folded_stack_trace.rs b/crates/evm/traces/src/folded_stack_trace.rs new file mode 100644 index 000000000..de76f8e80 --- /dev/null +++ b/crates/evm/traces/src/folded_stack_trace.rs @@ -0,0 +1,301 @@ +use alloy_primitives::hex::ToHexExt; +use revm_inspectors::tracing::{ + types::{CallTraceNode, CallTraceStep, DecodedTraceStep, TraceMemberOrder}, + CallTraceArena, +}; + +/// Builds a folded stack trace from a call trace arena. +pub fn build(arena: &CallTraceArena) -> Vec { + let mut fst = EvmFoldedStackTraceBuilder::default(); + fst.process_call_node(arena.nodes(), 0); + fst.build() +} + +/// Wrapper for building a folded stack trace using EVM call trace node. +#[derive(Default)] +pub struct EvmFoldedStackTraceBuilder { + /// Raw folded stack trace builder. + fst: FoldedStackTraceBuilder, +} + +impl EvmFoldedStackTraceBuilder { + /// Returns the folded stack trace. + pub fn build(self) -> Vec { + self.fst.build() + } + + /// Creates an entry for a EVM CALL in the folded stack trace. This method recursively processes + /// all the children nodes of the call node and at the end it exits. + pub fn process_call_node(&mut self, nodes: &[CallTraceNode], idx: usize) { + let node = &nodes[idx]; + + let func_name = if node.trace.kind.is_any_create() { + let default_contract_name = "Contract".to_string(); + let contract_name = node.trace.decoded.label.as_ref().unwrap_or(&default_contract_name); + format!("new {contract_name}") + } else { + let selector = node + .selector() + .map(|selector| selector.encode_hex_with_prefix()) + .unwrap_or("fallback".to_string()); + let signature = + node.trace.decoded.call_data.as_ref().map(|dc| &dc.signature).unwrap_or(&selector); + + if let Some(label) = &node.trace.decoded.label { + format!("{label}.{signature}") + } else { + signature.clone() + } + }; + + self.fst.enter(func_name, node.trace.gas_used as i64); + + // Track internal function step exits to do in this call context. + let mut step_exits = vec![]; + + // Process children nodes. + for order in &node.ordering { + match order { + TraceMemberOrder::Call(child_idx) => { + let child_node_idx = node.children[*child_idx]; + self.process_call_node(nodes, child_node_idx); + } + TraceMemberOrder::Step(step_idx) => { + self.exit_previous_steps(&mut step_exits, *step_idx); + self.process_step(&node.trace.steps, *step_idx, &mut step_exits) + } + TraceMemberOrder::Log(_) => {} + } + } + + // Exit pending internal function calls if any. + for _ in 0..step_exits.len() { + self.fst.exit(); + } + + // Exit from this call context in the folded stack trace. + self.fst.exit(); + } + + /// Creates an entry for an internal function call in the folded stack trace. This method only + /// enters the function in the folded stack trace, we cannot exit since we need to exit at a + /// future step. Hence, we keep track of the step end index in the `step_exits`. + fn process_step( + &mut self, + steps: &[CallTraceStep], + step_idx: usize, + step_exits: &mut Vec, + ) { + let step = &steps[step_idx]; + if let Some(decoded_step) = &step.decoded { + match decoded_step { + DecodedTraceStep::InternalCall(decoded_internal_call, step_end_idx) => { + let gas_used = steps[*step_end_idx].gas_used.saturating_sub(step.gas_used); + self.fst.enter(decoded_internal_call.func_name.clone(), gas_used as i64); + step_exits.push(*step_end_idx); + } + DecodedTraceStep::Line(_) => {} + } + } + } + + /// Exits all the previous internal calls that should end before starting step_idx. + fn exit_previous_steps(&mut self, step_exits: &mut Vec, step_idx: usize) { + let initial_length = step_exits.len(); + step_exits.retain(|&number| number > step_idx); + + let num_exits = initial_length - step_exits.len(); + for _ in 0..num_exits { + self.fst.exit(); + } + } +} + +/// Helps to translate a function enter-exit flow into a folded stack trace. +/// +/// Example: +/// fn top() { child_a(); child_b() } // consumes 500 gas +/// fn child_a() {} // consumes 100 gas +/// fn child_b() {} // consumes 200 gas +/// +/// For execution of the `top` function looks like: +/// 1. enter `top` +/// 2. enter `child_a` +/// 3. exit `child_a` +/// 4. enter `child_b` +/// 5. exit `child_b` +/// 6. exit `top` +/// +/// The translated folded stack trace lines look like: +/// 1. top +/// 2. top;child_a +/// 3. top;child_b +/// +/// Including the gas consumed by the function by itself. +/// 1. top 200 // 500 - 100 - 200 +/// 2. top;child_a 100 +/// 3. top;child_b 200 +#[derive(Debug, Default)] +pub struct FoldedStackTraceBuilder { + /// Trace entries. + traces: Vec, + /// Number of exits to be done before entering a new function. + exits: usize, +} + +#[derive(Debug, Default)] +struct TraceEntry { + /// Names of all functions in the call stack of this trace. + names: Vec, + /// Gas consumed by this function, allowed to be negative due to refunds. + gas: i64, +} + +impl FoldedStackTraceBuilder { + /// Enter execution of a function call that consumes `gas`. + pub fn enter(&mut self, label: String, gas: i64) { + let mut names = self.traces.last().map(|entry| entry.names.clone()).unwrap_or_default(); + + while self.exits > 0 { + names.pop(); + self.exits -= 1; + } + + names.push(label); + self.traces.push(TraceEntry { names, gas }); + } + + /// Exit execution of a function call. + pub fn exit(&mut self) { + self.exits += 1; + } + + /// Returns folded stack trace. + pub fn build(mut self) -> Vec { + self.subtract_children(); + self.build_without_subtraction() + } + + /// Internal method to build the folded stack trace without subtracting gas consumed by + /// the children function calls. + fn build_without_subtraction(&mut self) -> Vec { + let mut lines = Vec::new(); + for TraceEntry { names, gas } in self.traces.iter() { + lines.push(format!("{} {}", names.join(";"), gas)); + } + lines + } + + /// Subtracts gas consumed by the children function calls from the parent function calls. + fn subtract_children(&mut self) { + // Iterate over each trace to find the children and subtract their values from the parents. + for i in 0..self.traces.len() { + let (left, right) = self.traces.split_at_mut(i); + let TraceEntry { names, gas } = &right[0]; + if names.len() > 1 { + let parent_trace_to_match = &names[..names.len() - 1]; + for parent in left.iter_mut().rev() { + if parent.names == parent_trace_to_match { + parent.gas -= gas; + break; + } + } + } + } + } +} + +mod tests { + #[test] + fn test_fst_1() { + let mut trace = super::FoldedStackTraceBuilder::default(); + trace.enter("top".to_string(), 500); + trace.enter("child_a".to_string(), 100); + trace.exit(); + trace.enter("child_b".to_string(), 200); + + assert_eq!( + trace.build_without_subtraction(), + vec![ + "top 500", // + "top;child_a 100", + "top;child_b 200", + ] + ); + assert_eq!( + trace.build(), + vec![ + "top 200", // 500 - 100 - 200 + "top;child_a 100", + "top;child_b 200", + ] + ); + } + + #[test] + fn test_fst_2() { + let mut trace = super::FoldedStackTraceBuilder::default(); + trace.enter("top".to_string(), 500); + trace.enter("child_a".to_string(), 300); + trace.enter("child_b".to_string(), 100); + trace.exit(); + trace.exit(); + trace.enter("child_c".to_string(), 100); + + assert_eq!( + trace.build_without_subtraction(), + vec![ + "top 500", // + "top;child_a 300", + "top;child_a;child_b 100", + "top;child_c 100", + ] + ); + + assert_eq!( + trace.build(), + vec![ + "top 100", // 500 - 300 - 100 + "top;child_a 200", // 300 - 100 + "top;child_a;child_b 100", + "top;child_c 100", + ] + ); + } + + #[test] + fn test_fst_3() { + let mut trace = super::FoldedStackTraceBuilder::default(); + trace.enter("top".to_string(), 1700); + trace.enter("child_a".to_string(), 500); + trace.exit(); + trace.enter("child_b".to_string(), 500); + trace.enter("child_c".to_string(), 500); + trace.exit(); + trace.exit(); + trace.exit(); + trace.enter("top2".to_string(), 1700); + + assert_eq!( + trace.build_without_subtraction(), + vec![ + "top 1700", // + "top;child_a 500", + "top;child_b 500", + "top;child_b;child_c 500", + "top2 1700", + ] + ); + + assert_eq!( + trace.build(), + vec![ + "top 700", // + "top;child_a 500", + "top;child_b 0", + "top;child_b;child_c 500", + "top2 1700", + ] + ); + } +} diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index 4a80b164a..fb1665a5a 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -42,6 +42,8 @@ pub use decoder::{CallTraceDecoder, CallTraceDecoderBuilder}; pub mod debug; pub use debug::DebugTraceIdentifier; +pub mod folded_stack_trace; + pub type Traces = Vec<(TraceKind, SparsedTraceArena)>; /// Trace arena keeping track of ignored trace items. diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 4f9110ff6..77034d619 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -78,6 +78,7 @@ dialoguer = { version = "0.11", default-features = false } dunce.workspace = true futures.workspace = true indicatif = "0.17" +inferno = { version = "0.11.19", default-features = false } itertools.workspace = true parking_lot.workspace = true regex = { version = "1", default-features = false } diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index b0792a8cf..1cc647b0c 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -1,7 +1,7 @@ use super::{install, test::filter::ProjectPathsAwareFilter, watch::WatchArgs}; use alloy_primitives::U256; use clap::{Parser, ValueHint}; -use eyre::Result; +use eyre::{Context, OptionExt, Result}; use forge::{ decode::decode_console_logs, gas_report::GasReport, @@ -9,7 +9,7 @@ use forge::{ result::{SuiteResult, TestOutcome, TestStatus}, traces::{ debug::{ContractSources, DebugTraceIdentifier}, - decode_trace_arena, + decode_trace_arena, folded_stack_trace, identifier::SignaturesIdentifier, render_trace_arena, CallTraceDecoderBuilder, InternalTraceMode, TraceKind, }, @@ -81,6 +81,14 @@ pub struct TestArgs { #[arg(long, value_name = "TEST_FUNCTION")] debug: Option, + /// Generate a flamegraph for a single test. Implies `--decode-internal`. + #[arg(long, conflicts_with = "flamechart")] + flamegraph: bool, + + /// Generate a flamechart for a single test. Implies `--decode-internal`. + #[arg(long, conflicts_with = "flamegraph")] + flamechart: bool, + /// Whether to identify internal functions in traces. /// /// If no argument is passed to this flag, it will trace internal functions scope and decode @@ -254,7 +262,7 @@ impl TestArgs { /// configured filter will be executed /// /// Returns the test results for all matching tests. - pub async fn execute_tests(self) -> Result { + pub async fn execute_tests(mut self) -> Result { // Merge all configs. let (mut config, mut evm_opts) = self.load_config_and_evm_opts_emit_warnings()?; @@ -308,14 +316,22 @@ impl TestArgs { .profiles(profiles) .build(&output, project_root)?; + let should_debug = self.debug.is_some(); + let should_draw = self.flamegraph || self.flamechart; + // Determine print verbosity and executor verbosity. let verbosity = evm_opts.verbosity; - if self.gas_report && evm_opts.verbosity < 3 { + if (self.gas_report && evm_opts.verbosity < 3) || self.flamegraph || self.flamechart { evm_opts.verbosity = 3; } let env = evm_opts.evm_env().await?; + // Enable internal tracing for more informative flamegraph. + if should_draw { + self.decode_internal = Some(None); + } + // Choose the internal function tracing mode, if --decode-internal is provided. let decode_internal = if let Some(maybe_fn) = self.decode_internal.as_ref() { if maybe_fn.is_some() { @@ -330,7 +346,6 @@ impl TestArgs { }; // Prepare the test builder. - let should_debug = self.debug.is_some(); let config = Arc::new(config); let runner = MultiContractRunnerBuilder::new(config.clone()) .set_debug(should_debug) @@ -366,18 +381,60 @@ impl TestArgs { )?; let libraries = runner.libraries.clone(); - let outcome = self.run_tests(runner, config, verbosity, &filter, &output).await?; + let mut outcome = self.run_tests(runner, config, verbosity, &filter, &output).await?; + + if should_draw { + let (suite_name, test_name, mut test_result) = + outcome.remove_first().ok_or_eyre("no tests were executed")?; + + let arena = test_result + .traces + .iter_mut() + .find_map( + |(kind, arena)| { + if *kind == TraceKind::Execution { + Some(arena) + } else { + None + } + }, + ) + .unwrap(); + + // Decode traces. + let decoder = outcome.last_run_decoder.as_ref().unwrap(); + decode_trace_arena(arena, decoder).await?; + let mut fst = folded_stack_trace::build(arena); + + let label = if self.flamegraph { "flamegraph" } else { "flamechart" }; + let contract = suite_name.split(':').last().unwrap(); + let test_name = test_name.trim_end_matches("()"); + let file_name = format!("cache/{label}_{contract}_{test_name}.svg"); + let file = std::fs::File::create(&file_name).wrap_err("failed to create file")?; + + let mut options = inferno::flamegraph::Options::default(); + options.title = format!("{label} {contract}::{test_name}"); + options.count_name = "gas".to_string(); + if self.flamechart { + options.flame_chart = true; + fst.reverse(); + } + + // Generate SVG. + inferno::flamegraph::from_lines(&mut options, fst.iter().map(|s| s.as_str()), file) + .wrap_err("failed to write svg")?; + println!("\nSaved to {file_name}"); + + // Open SVG in default program. + if opener::open(&file_name).is_err() { + println!("\nFailed to open {file_name}. Please open it manually."); + } + } if should_debug { // Get first non-empty suite result. We will have only one such entry. - let Some((_, test_result)) = outcome - .results - .iter() - .find(|(_, r)| !r.test_results.is_empty()) - .map(|(_, r)| (r, r.test_results.values().next().unwrap())) - else { - return Err(eyre::eyre!("no tests were executed")); - }; + let (_, _, test_result) = + outcome.remove_first().ok_or_eyre("no tests were executed")?; let sources = ContractSources::from_project_output(&output, project.root(), Some(&libraries))?; @@ -417,13 +474,17 @@ impl TestArgs { trace!(target: "forge::test", "running all tests"); let num_filtered = runner.matching_test_functions(filter).count(); - if (self.debug.is_some() || self.decode_internal.as_ref().map_or(false, |v| v.is_some())) && + if (self.debug.is_some() || + self.decode_internal.as_ref().map_or(false, |v| v.is_some()) || + self.flamegraph || + self.flamechart) && num_filtered != 1 { eyre::bail!( - "{num_filtered} tests matched your criteria, but exactly 1 test must match in order to run the debugger.\n\n\ + "{num_filtered} tests matched your criteria, but exactly 1 test must match in order to {action}.\n\n\ Use --match-contract and --match-path to further limit the search.\n\ - Filter used:\n{filter}" + Filter used:\n{filter}", + action = if self.flamegraph {"generate a flamegraph"} else if self.flamechart {"generate a flamechart"} else {"run the debugger"}, ); } @@ -489,7 +550,11 @@ impl TestArgs { decoder.clear_addresses(); // We identify addresses if we're going to print *any* trace or gas report. - let identify_addresses = verbosity >= 3 || self.gas_report || self.debug.is_some(); + let identify_addresses = verbosity >= 3 || + self.gas_report || + self.debug.is_some() || + self.flamegraph || + self.flamechart; // Print suite header. println!(); diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index e7953575f..aa88bc401 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -184,6 +184,18 @@ impl TestOutcome { // TODO: Avoid process::exit std::process::exit(1); } + + /// Removes first test result, if any. + pub fn remove_first(&mut self) -> Option<(String, String, TestResult)> { + self.results.iter_mut().find_map(|(suite_name, suite)| { + if let Some(test_name) = suite.test_results.keys().next().cloned() { + let result = suite.test_results.remove(&test_name).unwrap(); + Some((suite_name.clone(), test_name, result)) + } else { + None + } + }) + } } /// A set of test results for a single test suite, which is all the tests in a single contract. diff --git a/deny.toml b/deny.toml index 77cfc79a3..e908828cc 100644 --- a/deny.toml +++ b/deny.toml @@ -52,6 +52,7 @@ allow = [ "BSL-1.0", "0BSD", "MPL-2.0", + "CDDL-1.0", ] # Allow 1 or more licenses on a per-crate basis, so that particular licenses From 209776527326e179448ca80e7591f533b1d135fe Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 10 Sep 2024 12:23:37 +0200 Subject: [PATCH 152/184] test: fix flaky test (#8839) --- crates/anvil/tests/it/traces.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/anvil/tests/it/traces.rs b/crates/anvil/tests/it/traces.rs index 5c9d87ca1..aaa2ca298 100644 --- a/crates/anvil/tests/it/traces.rs +++ b/crates/anvil/tests/it/traces.rs @@ -766,15 +766,15 @@ async fn test_trace_filter() { for i in 0..=5 { let tx = TransactionRequest::default().to(to).value(U256::from(i)).from(from); let tx = WithOtherFields::new(tx); - api.send_transaction(tx).await.unwrap(); + provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); let tx = TransactionRequest::default().to(to_two).value(U256::from(i)).from(from_two); let tx = WithOtherFields::new(tx); - api.send_transaction(tx).await.unwrap(); + provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); } let traces = api.trace_filter(tracer).await.unwrap(); - assert_eq!(traces.len(), 5); + assert_eq!(traces.len(), 6); // Test for the following actions: // Create (deploy the contract) @@ -804,11 +804,11 @@ async fn test_trace_filter() { for i in 0..=5 { let tx = TransactionRequest::default().to(to_two).value(U256::from(i)).from(from_two); let tx = WithOtherFields::new(tx); - api.send_transaction(tx).await.unwrap(); + provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); } let traces = api.trace_filter(tracer).await.unwrap(); - assert_eq!(traces.len(), 8); + assert_eq!(traces.len(), 9); // Test Range Error let latest = provider.get_block_number().await.unwrap(); @@ -854,7 +854,7 @@ async fn test_trace_filter() { for i in 0..=10 { let tx = TransactionRequest::default().to(to).value(U256::from(i)).from(from); let tx = WithOtherFields::new(tx); - api.send_transaction(tx).await.unwrap(); + provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); } let traces = api.trace_filter(tracer).await.unwrap(); From be451fb93a0d0ec52152fb67cc6c36cd8fbd7ae1 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Tue, 10 Sep 2024 17:05:06 +0530 Subject: [PATCH 153/184] fix(anvil): prevent panic in ots (#8835) * fix(anvil): prevent panic in ots * fix(anvil): use block_by_number in ots_block_tx & ots_search_transactions * nit * nit --- crates/anvil/src/eth/otterscan/api.rs | 56 ++++++++++++--------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/crates/anvil/src/eth/otterscan/api.rs b/crates/anvil/src/eth/otterscan/api.rs index 798ff205b..e73fe4dd6 100644 --- a/crates/anvil/src/eth/otterscan/api.rs +++ b/crates/anvil/src/eth/otterscan/api.rs @@ -419,20 +419,19 @@ impl EthApi { let receipt_futs = block.transactions.hashes().map(|hash| self.transaction_receipt(hash)); - let receipts = join_all(receipt_futs) - .await - .into_iter() - .map(|r| match r { - Ok(Some(r)) => { - let timestamp = - self.backend.get_block(r.block_number.unwrap()).unwrap().header.timestamp; - let receipt = r.map_inner(OtsReceipt::from); - let res = OtsTransactionReceipt { receipt, timestamp: Some(timestamp) }; - Ok(res) - } - _ => Err(BlockchainError::DataUnavailable), - }) - .collect::>>()?; + let receipts = join_all(receipt_futs.map(|r| async { + if let Ok(Some(r)) = r.await { + let block = self.block_by_number(r.block_number.unwrap().into()).await?; + let timestamp = block.ok_or(BlockchainError::BlockNotFound)?.header.timestamp; + let receipt = r.map_inner(OtsReceipt::from); + Ok(OtsTransactionReceipt { receipt, timestamp: Some(timestamp) }) + } else { + Err(BlockchainError::BlockNotFound) + } + })) + .await + .into_iter() + .collect::>>()?; let transaction_count = block.transactions().len(); let fullblock = OtsBlock { block: block.inner, transaction_count }; @@ -458,23 +457,18 @@ impl EthApi { Ok(Some(t)) => Ok(t.inner), _ => Err(BlockchainError::DataUnavailable), }) - .collect::>()?; - - let receipts = join_all(hashes.iter().map(|hash| async { - match self.transaction_receipt(*hash).await { - Ok(Some(receipt)) => { - let timestamp = self - .backend - .get_block(receipt.block_number.unwrap()) - .unwrap() - .header - .timestamp; - let receipt = receipt.map_inner(OtsReceipt::from); - let res = OtsTransactionReceipt { receipt, timestamp: Some(timestamp) }; - Ok(res) - } - Ok(None) => Err(BlockchainError::DataUnavailable), - Err(e) => Err(e), + .collect::>>()?; + + let receipt_futs = hashes.iter().map(|hash| self.transaction_receipt(*hash)); + + let receipts = join_all(receipt_futs.map(|r| async { + if let Ok(Some(r)) = r.await { + let block = self.block_by_number(r.block_number.unwrap().into()).await?; + let timestamp = block.ok_or(BlockchainError::BlockNotFound)?.header.timestamp; + let receipt = r.map_inner(OtsReceipt::from); + Ok(OtsTransactionReceipt { receipt, timestamp: Some(timestamp) }) + } else { + Err(BlockchainError::BlockNotFound) } })) .await From 2c73013a01264e8577e728bee33961845d126963 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Wed, 11 Sep 2024 13:32:50 +0530 Subject: [PATCH 154/184] chore: bump alloy to fix #8830 (#8838) * bump alloy to fix #8830 * rm alloy patch * nit --- Cargo.lock | 157 +++++++++++-------- Cargo.toml | 93 ++++++----- crates/anvil/core/src/eth/transaction/mod.rs | 12 +- crates/anvil/tests/it/eip7702.rs | 4 +- 4 files changed, 149 insertions(+), 117 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 011a7af0f..6345afda7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,8 +85,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1468e3128e07c7afe4ff13c17e8170c330d12c322f8924b8bf6986a27e0aad3d" dependencies = [ "alloy-eips", "alloy-primitives", @@ -98,8 +99,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335d62de1a887f1b780441f8a3037f39c9fb26839cc9acd891c9b80396145cd5" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -166,8 +168,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c35df7b972b06f1b2f4e8b7a53328522fa788054a9d3e556faf2411c5a51d5a" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -184,8 +187,9 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7210f9206c0fa2a83c824cf8cb6c962126bc9fdc4f41ade1932f14150ef5f6" dependencies = [ "alloy-primitives", "alloy-serde", @@ -206,8 +210,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8866562186d237f1dfeaf989ef941a24764f764bf5c33311e37ead3519c6a429" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -219,8 +224,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe714e233f9eaf410de95a9af6bcd05d3a7f8c8de7a0817221e95a6b642a080" dependencies = [ "alloy-consensus", "alloy-eips", @@ -239,9 +245,11 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c5a38117974c5776a45e140226745a0b664f79736aa900995d8e4121558e064" dependencies = [ + "alloy-eips", "alloy-primitives", "alloy-serde", "serde", @@ -275,8 +283,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c65633d6ef83c3626913c004eaf166a6dd50406f724772ea8567135efd6dc5d3" dependencies = [ "alloy-chains", "alloy-consensus", @@ -313,8 +322,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "949db89abae6193b44cc90ebf2eeb74eb8d2a474383c5e62b45bdcd362e84f8f" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -325,7 +335,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower 0.4.13", + "tower 0.5.0", "tracing", ] @@ -353,8 +363,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fc328bb5d440599ba1b5aa44c0b9ab0625fbc3a403bb5ee94ed4a01ba23e07" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -370,15 +381,16 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower 0.4.13", + "tower 0.5.0", "tracing", "url", ] [[package]] name = "alloy-rpc-types" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f8ff679f94c497a8383f2cd09e2a099266e5f3d5e574bc82b4b379865707dbb" dependencies = [ "alloy-rpc-types-anvil", "alloy-rpc-types-engine", @@ -391,8 +403,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d430bf98148565e67b2c08f033dd5fb27ce901c3481018941ce1524b9ad4fba" dependencies = [ "alloy-primitives", "alloy-serde", @@ -401,8 +414,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66bb45f4c5efe227bcb51d89c97221225169976e18097671a0bd4393d8248a4" dependencies = [ "alloy-consensus", "alloy-eips", @@ -418,8 +432,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a59b1d7c86e0a653e7f3d29954f6de5a2878d8cfd1f010ff93be5c2c48cd3b1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -436,8 +451,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c54375e5a34ec5a2cf607f9ce98c0ece30dc76ad623afeb25d3953a8d7d30f20" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -449,8 +465,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65ae88491edfc8bbd55ba2b22b2ff24d4c522bacd8808461c4a232715fee3d22" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -460,8 +477,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51db8a6428a2159e01b7a43ec7aac801edd0c4db1d4de06f310c288940f16fd3" dependencies = [ "alloy-primitives", "arbitrary", @@ -471,8 +489,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebc1760c13592b7ba3fcd964abba546b8d6a9f10d15e8d92a8263731be33f36" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -486,8 +505,9 @@ dependencies = [ [[package]] name = "alloy-signer-aws" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa0d52968220ddfdb8d332cfddc6c7ae8147779aebb3c463d6023f1d458471b" dependencies = [ "alloy-consensus", "alloy-network", @@ -503,8 +523,9 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d369e98958cf6e2a41b1b1fcee21d73185f358db23751636e3d90b1f4aa184d" dependencies = [ "alloy-consensus", "alloy-network", @@ -520,8 +541,9 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f03b98771184d733139f0475e562f6f8cb8143d0a3765674078c0a3ed00c3b" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -539,8 +561,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bfb3508485aa798efb5725322e414313239274d3780079b7f8c6746b8ee6e1b" dependencies = [ "alloy-consensus", "alloy-network", @@ -558,8 +581,9 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "423c62e0596be68f55a7c03ec4b961d46c74e92ba2d0143fe147608a5c8fe4a4" dependencies = [ "alloy-consensus", "alloy-network", @@ -647,8 +671,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd5dc4e902f1860d54952446d246ac05386311ad61030a2b906ae865416d36e0" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -658,29 +683,31 @@ dependencies = [ "serde_json", "thiserror", "tokio", - "tower 0.4.13", + "tower 0.5.0", "tracing", "url", ] [[package]] name = "alloy-transport-http" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1742b94bb814f1ca6b322a6f9dd38a0252ff45a3119e40e888fb7029afa500ce" dependencies = [ "alloy-json-rpc", "alloy-transport", "reqwest", "serde_json", - "tower 0.4.13", + "tower 0.5.0", "tracing", "url", ] [[package]] name = "alloy-transport-ipc" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be321aac6f06d86855d41d4ce9ff9feb877fe7e9fe1cafce7380b981c12398c7" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -699,8 +726,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "0.3.1" -source = "git+https://github.com/alloy-rs/alloy?rev=7fab7ee#7fab7ee4b27220e755d21f4b50108d083ea341fd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8ed861e7030001364c8ffa2db63541f7bae275a6e636de7616c20f2fd3dc0c3" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -5967,9 +5995,9 @@ checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" [[package]] name = "op-alloy-consensus" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b7fbb0f5c3754c22c6ea30e100dca6aea73b747e693e27763e23ca92fb02f2f" +checksum = "ad134a77fdfebac469526756b207c7889593657eeaca374200332ec89175e27a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5983,10 +6011,11 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1fbb93dcb71aba9cd555784375011efce1fdaaea67e01972a0a9bc9eb90c626" +checksum = "f9fbd440cd8a5ccfa7c4c085b29fd0f0a1574fb53e1572f8e070cc105039e42b" dependencies = [ + "alloy-eips", "alloy-network", "alloy-primitives", "alloy-rpc-types-eth", @@ -8715,6 +8744,10 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36b837f86b25d7c0d7988f00a54e74739be6477f2aac6201b8f429a7569991b7" dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 0.1.2", "tower-layer", "tower-service", ] diff --git a/Cargo.toml b/Cargo.toml index fe8730a6b..b05398538 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -172,27 +172,27 @@ revm-inspectors = { version = "0.6", features = ["serde"] } ethers-contract-abigen = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { version = "0.3.1", default-features = false } -alloy-contract = { version = "0.3.1", default-features = false } -alloy-eips = { version = "0.3.1", default-features = false } -alloy-genesis = { version = "0.3.1", default-features = false } -alloy-json-rpc = { version = "0.3.1", default-features = false } -alloy-network = { version = "0.3.1", default-features = false } -alloy-provider = { version = "0.3.1", default-features = false } -alloy-pubsub = { version = "0.3.1", default-features = false } -alloy-rpc-client = { version = "0.3.1", default-features = false } -alloy-rpc-types = { version = "0.3.1", default-features = true } -alloy-serde = { version = "0.3.1", default-features = false } -alloy-signer = { version = "0.3.1", default-features = false } -alloy-signer-aws = { version = "0.3.1", default-features = false } -alloy-signer-gcp = { version = "0.3.1", default-features = false } -alloy-signer-ledger = { version = "0.3.1", default-features = false } -alloy-signer-local = { version = "0.3.1", default-features = false } -alloy-signer-trezor = { version = "0.3.1", default-features = false } -alloy-transport = { version = "0.3.1", default-features = false } -alloy-transport-http = { version = "0.3.1", default-features = false } -alloy-transport-ipc = { version = "0.3.1", default-features = false } -alloy-transport-ws = { version = "0.3.1", default-features = false } +alloy-consensus = { version = "0.3.3", default-features = false } +alloy-contract = { version = "0.3.3", default-features = false } +alloy-eips = { version = "0.3.3", default-features = false } +alloy-genesis = { version = "0.3.3", default-features = false } +alloy-json-rpc = { version = "0.3.3", default-features = false } +alloy-network = { version = "0.3.3", default-features = false } +alloy-provider = { version = "0.3.3", default-features = false } +alloy-pubsub = { version = "0.3.3", default-features = false } +alloy-rpc-client = { version = "0.3.3", default-features = false } +alloy-rpc-types = { version = "0.3.3", default-features = true } +alloy-serde = { version = "0.3.3", default-features = false } +alloy-signer = { version = "0.3.3", default-features = false } +alloy-signer-aws = { version = "0.3.3", default-features = false } +alloy-signer-gcp = { version = "0.3.3", default-features = false } +alloy-signer-ledger = { version = "0.3.3", default-features = false } +alloy-signer-local = { version = "0.3.3", default-features = false } +alloy-signer-trezor = { version = "0.3.3", default-features = false } +alloy-transport = { version = "0.3.3", default-features = false } +alloy-transport-http = { version = "0.3.3", default-features = false } +alloy-transport-ipc = { version = "0.3.3", default-features = false } +alloy-transport-ws = { version = "0.3.3", default-features = false } ## alloy-core alloy-dyn-abi = "0.8.1" @@ -208,7 +208,7 @@ alloy-rlp = "0.3" alloy-trie = "0.5.0" ## op-alloy for tests in anvil -op-alloy-rpc-types = "0.2.8" +op-alloy-rpc-types = "0.2.9" ## misc async-trait = "0.1" @@ -267,28 +267,27 @@ soldeer = "=0.3.4" proptest = "1" comfy-table = "7" -[patch.crates-io] -# https://github.com/alloy-rs/alloy/pull/1229 + https://github.com/alloy-rs/alloy/pull/1243 -alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } -alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } -alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } -alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } -alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } -alloy-network-primitives = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } -alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } -alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } -alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } -alloy-rpc-types-eth = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } -alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } -alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } -alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } -alloy-signer-gcp = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } -alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } -alloy-signer-local = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } -alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } -alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } -alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } -alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } -alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# [patch.crates-io] +# alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# alloy-network-primitives = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# alloy-rpc-types-eth = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# alloy-signer-gcp = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# alloy-signer-local = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } +# alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 7d80450f3..128f6e9cd 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -390,7 +390,7 @@ pub fn to_alloy_transaction_with_hash_and_sender( block_number: None, transaction_index: None, from, - to: t.tx().to.to().copied(), + to: Some(t.tx().to), value: t.tx().value, gas_price: Some(t.tx().max_fee_per_gas), max_fee_per_gas: Some(t.tx().max_fee_per_gas), @@ -602,7 +602,7 @@ impl PendingTransaction { } = tx.tx(); TxEnv { caller, - transact_to: *to, + transact_to: TxKind::Call(*to), data: input.clone(), chain_id: Some(*chain_id), nonce: Some(*nonce), @@ -851,7 +851,7 @@ impl TypedTransaction { access_list: t.tx().tx().access_list.clone(), }, Self::EIP7702(t) => TransactionEssentials { - kind: t.tx().to, + kind: TxKind::Call(t.tx().to), input: t.tx().input.clone(), nonce: t.tx().nonce, gas_limit: t.tx().gas_limit, @@ -975,7 +975,7 @@ impl TypedTransaction { Self::EIP2930(tx) => tx.tx().to, Self::EIP1559(tx) => tx.tx().to, Self::EIP4844(tx) => TxKind::Call(tx.tx().tx().to), - Self::EIP7702(tx) => tx.tx().to, + Self::EIP7702(tx) => TxKind::Call(tx.tx().to), Self::Deposit(tx) => tx.kind, } } @@ -1105,7 +1105,7 @@ impl TryFrom for TypedTransaction { max_priority_fee_per_gas: tx .max_priority_fee_per_gas .ok_or(ConversionError::MissingMaxPriorityFeePerGas)?, - to: tx.to.map_or(TxKind::Create, TxKind::Call), + to: tx.to.ok_or(ConversionError::MissingTo)?, value: tx.value, access_list: tx.access_list.ok_or(ConversionError::MissingAccessList)?, input: tx.input, @@ -1406,7 +1406,7 @@ impl From> for OtsReceipt { let receipt = ReceiptWithBloom::::from(value); let status = receipt.status(); let cumulative_gas_used = receipt.cumulative_gas_used() as u64; - let logs = receipt.receipt.logs.into_iter().map(|x| x.inner).collect(); + let logs = receipt.logs().to_vec(); let logs_bloom = receipt.logs_bloom; Self { status, cumulative_gas_used, logs: Some(logs), logs_bloom: Some(logs_bloom), r#type } diff --git a/crates/anvil/tests/it/eip7702.rs b/crates/anvil/tests/it/eip7702.rs index 828e0d351..2a0cc0e43 100644 --- a/crates/anvil/tests/it/eip7702.rs +++ b/crates/anvil/tests/it/eip7702.rs @@ -1,7 +1,7 @@ use crate::utils::http_provider; use alloy_consensus::{transaction::TxEip7702, SignableTransaction}; use alloy_network::{ReceiptResponse, TransactionBuilder, TxSignerSync}; -use alloy_primitives::{bytes, TxKind, U256}; +use alloy_primitives::{bytes, U256}; use alloy_provider::Provider; use alloy_rpc_types::{Authorization, TransactionRequest}; use alloy_serde::WithOtherFields; @@ -57,7 +57,7 @@ async fn can_send_eip7702_tx() { max_priority_fee_per_gas: eip1559_est.max_priority_fee_per_gas, gas_limit: 100000, chain_id: 31337, - to: TxKind::Call(from), + to: from, input: bytes!("11112222"), authorization_list: vec![authorization], ..Default::default() From d663f38be3114ccb94f08fe3b8ea26e27e2043c1 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 11 Sep 2024 16:38:38 +0300 Subject: [PATCH 155/184] feat(cheatcodes): additional cheatcodes to aid in symbolic testing (#8807) * feat(cheatcodes): additional cheatcodes to aid in symbolic testing * Support copies from arbitrary storage, docs * Changes after review: - separate cheatcodes tests with specific seed - better way to match mocked function - arbitrary_storage_end instead multiple calls - generate arbitrary value only when needed * Update crates/cheatcodes/src/utils.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * Fix tests with isolate-by-default --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/cheatcodes/assets/cheatcodes.json | 60 +++++++ crates/cheatcodes/spec/src/vm.rs | 17 ++ crates/cheatcodes/src/evm.rs | 21 ++- crates/cheatcodes/src/evm/mock.rs | 9 + crates/cheatcodes/src/inspector.rs | 140 +++++++++++++++- crates/cheatcodes/src/utils.rs | 30 +++- crates/evm/evm/src/inspectors/stack.rs | 12 ++ crates/forge/tests/it/cheats.rs | 26 ++- testdata/cheats/Vm.sol | 3 + .../default/cheats/ArbitraryStorage.t.sol | 125 ++++++++++++++ testdata/default/cheats/CopyStorage.t.sol | 158 ++++++++++++++++++ testdata/default/cheats/MockFunction.t.sol | 74 ++++++++ 12 files changed, 667 insertions(+), 8 deletions(-) create mode 100644 testdata/default/cheats/ArbitraryStorage.t.sol create mode 100644 testdata/default/cheats/CopyStorage.t.sol create mode 100644 testdata/default/cheats/MockFunction.t.sol diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 4517f075e..da501a11e 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -3331,6 +3331,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "copyStorage", + "description": "Utility cheatcode to copy storage of `from` contract to another `to` contract.", + "declaration": "function copyStorage(address from, address to) external;", + "visibility": "external", + "mutability": "", + "signature": "copyStorage(address,address)", + "selector": "0x203dac0d", + "selectorBytes": [ + 32, + 61, + 172, + 13 + ] + }, + "group": "utilities", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "createDir", @@ -5591,6 +5611,26 @@ "status": "stable", "safety": "unsafe" }, + { + "func": { + "id": "mockFunction", + "description": "Whenever a call is made to `callee` with calldata `data`, this cheatcode instead calls\n`target` with the same calldata. This functionality is similar to a delegate call made to\n`target` contract from `callee`.\nCan be used to substitute a call to a function with another implementation that captures\nthe primary logic of the original function but is easier to reason about.\nIf calldata is not a strict match then partial match by selector is attempted.", + "declaration": "function mockFunction(address callee, address target, bytes calldata data) external;", + "visibility": "external", + "mutability": "", + "signature": "mockFunction(address,address,bytes)", + "selector": "0xadf84d21", + "selectorBytes": [ + 173, + 248, + 77, + 33 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, { "func": { "id": "parseAddress", @@ -7791,6 +7831,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "setArbitraryStorage", + "description": "Utility cheatcode to set arbitrary storage for given target address.", + "declaration": "function setArbitraryStorage(address target) external;", + "visibility": "external", + "mutability": "", + "signature": "setArbitraryStorage(address)", + "selector": "0xe1631837", + "selectorBytes": [ + 225, + 99, + 24, + 55 + ] + }, + "group": "utilities", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "setBlockhash", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 980bab066..d0a921485 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -473,6 +473,15 @@ interface Vm { function mockCallRevert(address callee, uint256 msgValue, bytes calldata data, bytes calldata revertData) external; + /// Whenever a call is made to `callee` with calldata `data`, this cheatcode instead calls + /// `target` with the same calldata. This functionality is similar to a delegate call made to + /// `target` contract from `callee`. + /// Can be used to substitute a call to a function with another implementation that captures + /// the primary logic of the original function but is easier to reason about. + /// If calldata is not a strict match then partial match by selector is attempted. + #[cheatcode(group = Evm, safety = Unsafe)] + function mockFunction(address callee, address target, bytes calldata data) external; + // --- Impersonation (pranks) --- /// Sets the *next* call's `msg.sender` to be the input address. @@ -2303,6 +2312,14 @@ interface Vm { /// Unpauses collection of call traces. #[cheatcode(group = Utilities)] function resumeTracing() external view; + + /// Utility cheatcode to copy storage of `from` contract to another `to` contract. + #[cheatcode(group = Utilities)] + function copyStorage(address from, address to) external; + + /// Utility cheatcode to set arbitrary storage for given target address. + #[cheatcode(group = Utilities)] + function setArbitraryStorage(address target) external; } } diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index a3d517387..703d7db12 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -13,6 +13,7 @@ use foundry_evm_core::{ backend::{DatabaseExt, RevertSnapshotAction}, constants::{CALLER, CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, TEST_CONTRACT_ADDRESS}, }; +use rand::Rng; use revm::{ primitives::{Account, Bytecode, SpecId, KECCAK_EMPTY}, InnerEvmContext, @@ -89,7 +90,25 @@ impl Cheatcode for loadCall { let Self { target, slot } = *self; ensure_not_precompile!(&target, ccx); ccx.ecx.load_account(target)?; - let val = ccx.ecx.sload(target, slot.into())?; + let mut val = ccx.ecx.sload(target, slot.into())?; + + if val.is_cold && val.data.is_zero() { + if ccx.state.arbitrary_storage.is_arbitrary(&target) { + // If storage slot is untouched and load from a target with arbitrary storage, + // then set random value for current slot. + let rand_value = ccx.state.rng().gen(); + ccx.state.arbitrary_storage.save(ccx.ecx, target, slot.into(), rand_value); + val.data = rand_value; + } else if ccx.state.arbitrary_storage.is_copy(&target) { + // If storage slot is untouched and load from a target that copies storage from + // a source address with arbitrary storage, then copy existing arbitrary value. + // If no arbitrary value generated yet, then the random one is saved and set. + let rand_value = ccx.state.rng().gen(); + val.data = + ccx.state.arbitrary_storage.copy(ccx.ecx, target, slot.into(), rand_value); + } + } + Ok(val.abi_encode()) } } diff --git a/crates/cheatcodes/src/evm/mock.rs b/crates/cheatcodes/src/evm/mock.rs index 1a6ffb46a..cd7c459b6 100644 --- a/crates/cheatcodes/src/evm/mock.rs +++ b/crates/cheatcodes/src/evm/mock.rs @@ -89,6 +89,15 @@ impl Cheatcode for mockCallRevert_1Call { } } +impl Cheatcode for mockFunctionCall { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { callee, target, data } = self; + state.mocked_functions.entry(*callee).or_default().insert(data.clone(), *target); + + Ok(Default::default()) + } +} + #[allow(clippy::ptr_arg)] // Not public API, doesn't matter fn mock_call( state: &mut Cheatcodes, diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index f5238d810..e09e1e8c8 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -41,7 +41,7 @@ use revm::{ EOFCreateInputs, EOFCreateKind, Gas, InstructionResult, Interpreter, InterpreterAction, InterpreterResult, }, - primitives::{BlockEnv, CreateScheme, EVMError, SpecId, EOF_MAGIC_BYTES}, + primitives::{BlockEnv, CreateScheme, EVMError, EvmStorageSlot, SpecId, EOF_MAGIC_BYTES}, EvmContext, InnerEvmContext, Inspector, }; use rustc_hash::FxHashMap; @@ -254,6 +254,89 @@ impl GasMetering { } } +/// Holds data about arbitrary storage. +#[derive(Clone, Debug, Default)] +pub struct ArbitraryStorage { + /// Mapping of arbitrary storage addresses to generated values (slot, arbitrary value). + /// (SLOADs return random value if storage slot wasn't accessed). + /// Changed values are recorded and used to copy storage to different addresses. + pub values: HashMap>, + /// Mapping of address with storage copied to arbitrary storage address source. + pub copies: HashMap, +} + +impl ArbitraryStorage { + /// Whether the given address has arbitrary storage. + pub fn is_arbitrary(&self, address: &Address) -> bool { + self.values.contains_key(address) + } + + /// Whether the given address is a copy of an address with arbitrary storage. + pub fn is_copy(&self, address: &Address) -> bool { + self.copies.contains_key(address) + } + + /// Marks an address with arbitrary storage. + pub fn mark_arbitrary(&mut self, address: &Address) { + self.values.insert(*address, HashMap::default()); + } + + /// Maps an address that copies storage with the arbitrary storage address. + pub fn mark_copy(&mut self, from: &Address, to: &Address) { + if self.is_arbitrary(from) { + self.copies.insert(*to, *from); + } + } + + /// Saves arbitrary storage value for a given address: + /// - store value in changed values cache. + /// - update account's storage with given value. + pub fn save( + &mut self, + ecx: &mut InnerEvmContext, + address: Address, + slot: U256, + data: U256, + ) { + self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data); + if let Ok(mut account) = ecx.load_account(address) { + account.storage.insert(slot, EvmStorageSlot::new(data)); + } + } + + /// Copies arbitrary storage value from source address to the given target address: + /// - if a value is present in arbitrary values cache, then update target storage and return + /// existing value. + /// - if no value was yet generated for given slot, then save new value in cache and update both + /// source and target storages. + pub fn copy( + &mut self, + ecx: &mut InnerEvmContext, + target: Address, + slot: U256, + new_value: U256, + ) -> U256 { + let source = self.copies.get(&target).expect("missing arbitrary copy target entry"); + let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage"); + let value = match storage_cache.get(&slot) { + Some(value) => *value, + None => { + storage_cache.insert(slot, new_value); + // Update source storage with new value. + if let Ok(mut source_account) = ecx.load_account(*source) { + source_account.storage.insert(slot, EvmStorageSlot::new(new_value)); + } + new_value + } + }; + // Update target storage with new value. + if let Ok(mut target_account) = ecx.load_account(target) { + target_account.storage.insert(slot, EvmStorageSlot::new(value)); + } + value + } +} + /// List of transactions that can be broadcasted. pub type BroadcastableTransactions = VecDeque; @@ -320,6 +403,9 @@ pub struct Cheatcodes { // **Note**: inner must a BTreeMap because of special `Ord` impl for `MockCallDataContext` pub mocked_calls: HashMap>, + /// Mocked functions. Maps target address to be mocked to pair of (calldata, mock address). + pub mocked_functions: HashMap>, + /// Expected calls pub expected_calls: ExpectedCallTracker, /// Expected emits @@ -368,6 +454,9 @@ pub struct Cheatcodes { /// Ignored traces. pub ignored_traces: IgnoredTraces, + + /// Addresses with arbitrary storage. + pub arbitrary_storage: ArbitraryStorage, } // This is not derived because calling this in `fn new` with `..Default::default()` creates a second @@ -396,6 +485,7 @@ impl Cheatcodes { recorded_account_diffs_stack: Default::default(), recorded_logs: Default::default(), mocked_calls: Default::default(), + mocked_functions: Default::default(), expected_calls: Default::default(), expected_emits: Default::default(), allowed_mem_writes: Default::default(), @@ -410,6 +500,7 @@ impl Cheatcodes { breakpoints: Default::default(), rng: Default::default(), ignored_traces: Default::default(), + arbitrary_storage: Default::default(), } } @@ -1045,7 +1136,7 @@ impl Inspector for Cheatcodes { } #[inline] - fn step_end(&mut self, interpreter: &mut Interpreter, _ecx: &mut EvmContext) { + fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { if self.gas_metering.paused { self.meter_gas_end(interpreter); } @@ -1053,6 +1144,14 @@ impl Inspector for Cheatcodes { if self.gas_metering.touched { self.meter_gas_check(interpreter); } + + // `setArbitraryStorage` and `copyStorage`: add arbitrary values to storage. + if (self.arbitrary_storage.is_arbitrary(&interpreter.contract().target_address) || + self.arbitrary_storage.is_copy(&interpreter.contract().target_address)) && + interpreter.current_opcode() == op::SLOAD + { + self.arbitrary_storage_end(interpreter, ecx); + } } fn log(&mut self, interpreter: &mut Interpreter, _ecx: &mut EvmContext, log: &Log) { @@ -1465,6 +1564,43 @@ impl Cheatcodes { } } + /// Generates or copies arbitrary values for storage slots. + /// Invoked in inspector `step_end` (when the current opcode is not executed), if current opcode + /// to execute is `SLOAD` and storage slot is cold. + /// Ensures that in next step (when `SLOAD` opcode is executed) an arbitrary value is returned: + /// - copies the existing arbitrary storage value (or the new generated one if no value in + /// cache) from mapped source address to the target address. + /// - generates arbitrary value and saves it in target address storage. + #[cold] + fn arbitrary_storage_end( + &mut self, + interpreter: &mut Interpreter, + ecx: &mut EvmContext, + ) { + let key = try_or_return!(interpreter.stack().peek(0)); + let target_address = interpreter.contract().target_address; + if let Ok(value) = ecx.sload(target_address, key) { + if value.is_cold && value.data.is_zero() { + let arbitrary_value = self.rng().gen(); + if self.arbitrary_storage.is_copy(&target_address) { + self.arbitrary_storage.copy( + &mut ecx.inner, + target_address, + key, + arbitrary_value, + ); + } else { + self.arbitrary_storage.save( + &mut ecx.inner, + target_address, + key, + arbitrary_value, + ); + } + } + } + } + /// Records storage slots reads and writes. #[cold] fn record_accesses(&mut self, interpreter: &mut Interpreter) { diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index 642cf83ab..0896f2b31 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -1,6 +1,6 @@ //! Implementations of [`Utilities`](spec::Group::Utilities) cheatcodes. -use crate::{Cheatcode, Cheatcodes, Result, Vm::*}; +use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Result, Vm::*}; use alloy_primitives::{Address, U256}; use alloy_sol_types::SolValue; use foundry_common::ens::namehash; @@ -149,3 +149,31 @@ impl Cheatcode for resumeTracingCall { Ok(Default::default()) } } + +impl Cheatcode for setArbitraryStorageCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { target } = self; + ccx.state.arbitrary_storage.mark_arbitrary(target); + + Ok(Default::default()) + } +} + +impl Cheatcode for copyStorageCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { from, to } = self; + ensure!( + !ccx.state.arbitrary_storage.is_arbitrary(to), + "target address cannot have arbitrary storage" + ); + if let Ok(from_account) = ccx.load_account(*from) { + let from_storage = from_account.storage.clone(); + if let Ok(mut to_account) = ccx.load_account(*to) { + to_account.storage = from_storage; + ccx.state.arbitrary_storage.mark_copy(from, to); + } + } + + Ok(Default::default()) + } +} diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index e44d499ea..3df8dc8f0 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -726,6 +726,18 @@ impl<'a, DB: DatabaseExt> Inspector for InspectorStackRefMut<'a> { ecx.journaled_state.depth += self.in_inner_context as usize; if let Some(cheatcodes) = self.cheatcodes.as_deref_mut() { + // Handle mocked functions, replace bytecode address with mock if matched. + if let Some(mocks) = cheatcodes.mocked_functions.get(&call.target_address) { + // Check if any mock function set for call data or if catch-all mock function set + // for selector. + if let Some(target) = mocks + .get(&call.input) + .or_else(|| call.input.get(..4).and_then(|selector| mocks.get(selector))) + { + call.bytecode_address = *target; + } + } + if let Some(output) = cheatcodes.call_with_executor(ecx, call, self.inner) { if output.result.result != InstructionResult::Continue { ecx.journaled_state.depth -= self.in_inner_context as usize; diff --git a/crates/forge/tests/it/cheats.rs b/crates/forge/tests/it/cheats.rs index 2bbbee902..a60602cbc 100644 --- a/crates/forge/tests/it/cheats.rs +++ b/crates/forge/tests/it/cheats.rs @@ -7,14 +7,16 @@ use crate::{ TEST_DATA_MULTI_VERSION, }, }; +use alloy_primitives::U256; use foundry_config::{fs_permissions::PathPermission, FsPermissions}; use foundry_test_utils::Filter; -/// Executes all cheat code tests but not fork cheat codes or tests that require isolation mode +/// Executes all cheat code tests but not fork cheat codes or tests that require isolation mode or +/// specific seed. async fn test_cheats_local(test_data: &ForgeTestData) { let mut filter = Filter::new(".*", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}*")) .exclude_paths("Fork") - .exclude_contracts("Isolated"); + .exclude_contracts("(Isolated|WithSeed)"); // Exclude FFI tests on Windows because no `echo`, and file tests that expect certain file paths if cfg!(windows) { @@ -22,7 +24,7 @@ async fn test_cheats_local(test_data: &ForgeTestData) { } if cfg!(feature = "isolate-by-default") { - filter = filter.exclude_contracts("LastCallGasDefaultTest"); + filter = filter.exclude_contracts("(LastCallGasDefaultTest|MockFunctionTest|WithSeed)"); } let mut config = test_data.config.clone(); @@ -32,7 +34,7 @@ async fn test_cheats_local(test_data: &ForgeTestData) { TestConfig::with_filter(runner, filter).run().await; } -/// Executes subset of all cheat code tests in isolation mode +/// Executes subset of all cheat code tests in isolation mode. async fn test_cheats_local_isolated(test_data: &ForgeTestData) { let filter = Filter::new(".*", ".*(Isolated)", &format!(".*cheats{RE_PATH_SEPARATOR}*")); @@ -43,6 +45,17 @@ async fn test_cheats_local_isolated(test_data: &ForgeTestData) { TestConfig::with_filter(runner, filter).run().await; } +/// Executes subset of all cheat code tests using a specific seed. +async fn test_cheats_local_with_seed(test_data: &ForgeTestData) { + let filter = Filter::new(".*", ".*(WithSeed)", &format!(".*cheats{RE_PATH_SEPARATOR}*")); + + let mut config = test_data.config.clone(); + config.fuzz.seed = Some(U256::from(100)); + let runner = test_data.runner_with_config(config); + + TestConfig::with_filter(runner, filter).run().await; +} + #[tokio::test(flavor = "multi_thread")] async fn test_cheats_local_default() { test_cheats_local(&TEST_DATA_DEFAULT).await @@ -53,6 +66,11 @@ async fn test_cheats_local_default_isolated() { test_cheats_local_isolated(&TEST_DATA_DEFAULT).await } +#[tokio::test(flavor = "multi_thread")] +async fn test_cheats_local_default_with_seed() { + test_cheats_local_with_seed(&TEST_DATA_DEFAULT).await +} + #[tokio::test(flavor = "multi_thread")] async fn test_cheats_local_multi_version() { test_cheats_local(&TEST_DATA_MULTI_VERSION).await diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 5b6750237..edc7dad32 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -162,6 +162,7 @@ interface Vm { function computeCreateAddress(address deployer, uint256 nonce) external pure returns (address); function cool(address target) external; function copyFile(string calldata from, string calldata to) external returns (uint64 copied); + function copyStorage(address from, address to) external; function createDir(string calldata path, bool recursive) external; function createFork(string calldata urlOrAlias) external returns (uint256 forkId); function createFork(string calldata urlOrAlias, uint256 blockNumber) external returns (uint256 forkId); @@ -275,6 +276,7 @@ interface Vm { function mockCallRevert(address callee, uint256 msgValue, bytes calldata data, bytes calldata revertData) external; function mockCall(address callee, bytes calldata data, bytes calldata returnData) external; function mockCall(address callee, uint256 msgValue, bytes calldata data, bytes calldata returnData) external; + function mockFunction(address callee, address target, bytes calldata data) external; function parseAddress(string calldata stringifiedValue) external pure returns (address parsedValue); function parseBool(string calldata stringifiedValue) external pure returns (bool parsedValue); function parseBytes(string calldata stringifiedValue) external pure returns (bytes memory parsedValue); @@ -385,6 +387,7 @@ interface Vm { function serializeUintToHex(string calldata objectKey, string calldata valueKey, uint256 value) external returns (string memory json); function serializeUint(string calldata objectKey, string calldata valueKey, uint256 value) external returns (string memory json); function serializeUint(string calldata objectKey, string calldata valueKey, uint256[] calldata values) external returns (string memory json); + function setArbitraryStorage(address target) external; function setBlockhash(uint256 blockNumber, bytes32 blockHash) external; function setEnv(string calldata name, string calldata value) external; function setNonce(address account, uint64 newNonce) external; diff --git a/testdata/default/cheats/ArbitraryStorage.t.sol b/testdata/default/cheats/ArbitraryStorage.t.sol new file mode 100644 index 000000000..86910279e --- /dev/null +++ b/testdata/default/cheats/ArbitraryStorage.t.sol @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract Counter { + uint256 public a; + address public b; + int8 public c; + address[] public owners; + + function setA(uint256 _a) public { + a = _a; + } + + function setB(address _b) public { + b = _b; + } + + function getOwner(uint256 pos) public view returns (address) { + return owners[pos]; + } + + function setOwner(uint256 pos, address owner) public { + owners[pos] = owner; + } +} + +contract CounterArbitraryStorageWithSeedTest is DSTest { + Vm vm = Vm(HEVM_ADDRESS); + + function test_fresh_storage() public { + uint256 index = 55; + Counter counter = new Counter(); + vm.setArbitraryStorage(address(counter)); + // Next call would fail with array out of bounds without arbitrary storage. + address owner = counter.getOwner(index); + // Subsequent calls should retrieve same value + assertEq(counter.getOwner(index), owner); + // Change slot and make sure new value retrieved + counter.setOwner(index, address(111)); + assertEq(counter.getOwner(index), address(111)); + } + + function test_arbitrary_storage_warm() public { + Counter counter = new Counter(); + vm.setArbitraryStorage(address(counter)); + assertGt(counter.a(), 0); + counter.setA(0); + // This should remain 0 if explicitly set. + assertEq(counter.a(), 0); + counter.setA(11); + assertEq(counter.a(), 11); + } + + function test_arbitrary_storage_multiple_read_writes() public { + Counter counter = new Counter(); + vm.setArbitraryStorage(address(counter)); + uint256 slot1 = vm.randomUint(0, 100); + uint256 slot2 = vm.randomUint(0, 100); + require(slot1 != slot2, "random positions should be different"); + address alice = counter.owners(slot1); + address bob = counter.owners(slot2); + require(alice != bob, "random storage values should be different"); + counter.setOwner(slot1, bob); + counter.setOwner(slot2, alice); + assertEq(alice, counter.owners(slot2)); + assertEq(bob, counter.owners(slot1)); + } +} + +contract AContract { + uint256[] public a; + address[] public b; + int8[] public c; + bytes32[] public d; +} + +contract AContractArbitraryStorageWithSeedTest is DSTest { + Vm vm = Vm(HEVM_ADDRESS); + + function test_arbitrary_storage_with_seed() public { + AContract target = new AContract(); + vm.setArbitraryStorage(address(target)); + assertEq(target.a(11), 85286582241781868037363115933978803127245343755841464083427462398552335014708); + assertEq(target.b(22), 0x939180Daa938F9e18Ff0E76c112D25107D358B02); + assertEq(target.c(33), -104); + assertEq(target.d(44), 0x6c178fa9c434f142df61a5355cc2b8d07be691b98dabf5b1a924f2bce97a19c7); + } +} + +contract SymbolicStore { + uint256 public testNumber = 1337; // slot 0 + + constructor() {} +} + +contract SymbolicStorageWithSeedTest is DSTest { + Vm vm = Vm(HEVM_ADDRESS); + + function test_SymbolicStorage() public { + uint256 slot = vm.randomUint(0, 100); + address addr = 0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8; + vm.setArbitraryStorage(addr); + bytes32 value = vm.load(addr, bytes32(slot)); + assertEq(uint256(value), 85286582241781868037363115933978803127245343755841464083427462398552335014708); + // Load slot again and make sure we get same value. + bytes32 value1 = vm.load(addr, bytes32(slot)); + assertEq(uint256(value), uint256(value1)); + } + + function test_SymbolicStorage1() public { + uint256 slot = vm.randomUint(0, 100); + SymbolicStore myStore = new SymbolicStore(); + vm.setArbitraryStorage(address(myStore)); + bytes32 value = vm.load(address(myStore), bytes32(uint256(slot))); + assertEq(uint256(value), 85286582241781868037363115933978803127245343755841464083427462398552335014708); + } + + function testEmptyInitialStorage(uint256 slot) public { + bytes32 storage_value = vm.load(address(vm), bytes32(slot)); + assertEq(uint256(storage_value), 0); + } +} diff --git a/testdata/default/cheats/CopyStorage.t.sol b/testdata/default/cheats/CopyStorage.t.sol new file mode 100644 index 000000000..895847497 --- /dev/null +++ b/testdata/default/cheats/CopyStorage.t.sol @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract Counter { + uint256 public a; + address public b; + int256[] public c; + + function setA(uint256 _a) public { + a = _a; + } + + function setB(address _b) public { + b = _b; + } +} + +contract CounterWithSeedTest is DSTest { + Counter public counter; + Counter public counter1; + Vm vm = Vm(HEVM_ADDRESS); + + function test_copy_storage() public { + counter = new Counter(); + counter.setA(1000); + counter.setB(address(27)); + counter1 = new Counter(); + counter1.setA(11); + counter1.setB(address(50)); + + assertEq(counter.a(), 1000); + assertEq(counter.b(), address(27)); + assertEq(counter1.a(), 11); + assertEq(counter1.b(), address(50)); + vm.copyStorage(address(counter), address(counter1)); + assertEq(counter.a(), 1000); + assertEq(counter.b(), address(27)); + assertEq(counter1.a(), 1000); + assertEq(counter1.b(), address(27)); + } + + function test_copy_storage_from_arbitrary() public { + counter = new Counter(); + counter1 = new Counter(); + vm.setArbitraryStorage(address(counter)); + vm.copyStorage(address(counter), address(counter1)); + + // Make sure untouched storage has same values. + assertEq(counter.a(), counter1.a()); + assertEq(counter.b(), counter1.b()); + assertEq(counter.c(33), counter1.c(33)); + + // Change storage in source storage contract and make sure copy is not changed. + counter.setA(1000); + counter1.setB(address(50)); + assertEq(counter.a(), 1000); + assertEq(counter1.a(), 40426841063417815470953489044557166618267862781491517122018165313568904172524); + assertEq(counter.b(), 0x485E9Cc0ef187E54A3AB45b50c3DcE43f2C223B1); + assertEq(counter1.b(), address(50)); + } +} + +contract CopyStorageContract { + uint256 public x; +} + +contract CopyStorageTest is DSTest { + CopyStorageContract csc_1; + CopyStorageContract csc_2; + CopyStorageContract csc_3; + Vm vm = Vm(HEVM_ADDRESS); + + function _storeUInt256(address contractAddress, uint256 slot, uint256 value) internal { + vm.store(contractAddress, bytes32(slot), bytes32(value)); + } + + function setUp() public { + csc_1 = new CopyStorageContract(); + csc_2 = new CopyStorageContract(); + csc_3 = new CopyStorageContract(); + } + + function test_copy_storage() public { + // Make the storage of first contract symbolic + vm.setArbitraryStorage(address(csc_1)); + // and explicitly put a constrained symbolic value into the slot for `x` + uint256 x_1 = vm.randomUint(); + _storeUInt256(address(csc_1), 0, x_1); + // `x` of second contract is uninitialized + assert(csc_2.x() == 0); + // Copy storage from first to second contract + vm.copyStorage(address(csc_1), address(csc_2)); + // `x` of second contract is now the `x` of the first + assert(csc_2.x() == x_1); + } + + function test_copy_storage_same_values_on_load() public { + // Make the storage of first contract symbolic + vm.setArbitraryStorage(address(csc_1)); + vm.copyStorage(address(csc_1), address(csc_2)); + uint256 slot1 = vm.randomUint(0, 100); + uint256 slot2 = vm.randomUint(0, 100); + bytes32 value1 = vm.load(address(csc_1), bytes32(slot1)); + bytes32 value2 = vm.load(address(csc_1), bytes32(slot2)); + + bytes32 value3 = vm.load(address(csc_2), bytes32(slot1)); + bytes32 value4 = vm.load(address(csc_2), bytes32(slot2)); + + // Check storage values are the same for both source and target contracts. + assertEq(value1, value3); + assertEq(value2, value4); + } + + function test_copy_storage_consistent_values() public { + // Make the storage of first contract symbolic. + vm.setArbitraryStorage(address(csc_1)); + // Copy arbitrary storage to 2 contracts. + vm.copyStorage(address(csc_1), address(csc_2)); + vm.copyStorage(address(csc_1), address(csc_3)); + uint256 slot1 = vm.randomUint(0, 100); + uint256 slot2 = vm.randomUint(0, 100); + + // Load slot 1 from 1st copied contract and slot2 from symbolic contract. + bytes32 value3 = vm.load(address(csc_2), bytes32(slot1)); + bytes32 value2 = vm.load(address(csc_1), bytes32(slot2)); + + bytes32 value1 = vm.load(address(csc_1), bytes32(slot1)); + bytes32 value4 = vm.load(address(csc_2), bytes32(slot2)); + + // Make sure same values for both copied and symbolic contract. + assertEq(value3, value1); + assertEq(value2, value4); + + uint256 x_1 = vm.randomUint(); + // Change slot1 of 1st copied contract. + _storeUInt256(address(csc_2), slot1, x_1); + value3 = vm.load(address(csc_2), bytes32(slot1)); + bytes32 value5 = vm.load(address(csc_3), bytes32(slot1)); + // Make sure value for 1st contract copied is different than symbolic contract value. + assert(value3 != value1); + // Make sure same values for 2nd contract copied and symbolic contract. + assertEq(value5, value1); + + uint256 x_2 = vm.randomUint(); + // Change slot2 of symbolic contract. + _storeUInt256(address(csc_1), slot2, x_2); + value2 = vm.load(address(csc_1), bytes32(slot2)); + bytes32 value6 = vm.load(address(csc_3), bytes32(slot2)); + // Make sure value for symbolic contract value is different than 1st contract copied. + assert(value2 != value4); + // Make sure value for symbolic contract value is different than 2nd contract copied. + assert(value2 != value6); + assertEq(value4, value6); + } +} diff --git a/testdata/default/cheats/MockFunction.t.sol b/testdata/default/cheats/MockFunction.t.sol new file mode 100644 index 000000000..9cf1004ca --- /dev/null +++ b/testdata/default/cheats/MockFunction.t.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract MockFunctionContract { + uint256 public a; + + function mocked_function() public { + a = 321; + } + + function mocked_args_function(uint256 x) public { + a = 321 + x; + } +} + +contract ModelMockFunctionContract { + uint256 public a; + + function mocked_function() public { + a = 123; + } + + function mocked_args_function(uint256 x) public { + a = 123 + x; + } +} + +contract MockFunctionTest is DSTest { + MockFunctionContract my_contract; + ModelMockFunctionContract model_contract; + Vm vm = Vm(HEVM_ADDRESS); + + function setUp() public { + my_contract = new MockFunctionContract(); + model_contract = new ModelMockFunctionContract(); + } + + function test_mock_function() public { + vm.mockFunction( + address(my_contract), + address(model_contract), + abi.encodeWithSelector(MockFunctionContract.mocked_function.selector) + ); + my_contract.mocked_function(); + assertEq(my_contract.a(), 123); + } + + function test_mock_function_concrete_args() public { + vm.mockFunction( + address(my_contract), + address(model_contract), + abi.encodeWithSelector(MockFunctionContract.mocked_args_function.selector, 456) + ); + my_contract.mocked_args_function(456); + assertEq(my_contract.a(), 123 + 456); + my_contract.mocked_args_function(567); + assertEq(my_contract.a(), 321 + 567); + } + + function test_mock_function_all_args() public { + vm.mockFunction( + address(my_contract), + address(model_contract), + abi.encodeWithSelector(MockFunctionContract.mocked_args_function.selector) + ); + my_contract.mocked_args_function(678); + assertEq(my_contract.a(), 123 + 678); + my_contract.mocked_args_function(789); + assertEq(my_contract.a(), 123 + 789); + } +} From 32022238364879c050e91f5ad6de587dafa331bd Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 12 Sep 2024 07:34:28 +0200 Subject: [PATCH 156/184] chore: add more size optimizations (#8847) --- .gitignore | 1 - Cargo.toml | 14 ++++++++++---- crates/evm/traces/src/debug/sources.rs | 22 +++++++++------------- crates/fmt/src/buffer.rs | 8 ++++++-- crates/fmt/src/formatter.rs | 11 ++++++++++- crates/forge/bin/cmd/test/mod.rs | 2 +- 6 files changed, 36 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index 14e811f2d..19f666e45 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,3 @@ out/ out.json .idea .vscode -bloat* diff --git a/Cargo.toml b/Cargo.toml index b05398538..fbf9be9b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -121,11 +121,17 @@ axum.opt-level = 3 # keystores scrypt.opt-level = 3 -# Override packages which aren't perf-sensitive for faster compilation speed. +# Override packages which aren't perf-sensitive for faster compilation speed and smaller binary size. [profile.release.package] -mdbook.opt-level = 1 -protobuf.opt-level = 1 -trezor-client.opt-level = 1 +alloy-sol-macro-expander.opt-level = "z" +html5ever.opt-level = "z" +mdbook.opt-level = "z" +prettyplease.opt-level = "z" +protobuf.opt-level = "z" +pulldown-cmark.opt-level = "z" +syn-solidity.opt-level = "z" +syn.opt-level = "z" +trezor-client.opt-level = "z" [workspace.dependencies] anvil = { path = "crates/anvil" } diff --git a/crates/evm/traces/src/debug/sources.rs b/crates/evm/traces/src/debug/sources.rs index feb870381..5087a06f3 100644 --- a/crates/evm/traces/src/debug/sources.rs +++ b/crates/evm/traces/src/debug/sources.rs @@ -124,7 +124,7 @@ impl ContractSources { /// Collects the contract sources and artifacts from the project compile output. pub fn from_project_output( output: &ProjectCompileOutput, - root: impl AsRef, + root: &Path, libraries: Option<&Libraries>, ) -> Result { let mut sources = Self::default(); @@ -135,13 +135,12 @@ impl ContractSources { pub fn insert( &mut self, output: &ProjectCompileOutput, - root: impl AsRef, + root: &Path, libraries: Option<&Libraries>, ) -> Result<()> where C::Language: Into, { - let root = root.as_ref(); let link_data = libraries.map(|libraries| { let linker = Linker::new(root, output.artifact_ids().collect()); (linker, libraries) @@ -152,16 +151,16 @@ impl ContractSources { .collect::>() .par_iter() .map(|(id, artifact)| { - let mut artifacts = Vec::new(); + let mut new_artifact = None; if let Some(file_id) = artifact.id { let artifact = if let Some((linker, libraries)) = link_data.as_ref() { - linker.link(id, libraries)?.into_contract_bytecode() + linker.link(id, libraries)? } else { - (*artifact).clone().into_contract_bytecode() + artifact.get_contract_bytecode() }; let bytecode = compact_to_contract(artifact.into_contract_bytecode())?; - artifacts.push(( + new_artifact = Some(( id.name.clone(), ArtifactData::new(bytecode, id.build_id.clone(), file_id)?, )); @@ -169,14 +168,11 @@ impl ContractSources { warn!(id = id.identifier(), "source not found"); }; - Ok(artifacts) + Ok(new_artifact) }) - .collect::>>()? - .into_iter() - .flatten() - .collect(); + .collect::>>()?; - for (name, artifact) in artifacts { + for (name, artifact) in artifacts.into_iter().flatten() { self.artifacts_by_name.entry(name).or_default().push(artifact); } diff --git a/crates/fmt/src/buffer.rs b/crates/fmt/src/buffer.rs index 8d62a70f9..b09e9e620 100644 --- a/crates/fmt/src/buffer.rs +++ b/crates/fmt/src/buffer.rs @@ -165,7 +165,11 @@ impl FormatBuffer { /// Write a raw string to the buffer. This will ignore indents and remove the indents of the /// written string to match the current base indent of this buffer if it is a temp buffer pub fn write_raw(&mut self, s: impl AsRef) -> std::fmt::Result { - let mut lines = s.as_ref().lines().peekable(); + self._write_raw(s.as_ref()) + } + + fn _write_raw(&mut self, s: &str) -> std::fmt::Result { + let mut lines = s.lines().peekable(); let mut comment_state = self.state.comment_state(); while let Some(line) = lines.next() { // remove the whitespace that covered by the base indent length (this is normally the @@ -187,7 +191,7 @@ impl FormatBuffer { self.last_char = trimmed_line.chars().next_back(); self.state = WriteState::WriteTokens(comment_state); } - if lines.peek().is_some() || s.as_ref().ends_with('\n') { + if lines.peek().is_some() || s.ends_with('\n') { if self.restrict_to_single_line { return Err(std::fmt::Error) } diff --git a/crates/fmt/src/formatter.rs b/crates/fmt/src/formatter.rs index a44aa697c..2ae083e5d 100644 --- a/crates/fmt/src/formatter.rs +++ b/crates/fmt/src/formatter.rs @@ -370,7 +370,16 @@ impl<'a, W: Write> Formatter<'a, W> { &mut self, byte_offset: usize, next_byte_offset: Option, - fun: impl FnMut(&mut Self) -> Result<()>, + mut fun: impl FnMut(&mut Self) -> Result<()>, + ) -> Result { + self.chunked_mono(byte_offset, next_byte_offset, &mut fun) + } + + fn chunked_mono( + &mut self, + byte_offset: usize, + next_byte_offset: Option, + fun: &mut dyn FnMut(&mut Self) -> Result<()>, ) -> Result { let postfixes_before = self.comments.remove_postfixes_before(byte_offset); let prefixes = self.comments.remove_prefixes_before(byte_offset); diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 1cc647b0c..669413d78 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -531,7 +531,7 @@ impl TestArgs { if self.decode_internal.is_some() { let sources = - ContractSources::from_project_output(output, &config.root, Some(&libraries))?; + ContractSources::from_project_output(output, &config.root.0, Some(&libraries))?; builder = builder.with_debug_identifier(DebugTraceIdentifier::new(sources)); } let mut decoder = builder.build(); From 0c79ab590b6a9d3dcb7e0e407811c76339009eca Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 12 Sep 2024 18:45:58 +0300 Subject: [PATCH 157/184] fix(test): increment nonce for calls too when isolate (#8854) --- crates/evm/evm/src/inspectors/stack.rs | 30 +------------------------- testdata/default/cheats/Nonce.t.sol | 30 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 29 deletions(-) create mode 100644 testdata/default/cheats/Nonce.t.sol diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 3df8dc8f0..30271910b 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -257,15 +257,8 @@ pub struct InspectorData { /// non-isolated mode. For descriptions and workarounds for those changes see: #[derive(Debug, Clone)] pub struct InnerContextData { - /// The sender of the inner EVM context. - /// It is also an origin of the transaction that created the inner EVM context. - sender: Address, - /// Nonce of the sender before invocation of the inner EVM context. - original_sender_nonce: u64, /// Origin of the transaction in the outer EVM context. original_origin: Address, - /// Whether the inner context was created by a CREATE transaction. - is_create: bool, } /// An inspector that calls multiple inspectors in sequence. @@ -490,14 +483,6 @@ impl<'a> InspectorStackRefMut<'a> { fn adjust_evm_data_for_inner_context(&mut self, ecx: &mut EvmContext) { let inner_context_data = self.inner_context_data.as_ref().expect("should be called in inner context"); - let sender_acc = ecx - .journaled_state - .state - .get_mut(&inner_context_data.sender) - .expect("failed to load sender"); - if !inner_context_data.is_create { - sender_acc.info.nonce = inner_context_data.original_sender_nonce; - } ecx.env.tx.caller = inner_context_data.original_origin; } @@ -541,13 +526,6 @@ impl<'a> InspectorStackRefMut<'a> { ecx.db.commit(ecx.journaled_state.state.clone()); - let nonce = ecx - .journaled_state - .load_account(caller, &mut ecx.db) - .expect("failed to load caller") - .info - .nonce; - let cached_env = ecx.env.clone(); ecx.env.block.basefee = U256::ZERO; @@ -555,7 +533,6 @@ impl<'a> InspectorStackRefMut<'a> { ecx.env.tx.transact_to = transact_to; ecx.env.tx.data = input; ecx.env.tx.value = value; - ecx.env.tx.nonce = Some(nonce); // Add 21000 to the gas limit to account for the base cost of transaction. ecx.env.tx.gas_limit = gas_limit + 21000; // If we haven't disabled gas limit checks, ensure that transaction gas limit will not @@ -566,12 +543,7 @@ impl<'a> InspectorStackRefMut<'a> { } ecx.env.tx.gas_price = U256::ZERO; - self.inner_context_data = Some(InnerContextData { - sender: ecx.env.tx.caller, - original_origin: cached_env.tx.caller, - original_sender_nonce: nonce, - is_create: matches!(transact_to, TxKind::Create), - }); + self.inner_context_data = Some(InnerContextData { original_origin: cached_env.tx.caller }); self.in_inner_context = true; let env = EnvWithHandlerCfg::new_with_spec_id(ecx.env.clone(), ecx.spec_id()); diff --git a/testdata/default/cheats/Nonce.t.sol b/testdata/default/cheats/Nonce.t.sol new file mode 100644 index 000000000..5dd8b0c6a --- /dev/null +++ b/testdata/default/cheats/Nonce.t.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract Counter { + uint256 public count; + + function increment() public { + count += 1; + } +} + +contract NonceIsolatedTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function testIncrementNonce() public { + address bob = address(14); + vm.startPrank(bob); + Counter counter = new Counter(); + assertEq(vm.getNonce(bob), 1); + counter.increment(); + assertEq(vm.getNonce(bob), 2); + new Counter(); + assertEq(vm.getNonce(bob), 3); + counter.increment(); + assertEq(vm.getNonce(bob), 4); + } +} From fb9dbba4ed81321557a68053f845ca6ea46b6ba1 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 12 Sep 2024 20:21:17 +0400 Subject: [PATCH 158/184] Revert "fix(test): increment nonce for calls too when isolate" (#8855) Revert "fix(test): increment nonce for calls too when isolate (#8854)" This reverts commit 0c79ab590b6a9d3dcb7e0e407811c76339009eca. --- crates/evm/evm/src/inspectors/stack.rs | 30 +++++++++++++++++++++++++- testdata/default/cheats/Nonce.t.sol | 30 -------------------------- 2 files changed, 29 insertions(+), 31 deletions(-) delete mode 100644 testdata/default/cheats/Nonce.t.sol diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 30271910b..3df8dc8f0 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -257,8 +257,15 @@ pub struct InspectorData { /// non-isolated mode. For descriptions and workarounds for those changes see: #[derive(Debug, Clone)] pub struct InnerContextData { + /// The sender of the inner EVM context. + /// It is also an origin of the transaction that created the inner EVM context. + sender: Address, + /// Nonce of the sender before invocation of the inner EVM context. + original_sender_nonce: u64, /// Origin of the transaction in the outer EVM context. original_origin: Address, + /// Whether the inner context was created by a CREATE transaction. + is_create: bool, } /// An inspector that calls multiple inspectors in sequence. @@ -483,6 +490,14 @@ impl<'a> InspectorStackRefMut<'a> { fn adjust_evm_data_for_inner_context(&mut self, ecx: &mut EvmContext) { let inner_context_data = self.inner_context_data.as_ref().expect("should be called in inner context"); + let sender_acc = ecx + .journaled_state + .state + .get_mut(&inner_context_data.sender) + .expect("failed to load sender"); + if !inner_context_data.is_create { + sender_acc.info.nonce = inner_context_data.original_sender_nonce; + } ecx.env.tx.caller = inner_context_data.original_origin; } @@ -526,6 +541,13 @@ impl<'a> InspectorStackRefMut<'a> { ecx.db.commit(ecx.journaled_state.state.clone()); + let nonce = ecx + .journaled_state + .load_account(caller, &mut ecx.db) + .expect("failed to load caller") + .info + .nonce; + let cached_env = ecx.env.clone(); ecx.env.block.basefee = U256::ZERO; @@ -533,6 +555,7 @@ impl<'a> InspectorStackRefMut<'a> { ecx.env.tx.transact_to = transact_to; ecx.env.tx.data = input; ecx.env.tx.value = value; + ecx.env.tx.nonce = Some(nonce); // Add 21000 to the gas limit to account for the base cost of transaction. ecx.env.tx.gas_limit = gas_limit + 21000; // If we haven't disabled gas limit checks, ensure that transaction gas limit will not @@ -543,7 +566,12 @@ impl<'a> InspectorStackRefMut<'a> { } ecx.env.tx.gas_price = U256::ZERO; - self.inner_context_data = Some(InnerContextData { original_origin: cached_env.tx.caller }); + self.inner_context_data = Some(InnerContextData { + sender: ecx.env.tx.caller, + original_origin: cached_env.tx.caller, + original_sender_nonce: nonce, + is_create: matches!(transact_to, TxKind::Create), + }); self.in_inner_context = true; let env = EnvWithHandlerCfg::new_with_spec_id(ecx.env.clone(), ecx.spec_id()); diff --git a/testdata/default/cheats/Nonce.t.sol b/testdata/default/cheats/Nonce.t.sol deleted file mode 100644 index 5dd8b0c6a..000000000 --- a/testdata/default/cheats/Nonce.t.sol +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; - -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract Counter { - uint256 public count; - - function increment() public { - count += 1; - } -} - -contract NonceIsolatedTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - - function testIncrementNonce() public { - address bob = address(14); - vm.startPrank(bob); - Counter counter = new Counter(); - assertEq(vm.getNonce(bob), 1); - counter.increment(); - assertEq(vm.getNonce(bob), 2); - new Counter(); - assertEq(vm.getNonce(bob), 3); - counter.increment(); - assertEq(vm.getNonce(bob), 4); - } -} From c6d342def10db104500e3295b1c2e5582491bd61 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 12 Sep 2024 21:30:57 +0200 Subject: [PATCH 159/184] test: use alchemy for arbitrum URLs (#8859) --- crates/anvil/tests/it/fork.rs | 8 ++++---- crates/cast/src/lib.rs | 8 +++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index d8465ec5c..ad2ed1e04 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -1156,7 +1156,7 @@ async fn test_arbitrum_fork_dev_balance() { let (api, handle) = spawn( fork_config() .with_fork_block_number(None::) - .with_eth_rpc_url(Some("https://arb1.arbitrum.io/rpc".to_string())), + .with_eth_rpc_url(Some(next_rpc_endpoint(NamedChain::Arbitrum))), ) .await; @@ -1174,7 +1174,7 @@ async fn test_arbitrum_fork_block_number() { let (_, handle) = spawn( fork_config() .with_fork_block_number(None::) - .with_eth_rpc_url(Some("https://arb1.arbitrum.io/rpc".to_string())), + .with_eth_rpc_url(Some(next_rpc_endpoint(NamedChain::Arbitrum))), ) .await; let provider = handle.http_provider(); @@ -1186,7 +1186,7 @@ async fn test_arbitrum_fork_block_number() { let (api, _) = spawn( fork_config() .with_fork_block_number(Some(initial_block_number)) - .with_eth_rpc_url(Some("https://arb1.arbitrum.io/rpc".to_string())), + .with_eth_rpc_url(Some(next_rpc_endpoint(NamedChain::Arbitrum))), ) .await; let block_number = api.block_number().unwrap().to::(); @@ -1212,7 +1212,7 @@ async fn test_arbitrum_fork_block_number() { // reset fork to different block number and compare with block returned by `eth_blockNumber` api.anvil_reset(Some(Forking { - json_rpc_url: Some("https://arb1.arbitrum.io/rpc".to_string()), + json_rpc_url: Some(next_rpc_endpoint(NamedChain::Arbitrum)), block_number: Some(initial_block_number - 2), })) .await diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 73ab5b28b..8132f8e4f 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -1648,12 +1648,10 @@ impl SimpleCast { /// ``` pub fn abi_encode(sig: &str, args: &[impl AsRef]) -> Result { let func = get_func(sig)?; - let calldata = match encode_function_args(&func, args) { - Ok(res) => hex::encode(res), + match encode_function_args(&func, args) { + Ok(res) => Ok(hex::encode_prefixed(&res[4..])), Err(e) => eyre::bail!("Could not ABI encode the function and arguments. Did you pass in the right types?\nError\n{}", e), - }; - let encoded = &calldata[8..]; - Ok(format!("0x{encoded}")) + } } /// Performs packed ABI encoding based off of the function signature or tuple. From 2cdbfaca634b284084d0f86357623aef7a0d2ce3 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 12 Sep 2024 22:57:00 +0200 Subject: [PATCH 160/184] feat: add support for test skip reasons (#8858) --- crates/cheatcodes/assets/cheatcodes.json | 24 ++- crates/cheatcodes/spec/src/vm.rs | 6 +- crates/cheatcodes/src/test.rs | 13 +- crates/evm/core/src/constants.rs | 2 +- crates/evm/core/src/decode.rs | 57 +++++- crates/evm/evm/src/executors/fuzz/mod.rs | 45 +++-- .../evm/evm/src/executors/invariant/error.rs | 6 +- crates/evm/evm/src/executors/mod.rs | 20 +- crates/evm/fuzz/src/error.rs | 9 +- crates/evm/fuzz/src/lib.rs | 2 + crates/forge/src/result.rs | 69 ++++--- crates/forge/src/runner.rs | 12 +- crates/forge/tests/cli/script.rs | 13 +- crates/forge/tests/cli/test_cmd.rs | 182 ++++++++++++------ crates/forge/tests/fixtures/ScriptVerify.sol | 2 +- crates/forge/tests/it/fuzz.rs | 2 +- crates/forge/tests/it/invariant.rs | 2 +- crates/test-utils/src/util.rs | 4 +- testdata/cheats/Vm.sol | 1 + 19 files changed, 308 insertions(+), 163 deletions(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index da501a11e..567167e21 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -8113,8 +8113,8 @@ }, { "func": { - "id": "skip", - "description": "Marks a test as skipped. Must be called at the top of the test.", + "id": "skip_0", + "description": "Marks a test as skipped. Must be called at the top level of a test.", "declaration": "function skip(bool skipTest) external;", "visibility": "external", "mutability": "", @@ -8131,6 +8131,26 @@ "status": "stable", "safety": "unsafe" }, + { + "func": { + "id": "skip_1", + "description": "Marks a test as skipped with a reason. Must be called at the top level of a test.", + "declaration": "function skip(bool skipTest, string calldata reason) external;", + "visibility": "external", + "mutability": "", + "signature": "skip(bool,string)", + "selector": "0xc42a80a7", + "selectorBytes": [ + 196, + 42, + 128, + 167 + ] + }, + "group": "testing", + "status": "stable", + "safety": "unsafe" + }, { "func": { "id": "sleep", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index d0a921485..49aeaa211 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -843,10 +843,14 @@ interface Vm { #[cheatcode(group = Testing, safety = Unsafe)] function expectSafeMemoryCall(uint64 min, uint64 max) external; - /// Marks a test as skipped. Must be called at the top of the test. + /// Marks a test as skipped. Must be called at the top level of a test. #[cheatcode(group = Testing, safety = Unsafe)] function skip(bool skipTest) external; + /// Marks a test as skipped with a reason. Must be called at the top level of a test. + #[cheatcode(group = Testing, safety = Unsafe)] + function skip(bool skipTest, string calldata reason) external; + /// Asserts that the given condition is true. #[cheatcode(group = Testing, safety = Safe)] function assertTrue(bool condition) external pure; diff --git a/crates/cheatcodes/src/test.rs b/crates/cheatcodes/src/test.rs index 0945f37a8..cc91dba45 100644 --- a/crates/cheatcodes/src/test.rs +++ b/crates/cheatcodes/src/test.rs @@ -70,14 +70,21 @@ impl Cheatcode for sleepCall { } } -impl Cheatcode for skipCall { +impl Cheatcode for skip_0Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { skipTest } = *self; - if skipTest { + skip_1Call { skipTest, reason: String::new() }.apply_stateful(ccx) + } +} + +impl Cheatcode for skip_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { skipTest, reason } = self; + if *skipTest { // Skip should not work if called deeper than at test level. // Since we're not returning the magic skip bytes, this will cause a test failure. ensure!(ccx.ecx.journaled_state.depth() <= 1, "`skip` can only be used at test level"); - Err(MAGIC_SKIP.into()) + Err([MAGIC_SKIP, reason.as_bytes()].concat().into()) } else { Ok(Default::default()) } diff --git a/crates/evm/core/src/constants.rs b/crates/evm/core/src/constants.rs index 5e49a01e7..70c7441d2 100644 --- a/crates/evm/core/src/constants.rs +++ b/crates/evm/core/src/constants.rs @@ -34,7 +34,7 @@ pub const TEST_CONTRACT_ADDRESS: Address = address!("b4c79daB8f259C7Aee6E5b2Aa72 /// Magic return value returned by the `assume` cheatcode. pub const MAGIC_ASSUME: &[u8] = b"FOUNDRY::ASSUME"; -/// Magic return value returned by the `skip` cheatcode. +/// Magic return value returned by the `skip` cheatcode. Optionally appended with a reason. pub const MAGIC_SKIP: &[u8] = b"FOUNDRY::SKIP"; /// The address that deploys the default CREATE2 deployer contract. diff --git a/crates/evm/core/src/decode.rs b/crates/evm/core/src/decode.rs index 29f448bce..b777ec453 100644 --- a/crates/evm/core/src/decode.rs +++ b/crates/evm/core/src/decode.rs @@ -9,7 +9,39 @@ use foundry_common::SELECTOR_LEN; use itertools::Itertools; use revm::interpreter::InstructionResult; use rustc_hash::FxHashMap; -use std::sync::OnceLock; +use std::{fmt, sync::OnceLock}; + +/// A skip reason. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SkipReason(pub Option); + +impl SkipReason { + /// Decodes a skip reason, if any. + pub fn decode(raw_result: &[u8]) -> Option { + raw_result.strip_prefix(crate::constants::MAGIC_SKIP).map(|reason| { + let reason = String::from_utf8_lossy(reason).into_owned(); + Self((!reason.is_empty()).then_some(reason)) + }) + } + + /// Decodes a skip reason from a string that was obtained by formatting `Self`. + /// + /// This is a hack to support re-decoding a skip reason in proptest. + pub fn decode_self(s: &str) -> Option { + s.strip_prefix("skipped").map(|rest| Self(rest.strip_prefix(": ").map(ToString::to_string))) + } +} + +impl fmt::Display for SkipReason { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("skipped")?; + if let Some(reason) = &self.0 { + f.write_str(": ")?; + f.write_str(reason)?; + } + Ok(()) + } +} /// Decode a set of logs, only returning logs from DSTest logging events and Hardhat's `console.log` pub fn decode_console_logs(logs: &[Log]) -> Vec { @@ -120,9 +152,8 @@ impl RevertDecoder { }; } - if err == crate::constants::MAGIC_SKIP { - // Also used in forge fuzz runner - return Some("SKIPPED".to_string()); + if let Some(reason) = SkipReason::decode(err) { + return Some(reason.to_string()); } // Solidity's `Error(string)` or `Panic(uint256)` @@ -177,11 +208,17 @@ impl RevertDecoder { } // Generic custom error. - Some(format!( - "custom error {}:{}", - hex::encode(selector), - std::str::from_utf8(data).map_or_else(|_| trimmed_hex(data), String::from) - )) + Some({ + let mut s = format!("custom error {}", hex::encode_prefixed(selector)); + if !data.is_empty() { + s.push_str(": "); + match std::str::from_utf8(data) { + Ok(data) => s.push_str(data), + Err(_) => s.push_str(&trimmed_hex(data)), + } + } + s + }) } } @@ -194,7 +231,7 @@ fn trimmed_hex(s: &[u8]) -> String { "{}…{} ({} bytes)", &hex::encode(&s[..n / 2]), &hex::encode(&s[s.len() - n / 2..]), - s.len() + s.len(), ) } } diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index 7fa5498bf..ee5a03dca 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -5,7 +5,10 @@ use alloy_primitives::{Address, Bytes, Log, U256}; use eyre::Result; use foundry_common::evm::Breakpoints; use foundry_config::FuzzConfig; -use foundry_evm_core::{constants::MAGIC_ASSUME, decode::RevertDecoder}; +use foundry_evm_core::{ + constants::MAGIC_ASSUME, + decode::{RevertDecoder, SkipReason}, +}; use foundry_evm_coverage::HitMaps; use foundry_evm_fuzz::{ strategies::{fuzz_calldata, fuzz_calldata_from_state, EvmFuzzState}, @@ -131,9 +134,8 @@ impl FuzzedExecutor { }) => { // We cannot use the calldata returned by the test runner in `TestError::Fail`, // since that input represents the last run case, which may not correspond with - // our failure - when a fuzz case fails, proptest will try - // to run at least one more case to find a minimal failure - // case. + // our failure - when a fuzz case fails, proptest will try to run at least one + // more case to find a minimal failure case. let reason = rd.maybe_decode(&outcome.1.result, Some(status)); execution_data.borrow_mut().logs.extend(outcome.1.logs.clone()); execution_data.borrow_mut().counterexample = outcome; @@ -157,6 +159,7 @@ impl FuzzedExecutor { first_case: fuzz_result.first_case.unwrap_or_default(), gas_by_case: fuzz_result.gas_by_case, success: run_result.is_ok(), + skipped: false, reason: None, counterexample: None, logs: fuzz_result.logs, @@ -168,20 +171,22 @@ impl FuzzedExecutor { }; match run_result { - // Currently the only operation that can trigger proptest global rejects is the - // `vm.assume` cheatcode, thus we surface this info to the user when the fuzz test - // aborts due to too many global rejects, making the error message more actionable. - Err(TestError::Abort(reason)) if reason.message() == "Too many global rejects" => { - result.reason = Some( - FuzzError::TooManyRejects(self.runner.config().max_global_rejects).to_string(), - ); - } + Ok(()) => {} Err(TestError::Abort(reason)) => { - result.reason = Some(reason.to_string()); + let msg = reason.message(); + // Currently the only operation that can trigger proptest global rejects is the + // `vm.assume` cheatcode, thus we surface this info to the user when the fuzz test + // aborts due to too many global rejects, making the error message more actionable. + result.reason = if msg == "Too many global rejects" { + let error = FuzzError::TooManyRejects(self.runner.config().max_global_rejects); + Some(error.to_string()) + } else { + Some(msg.to_string()) + }; } Err(TestError::Fail(reason, _)) => { let reason = reason.to_string(); - result.reason = if reason.is_empty() { None } else { Some(reason) }; + result.reason = (!reason.is_empty()).then_some(reason); let args = if let Some(data) = calldata.get(4..) { func.abi_decode_input(data, false).unwrap_or_default() @@ -193,7 +198,13 @@ impl FuzzedExecutor { BaseCounterExample::from_fuzz_call(calldata, args, call.traces), )); } - _ => {} + } + + if let Some(reason) = &result.reason { + if let Some(reason) = SkipReason::decode_self(reason) { + result.skipped = true; + result.reason = reason.0; + } } state.log_stats(); @@ -212,9 +223,9 @@ impl FuzzedExecutor { let mut call = self .executor .call_raw(self.sender, address, calldata.clone(), U256::ZERO) - .map_err(|_| TestCaseError::fail(FuzzError::FailedContractCall))?; + .map_err(|e| TestCaseError::fail(e.to_string()))?; - // When the `assume` cheatcode is called it returns a special string + // Handle `vm.assume`. if call.result.as_ref() == MAGIC_ASSUME { return Err(TestCaseError::reject(FuzzError::AssumeReject)) } diff --git a/crates/evm/evm/src/executors/invariant/error.rs b/crates/evm/evm/src/executors/invariant/error.rs index b8cff9b1d..f5eef18ed 100644 --- a/crates/evm/evm/src/executors/invariant/error.rs +++ b/crates/evm/evm/src/executors/invariant/error.rs @@ -40,9 +40,9 @@ impl InvariantFuzzError { Self::BrokenInvariant(case_data) | Self::Revert(case_data) => { (!case_data.revert_reason.is_empty()).then(|| case_data.revert_reason.clone()) } - Self::MaxAssumeRejects(allowed) => Some(format!( - "The `vm.assume` cheatcode rejected too many inputs ({allowed} allowed)" - )), + Self::MaxAssumeRejects(allowed) => { + Some(format!("`vm.assume` rejected too many inputs ({allowed} allowed)")) + } } } } diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 44bc33e37..655418fad 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -19,7 +19,7 @@ use foundry_evm_core::{ CALLER, CHEATCODE_ADDRESS, CHEATCODE_CONTRACT_HASH, DEFAULT_CREATE2_DEPLOYER, DEFAULT_CREATE2_DEPLOYER_CODE, DEFAULT_CREATE2_DEPLOYER_DEPLOYER, }, - decode::RevertDecoder, + decode::{RevertDecoder, SkipReason}, utils::StateChangeset, }; use foundry_evm_coverage::HitMaps; @@ -649,15 +649,15 @@ impl std::ops::DerefMut for ExecutionErr { #[derive(Debug, thiserror::Error)] pub enum EvmError { - /// Error which occurred during execution of a transaction + /// Error which occurred during execution of a transaction. #[error(transparent)] Execution(#[from] Box), - /// Error which occurred during ABI encoding/decoding + /// Error which occurred during ABI encoding/decoding. #[error(transparent)] - AbiError(#[from] alloy_dyn_abi::Error), - /// Error caused which occurred due to calling the skip() cheatcode. - #[error("Skipped")] - SkipError, + Abi(#[from] alloy_dyn_abi::Error), + /// Error caused which occurred due to calling the `skip` cheatcode. + #[error("{_0}")] + Skip(SkipReason), /// Any other error. #[error(transparent)] Eyre(#[from] eyre::Error), @@ -671,7 +671,7 @@ impl From for EvmError { impl From for EvmError { fn from(err: alloy_sol_types::Error) -> Self { - Self::AbiError(err.into()) + Self::Abi(err.into()) } } @@ -769,8 +769,8 @@ impl Default for RawCallResult { impl RawCallResult { /// Converts the result of the call into an `EvmError`. pub fn into_evm_error(self, rd: Option<&RevertDecoder>) -> EvmError { - if self.result[..] == crate::constants::MAGIC_SKIP[..] { - return EvmError::SkipError; + if let Some(reason) = SkipReason::decode(&self.result) { + return EvmError::Skip(reason); } let reason = rd.unwrap_or_default().decode(&self.result, Some(self.exit_reason)); EvmError::Execution(Box::new(self.into_execution_error(reason))) diff --git a/crates/evm/fuzz/src/error.rs b/crates/evm/fuzz/src/error.rs index 1371f4969..8fc3c1897 100644 --- a/crates/evm/fuzz/src/error.rs +++ b/crates/evm/fuzz/src/error.rs @@ -1,16 +1,13 @@ -//! errors related to fuzz tests +//! Errors related to fuzz tests. + use proptest::test_runner::Reason; /// Possible errors when running fuzz tests #[derive(Debug, thiserror::Error)] pub enum FuzzError { - #[error("Couldn't call unknown contract")] - UnknownContract, - #[error("Failed contract call")] - FailedContractCall, #[error("`vm.assume` reject")] AssumeReject, - #[error("The `vm.assume` cheatcode rejected too many inputs ({0} allowed)")] + #[error("`vm.assume` rejected too many inputs ({0} allowed)")] TooManyRejects(u32), } diff --git a/crates/evm/fuzz/src/lib.rs b/crates/evm/fuzz/src/lib.rs index 99232443b..fe1cb38d0 100644 --- a/crates/evm/fuzz/src/lib.rs +++ b/crates/evm/fuzz/src/lib.rs @@ -151,6 +151,8 @@ pub struct FuzzTestResult { /// properly, or that there was a revert and that the test was expected to fail /// (prefixed with `testFail`) pub success: bool, + /// Whether the test case was skipped. `reason` will contain the skip reason, if any. + pub skipped: bool, /// If there was a revert, this field will be populated. Note that the test can /// still be successful (i.e self.success == true) when it's expected to fail. diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index aa88bc401..be445d491 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -9,6 +9,7 @@ use eyre::Report; use foundry_common::{evm::Breakpoints, get_contract_name, get_file_name, shell}; use foundry_evm::{ coverage::HitMaps, + decode::SkipReason, executors::{EvmError, RawCallResult}, fuzz::{CounterExample, FuzzCase, FuzzFixtures, FuzzTestResult}, traces::{CallTraceArena, CallTraceDecoder, TraceKind, Traces}, @@ -53,17 +54,17 @@ impl TestOutcome { /// Returns an iterator over all individual succeeding tests and their names. pub fn successes(&self) -> impl Iterator { - self.tests().filter(|(_, t)| t.status == TestStatus::Success) + self.tests().filter(|(_, t)| t.status.is_success()) } /// Returns an iterator over all individual skipped tests and their names. pub fn skips(&self) -> impl Iterator { - self.tests().filter(|(_, t)| t.status == TestStatus::Skipped) + self.tests().filter(|(_, t)| t.status.is_skipped()) } /// Returns an iterator over all individual failing tests and their names. pub fn failures(&self) -> impl Iterator { - self.tests().filter(|(_, t)| t.status == TestStatus::Failure) + self.tests().filter(|(_, t)| t.status.is_failure()) } /// Returns an iterator over all individual tests and their names. @@ -221,17 +222,17 @@ impl SuiteResult { /// Returns an iterator over all individual succeeding tests and their names. pub fn successes(&self) -> impl Iterator { - self.tests().filter(|(_, t)| t.status == TestStatus::Success) + self.tests().filter(|(_, t)| t.status.is_success()) } /// Returns an iterator over all individual skipped tests and their names. pub fn skips(&self) -> impl Iterator { - self.tests().filter(|(_, t)| t.status == TestStatus::Skipped) + self.tests().filter(|(_, t)| t.status.is_skipped()) } /// Returns an iterator over all individual failing tests and their names. pub fn failures(&self) -> impl Iterator { - self.tests().filter(|(_, t)| t.status == TestStatus::Failure) + self.tests().filter(|(_, t)| t.status.is_failure()) } /// Returns the number of tests that passed. @@ -395,29 +396,39 @@ impl fmt::Display for TestResult { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.status { TestStatus::Success => "[PASS]".green().fmt(f), - TestStatus::Skipped => "[SKIP]".yellow().fmt(f), + TestStatus::Skipped => { + let mut s = String::from("[SKIP"); + if let Some(reason) = &self.reason { + write!(s, ": {reason}").unwrap(); + } + s.push(']'); + s.yellow().fmt(f) + } TestStatus::Failure => { - let mut s = String::from("[FAIL. Reason: "); - - let reason = self.reason.as_deref().unwrap_or("assertion failed"); - s.push_str(reason); + let mut s = String::from("[FAIL"); + if self.reason.is_some() || self.counterexample.is_some() { + if let Some(reason) = &self.reason { + write!(s, ": {reason}").unwrap(); + } - if let Some(counterexample) = &self.counterexample { - match counterexample { - CounterExample::Single(ex) => { - write!(s, "; counterexample: {ex}]").unwrap(); - } - CounterExample::Sequence(sequence) => { - s.push_str("]\n\t[Sequence]\n"); - for ex in sequence { - writeln!(s, "\t\t{ex}").unwrap(); + if let Some(counterexample) = &self.counterexample { + match counterexample { + CounterExample::Single(ex) => { + write!(s, "; counterexample: {ex}]").unwrap(); + } + CounterExample::Sequence(sequence) => { + s.push_str("]\n\t[Sequence]\n"); + for ex in sequence { + writeln!(s, "\t\t{ex}").unwrap(); + } } } + } else { + s.push(']'); } } else { s.push(']'); } - s.red().fmt(f) } } @@ -455,8 +466,9 @@ impl TestResult { } /// Returns the skipped result for single test (used in skipped fuzz test too). - pub fn single_skip(mut self) -> Self { + pub fn single_skip(mut self, reason: SkipReason) -> Self { self.status = TestStatus::Skipped; + self.reason = reason.0; self } @@ -511,22 +523,27 @@ impl TestResult { self.traces.extend(result.traces.map(|traces| (TraceKind::Execution, traces))); self.merge_coverages(result.coverage); - self.status = match result.success { - true => TestStatus::Success, - false => TestStatus::Failure, + self.status = if result.skipped { + TestStatus::Skipped + } else if result.success { + TestStatus::Success + } else { + TestStatus::Failure }; self.reason = result.reason; self.counterexample = result.counterexample; self.duration = Duration::default(); self.gas_report_traces = result.gas_report_traces.into_iter().map(|t| vec![t]).collect(); self.breakpoints = result.breakpoints.unwrap_or_default(); + self } /// Returns the skipped result for invariant test. - pub fn invariant_skip(mut self) -> Self { + pub fn invariant_skip(mut self, reason: SkipReason) -> Self { self.kind = TestKind::Invariant { runs: 1, calls: 1, reverts: 1 }; self.status = TestStatus::Skipped; + self.reason = reason.0; self } diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 3cfbd76c9..02b8150df 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -443,7 +443,7 @@ impl<'a> ContractRunner<'a> { ) { Ok(res) => (res.raw, None), Err(EvmError::Execution(err)) => (err.raw, Some(err.reason)), - Err(EvmError::SkipError) => return test_result.single_skip(), + Err(EvmError::Skip(reason)) => return test_result.single_skip(reason), Err(err) => return test_result.single_fail(Some(err.to_string())), }; @@ -467,7 +467,7 @@ impl<'a> ContractRunner<'a> { let mut test_result = TestResult::new(setup); // First, run the test normally to see if it needs to be skipped. - if let Err(EvmError::SkipError) = self.executor.call( + if let Err(EvmError::Skip(reason)) = self.executor.call( self.sender, address, func, @@ -475,7 +475,7 @@ impl<'a> ContractRunner<'a> { U256::ZERO, Some(self.revert_decoder), ) { - return test_result.invariant_skip() + return test_result.invariant_skip(reason); }; let mut evm = InvariantExecutor::new( @@ -661,12 +661,6 @@ impl<'a> ContractRunner<'a> { self.revert_decoder, progress.as_ref(), ); - - // Check the last test result and skip the test - // if it's marked as so. - if let Some("SKIPPED") = result.reason.as_deref() { - return test_result.single_skip() - } test_result.fuzz_result(result) } diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 6ef99d9d1..ee803147a 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -215,20 +215,15 @@ contract DeployScript is Script { "--slow", "--broadcast", "--unlocked", + "--ignored-error-codes=2018", // `wasteGas` can be restricted to view ]) .assert_success() .stdout_eq(str![[r#" [COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] -Compiler run successful with warnings: -Warning (2018): Function state mutability can be restricted to view - [FILE]:7:5: - | -7 | function wasteGas(uint256 minGas) public { - | ^ (Relevant source part starts here and spans across multiple lines). - +Compiler run successful! Traces: - [81040] DeployScript::run() + [81034] DeployScript::run() ├─ [0] VM::startBroadcast() │ └─ ← [Return] ├─ [45299] → new GasWaster@[..] @@ -336,7 +331,7 @@ Warning (2018): Function state mutability can be restricted to view | ^ (Relevant source part starts here and spans across multiple lines). Traces: - [81040] DeployScript::run() + [81034] DeployScript::run() ├─ [0] VM::startBroadcast() │ └─ ← [Return] ├─ [45299] → new GasWaster@[..] diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 3e3335f0c..b6eec8903 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -529,7 +529,7 @@ Compiler run successful! Ran 1 test for test/Contract.t.sol:USDTCallingTest [PASS] test() ([GAS]) Traces: - [9537] USDTCallingTest::test() + [9516] USDTCallingTest::test() ├─ [0] VM::createSelectFork("[..]") │ └─ ← [Return] 0 ├─ [3110] 0xdAC17F958D2ee523a2206206994597C13D831ec7::name() [staticcall] @@ -573,7 +573,7 @@ contract CustomTypesTest is Test { Compiler run successful! Ran 2 tests for test/Contract.t.sol:CustomTypesTest -[FAIL. Reason: PoolNotInitialized()] testErr() ([GAS]) +[FAIL: PoolNotInitialized()] testErr() ([GAS]) Traces: [254] CustomTypesTest::testErr() └─ ← [Revert] PoolNotInitialized() @@ -590,7 +590,7 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 1 failed, 0 skipped (2 total tests) Failing tests: Encountered 1 failing test in test/Contract.t.sol:CustomTypesTest -[FAIL. Reason: PoolNotInitialized()] testErr() ([GAS]) +[FAIL: PoolNotInitialized()] testErr() ([GAS]) Encountered a total of 1 failing tests, 1 tests succeeded @@ -632,7 +632,8 @@ forgetest_init!(can_test_transient_storage_with_isolation, |prj, cmd| { prj.add_test( "Contract.t.sol", - r#"pragma solidity 0.8.24; + r#" +pragma solidity ^0.8.24; import {Test} from "forge-std/Test.sol"; contract TransientTester { @@ -687,7 +688,7 @@ forgetest_init!(can_disable_block_gas_limit, |prj, cmd| { prj.add_test( "Contract.t.sol", - &r#"pragma solidity 0.8.24; + &r#" import {Test} from "forge-std/Test.sol"; contract C is Test {} @@ -743,7 +744,7 @@ forgetest_init!(should_not_shrink_fuzz_failure, |prj, cmd| { prj.add_test( "CounterFuzz.t.sol", - r#"pragma solidity 0.8.24; + r#" import {Test} from "forge-std/Test.sol"; contract Counter { @@ -776,14 +777,14 @@ contract CounterTest is Test { Compiler run successful! Ran 1 test for test/CounterFuzz.t.sol:CounterTest -[FAIL. Reason: panic: arithmetic underflow or overflow (0x11); counterexample: calldata=0xa76d58f5ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff args=[115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]]] testAddOne(uint256) (runs: 61, [AVG_GAS]) +[FAIL: panic: arithmetic underflow or overflow (0x11); counterexample: calldata=0xa76d58f5ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff args=[115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]]] testAddOne(uint256) (runs: 61, [AVG_GAS]) Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) Failing tests: Encountered 1 failing test in test/CounterFuzz.t.sol:CounterTest -[FAIL. Reason: panic: arithmetic underflow or overflow (0x11); counterexample: calldata=0xa76d58f5ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff args=[115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]]] testAddOne(uint256) (runs: 61, [AVG_GAS]) +[FAIL: panic: arithmetic underflow or overflow (0x11); counterexample: calldata=0xa76d58f5ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff args=[115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]]] testAddOne(uint256) (runs: 61, [AVG_GAS]) Encountered a total of 1 failing tests, 0 tests succeeded @@ -794,7 +795,7 @@ forgetest_init!(should_exit_early_on_invariant_failure, |prj, cmd| { prj.wipe_contracts(); prj.add_test( "CounterInvariant.t.sol", - r#"pragma solidity 0.8.24; + r#" import {Test} from "forge-std/Test.sol"; contract Counter { @@ -827,14 +828,14 @@ contract CounterTest is Test { Compiler run successful! Ran 1 test for test/CounterInvariant.t.sol:CounterTest -[FAIL. Reason: failed to set up invariant testing environment: wrong count] invariant_early_exit() (runs: 0, calls: 0, reverts: 0) +[FAIL: failed to set up invariant testing environment: wrong count] invariant_early_exit() (runs: 0, calls: 0, reverts: 0) Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) Failing tests: Encountered 1 failing test in test/CounterInvariant.t.sol:CounterTest -[FAIL. Reason: failed to set up invariant testing environment: wrong count] invariant_early_exit() (runs: 0, calls: 0, reverts: 0) +[FAIL: failed to set up invariant testing environment: wrong count] invariant_early_exit() (runs: 0, calls: 0, reverts: 0) Encountered a total of 1 failing tests, 0 tests succeeded @@ -845,7 +846,7 @@ forgetest_init!(should_replay_failures_only, |prj, cmd| { prj.wipe_contracts(); prj.add_test( "ReplayFailures.t.sol", - r#"pragma solidity 0.8.24; + r#" import {Test} from "forge-std/Test.sol"; contract ReplayFailuresTest is Test { @@ -876,17 +877,17 @@ Compiler run successful! Ran 4 tests for test/ReplayFailures.t.sol:ReplayFailuresTest [PASS] testA() ([GAS]) -[FAIL. Reason: revert: testB failed] testB() ([GAS]) +[FAIL: revert: testB failed] testB() ([GAS]) [PASS] testC() ([GAS]) -[FAIL. Reason: revert: testD failed] testD() ([GAS]) +[FAIL: revert: testD failed] testD() ([GAS]) Suite result: FAILED. 2 passed; 2 failed; 0 skipped; [ELAPSED] Ran 1 test suite [ELAPSED]: 2 tests passed, 2 failed, 0 skipped (4 total tests) Failing tests: Encountered 2 failing tests in test/ReplayFailures.t.sol:ReplayFailuresTest -[FAIL. Reason: revert: testB failed] testB() ([GAS]) -[FAIL. Reason: revert: testD failed] testD() ([GAS]) +[FAIL: revert: testB failed] testB() ([GAS]) +[FAIL: revert: testD failed] testD() ([GAS]) Encountered a total of 2 failing tests, 2 tests succeeded @@ -900,16 +901,16 @@ Encountered a total of 2 failing tests, 2 tests succeeded No files changed, compilation skipped Ran 2 tests for test/ReplayFailures.t.sol:ReplayFailuresTest -[FAIL. Reason: revert: testB failed] testB() ([GAS]) -[FAIL. Reason: revert: testD failed] testD() ([GAS]) +[FAIL: revert: testB failed] testB() ([GAS]) +[FAIL: revert: testD failed] testD() ([GAS]) Suite result: FAILED. 0 passed; 2 failed; 0 skipped; [ELAPSED] Ran 1 test suite [ELAPSED]: 0 tests passed, 2 failed, 0 skipped (2 total tests) Failing tests: Encountered 2 failing tests in test/ReplayFailures.t.sol:ReplayFailuresTest -[FAIL. Reason: revert: testB failed] testB() ([GAS]) -[FAIL. Reason: revert: testD failed] testD() ([GAS]) +[FAIL: revert: testB failed] testB() ([GAS]) +[FAIL: revert: testD failed] testD() ([GAS]) Encountered a total of 2 failing tests, 0 tests succeeded @@ -924,6 +925,7 @@ forgetest_init!(should_show_precompile_labels, |prj, cmd| { "Contract.t.sol", r#" import {Test} from "forge-std/Test.sol"; + contract PrecompileLabelsTest is Test { function testPrecompileLabels() public { vm.deal(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D), 1 ether); @@ -1011,14 +1013,15 @@ forgetest_init!(should_show_logs_when_fuzz_test, |prj, cmd| { prj.add_test( "ContractFuzz.t.sol", - r#"pragma solidity 0.8.24; - import {Test, console2} from "forge-std/Test.sol"; - contract ContractFuzz is Test { - function testFuzzConsoleLog(uint256 x) public pure { - console2.log("inside fuzz test, x is:", x); - } + r#" +import {Test, console} from "forge-std/Test.sol"; + +contract ContractFuzz is Test { + function testFuzzConsoleLog(uint256 x) public pure { + console.log("inside fuzz test, x is:", x); } - "#, +} + "#, ) .unwrap(); cmd.args(["test", "-vv"]).assert_success().stdout_eq(str![[r#" @@ -1054,16 +1057,16 @@ forgetest_init!(should_show_logs_when_fuzz_test_inline_config, |prj, cmd| { prj.add_test( "ContractFuzz.t.sol", - r#"pragma solidity 0.8.24; - import {Test, console2} from "forge-std/Test.sol"; - contract ContractFuzz is Test { + r#" +import {Test, console} from "forge-std/Test.sol"; - /// forge-config: default.fuzz.show-logs = true - function testFuzzConsoleLog(uint256 x) public pure { - console2.log("inside fuzz test, x is:", x); - } +contract ContractFuzz is Test { + /// forge-config: default.fuzz.show-logs = true + function testFuzzConsoleLog(uint256 x) public pure { + console.log("inside fuzz test, x is:", x); } - "#, +} + "#, ) .unwrap(); cmd.args(["test", "-vv"]).assert_success().stdout_eq(str![[r#" @@ -1101,12 +1104,12 @@ forgetest_init!(should_not_show_logs_when_fuzz_test, |prj, cmd| { prj.add_test( "ContractFuzz.t.sol", - r#"pragma solidity 0.8.24; - import {Test, console2} from "forge-std/Test.sol"; + r#" + import {Test, console} from "forge-std/Test.sol"; contract ContractFuzz is Test { function testFuzzConsoleLog(uint256 x) public pure { - console2.log("inside fuzz test, x is:", x); + console.log("inside fuzz test, x is:", x); } } "#, @@ -1140,15 +1143,15 @@ forgetest_init!(should_not_show_logs_when_fuzz_test_inline_config, |prj, cmd| { prj.add_test( "ContractFuzz.t.sol", - r#"pragma solidity 0.8.24; - import {Test, console2} from "forge-std/Test.sol"; - contract ContractFuzz is Test { + r#" +import {Test, console} from "forge-std/Test.sol"; - /// forge-config: default.fuzz.show-logs = false - function testFuzzConsoleLog(uint256 x) public pure { - console2.log("inside fuzz test, x is:", x); - } +contract ContractFuzz is Test { + /// forge-config: default.fuzz.show-logs = false + function testFuzzConsoleLog(uint256 x) public pure { + console.log("inside fuzz test, x is:", x); } +} "#, ) .unwrap(); @@ -1177,8 +1180,9 @@ forgetest_init!(internal_functions_trace, |prj, cmd| { prj.add_test( "Simple", - r#"pragma solidity 0.8.24; - import {Test, console2} from "forge-std/Test.sol"; + r#" +import {Test, console} from "forge-std/Test.sol"; + contract SimpleContract { uint256 public num; address public addr; @@ -1254,8 +1258,8 @@ forgetest_init!(internal_functions_trace_memory, |prj, cmd| { prj.add_test( "Simple", - r#"pragma solidity 0.8.24; -import {Test, console2} from "forge-std/Test.sol"; + r#" +import {Test, console} from "forge-std/Test.sol"; contract SimpleContract { string public str = "initial value"; @@ -1283,7 +1287,7 @@ contract SimpleContractTest is Test { r#" ... Traces: - [421960] SimpleContractTest::test() + [421947] SimpleContractTest::test() ├─ [385978] → new SimpleContract@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f │ └─ ← [Return] 1814 bytes of code ├─ [2534] SimpleContract::setStr("new value") @@ -1506,7 +1510,7 @@ Logs: }); // https://github.com/foundry-rs/foundry/issues/4370 -forgetest_init!(repro_4370, |prj, cmd| { +forgetest_init!(pause_gas_metering_with_delete, |prj, cmd| { prj.wipe_contracts(); prj.add_test( @@ -1543,7 +1547,7 @@ forgetest_init!(pause_tracing, |prj, cmd| { prj.add_source( "Pause.t.sol", - r#"pragma solidity 0.8.24; + r#" import {Vm} from "./Vm.sol"; import {DSTest} from "./test.sol"; contract TraceGenerator is DSTest { @@ -1628,7 +1632,7 @@ forgetest_init!(gas_metering_reset, |prj, cmd| { prj.add_source( "ATest.t.sol", - r#"pragma solidity 0.8.24; + r#" import {Vm} from "./Vm.sol"; import {DSTest} from "./test.sol"; contract B { @@ -1780,8 +1784,8 @@ contract CounterTest is Test { cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" ... -[FAIL. Reason: Error != expected error: NumberNotEven(1) != RandomError()] test_decode() ([GAS]) -[FAIL. Reason: Error != expected error: NumberNotEven(1) != NumberNotEven(2)] test_decode_with_args() ([GAS]) +[FAIL: Error != expected error: NumberNotEven(1) != RandomError()] test_decode() ([GAS]) +[FAIL: Error != expected error: NumberNotEven(1) != NumberNotEven(2)] test_decode_with_args() ([GAS]) ... "#]]); }); @@ -1795,7 +1799,7 @@ forgetest_init!(test_expect_partial_revert, |prj, cmd| { prj.add_source( "Counter.t.sol", - r#"pragma solidity 0.8.24; + r#" import {Vm} from "./Vm.sol"; import {DSTest} from "./test.sol"; contract Counter { @@ -1830,7 +1834,7 @@ contract CounterTest is DSTest { ... [PASS] testExpectPartialRevertWith4Bytes() ([GAS]) [PASS] testExpectPartialRevertWithSelector() ([GAS]) -[FAIL. Reason: Error != expected error: WrongNumber(0) != custom error 238ace70:] testExpectRevert() ([GAS]) +[FAIL: Error != expected error: WrongNumber(0) != custom error 0x238ace70] testExpectRevert() ([GAS]) ... "#]]); }); @@ -1849,7 +1853,7 @@ forgetest_init!(test_assume_no_revert, |prj, cmd| { prj.add_source( "Counter.t.sol", - r#"pragma solidity 0.8.24; + r#" import {Vm} from "./Vm.sol"; import {DSTest} from "./test.sol"; contract CounterWithRevert { @@ -1912,10 +1916,66 @@ contract CounterRevertTest is DSTest { cmd.args(["test"]).with_no_redact().assert_failure().stdout_eq(str![[r#" ... -[FAIL. Reason: assertion failed; counterexample: [..]] test_assume_no_revert_fail_assert(uint256) [..] -[FAIL. Reason: CheckError(); counterexample: [..]] test_assume_no_revert_fail_in_2nd_call(uint256) [..] -[FAIL. Reason: CheckError(); counterexample: [..]] test_assume_no_revert_fail_in_3rd_call(uint256) [..] +[FAIL; counterexample: [..]] test_assume_no_revert_fail_assert(uint256) [..] +[FAIL: CheckError(); counterexample: [..]] test_assume_no_revert_fail_in_2nd_call(uint256) [..] +[FAIL: CheckError(); counterexample: [..]] test_assume_no_revert_fail_in_3rd_call(uint256) [..] [PASS] test_assume_no_revert_pass(uint256) [..] ... "#]]); }); + +forgetest_init!(skip_output, |prj, cmd| { + prj.wipe_contracts(); + prj.insert_ds_test(); + prj.insert_vm(); + prj.clear(); + + prj.add_source( + "Counter.t.sol", + r#" + import {Vm} from "./Vm.sol"; + import {DSTest} from "./test.sol"; + + contract Skips is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function test_skipUnit() public { + vm.skip(true); + } + function test_skipUnitReason() public { + vm.skip(true, "unit"); + } + + function test_skipFuzz(uint) public { + vm.skip(true); + } + function test_skipFuzzReason(uint) public { + vm.skip(true, "fuzz"); + } + + function invariant_skipInvariant() public { + vm.skip(true); + } + function invariant_skipInvariantReason() public { + vm.skip(true, "invariant"); + } + } + "#, + ) + .unwrap(); + + cmd.arg("test").assert_success().stdout_eq(str![[r#" +... +Ran 6 tests for src/Counter.t.sol:Skips +[SKIP] invariant_skipInvariant() (runs: 1, calls: 1, reverts: 1) +[SKIP: invariant] invariant_skipInvariantReason() (runs: 1, calls: 1, reverts: 1) +[SKIP] test_skipFuzz(uint256) (runs: 0, [AVG_GAS]) +[SKIP: fuzz] test_skipFuzzReason(uint256) (runs: 0, [AVG_GAS]) +[SKIP] test_skipUnit() ([GAS]) +[SKIP: unit] test_skipUnitReason() ([GAS]) +Suite result: ok. 0 passed; 0 failed; 6 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 0 failed, 6 skipped (6 total tests) + +"#]]); +}); diff --git a/crates/forge/tests/fixtures/ScriptVerify.sol b/crates/forge/tests/fixtures/ScriptVerify.sol index 4ff4c0750..8dc611f5e 100644 --- a/crates/forge/tests/fixtures/ScriptVerify.sol +++ b/crates/forge/tests/fixtures/ScriptVerify.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity =0.8.16; +pragma solidity ^0.8.16; import {Unique} from "./unique.sol"; diff --git a/crates/forge/tests/it/fuzz.rs b/crates/forge/tests/it/fuzz.rs index b0bd57a05..e3fc31b8b 100644 --- a/crates/forge/tests/it/fuzz.rs +++ b/crates/forge/tests/it/fuzz.rs @@ -198,7 +198,7 @@ contract InlineMaxRejectsTest is Test { cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" ... -[FAIL. Reason: The `vm.assume` cheatcode rejected too many inputs (1 allowed)] test_fuzz_bound(uint256) (runs: 0, [AVG_GAS]) +[FAIL: `vm.assume` rejected too many inputs (1 allowed)] test_fuzz_bound(uint256) (runs: 0, [AVG_GAS]) ... "#]]); }); diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index bd9286713..5a571f772 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -479,7 +479,7 @@ async fn test_invariant_assume_respects_restrictions() { vec![( "invariant_dummy()", false, - Some("The `vm.assume` cheatcode rejected too many inputs (1 allowed)".into()), + Some("`vm.assume` rejected too many inputs (1 allowed)".into()), None, None, )], diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 4b9f8f53e..70db0a407 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -47,11 +47,11 @@ static TEMPLATE_LOCK: LazyLock = static NEXT_ID: AtomicUsize = AtomicUsize::new(0); /// The default Solc version used when compiling tests. -pub const SOLC_VERSION: &str = "0.8.23"; +pub const SOLC_VERSION: &str = "0.8.27"; /// Another Solc version used when compiling tests. Necessary to avoid downloading multiple /// versions. -pub const OTHER_SOLC_VERSION: &str = "0.8.22"; +pub const OTHER_SOLC_VERSION: &str = "0.8.26"; /// External test builder #[derive(Clone, Debug)] diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index edc7dad32..eacf81bda 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -402,6 +402,7 @@ interface Vm { function sign(bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); function sign(address signer, bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); function skip(bool skipTest) external; + function skip(bool skipTest, string calldata reason) external; function sleep(uint256 duration) external; function snapshot() external returns (uint256 snapshotId); function split(string calldata input, string calldata delimiter) external pure returns (string[] memory outputs); From 19bd60a173cbc9870eb733e2650e9774c83f3cbd Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 13 Sep 2024 09:56:53 +0300 Subject: [PATCH 161/184] chore(cheatcodes): ArbitraryStorage as option (#8848) * chore(cheatcodes): ArbitraryStorage as option * Update crates/cheatcodes/src/utils.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * Update crates/cheatcodes/src/inspector.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * Early arbitrary_storage_end return --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/cheatcodes/src/evm.rs | 19 +++++-- crates/cheatcodes/src/inspector.rs | 86 ++++++++++++++++++------------ crates/cheatcodes/src/utils.rs | 10 ++-- 3 files changed, 72 insertions(+), 43 deletions(-) diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 703d7db12..45b6f8c75 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -93,19 +93,28 @@ impl Cheatcode for loadCall { let mut val = ccx.ecx.sload(target, slot.into())?; if val.is_cold && val.data.is_zero() { - if ccx.state.arbitrary_storage.is_arbitrary(&target) { + if ccx.state.has_arbitrary_storage(&target) { // If storage slot is untouched and load from a target with arbitrary storage, // then set random value for current slot. let rand_value = ccx.state.rng().gen(); - ccx.state.arbitrary_storage.save(ccx.ecx, target, slot.into(), rand_value); + ccx.state.arbitrary_storage.as_mut().unwrap().save( + ccx.ecx, + target, + slot.into(), + rand_value, + ); val.data = rand_value; - } else if ccx.state.arbitrary_storage.is_copy(&target) { + } else if ccx.state.is_arbitrary_storage_copy(&target) { // If storage slot is untouched and load from a target that copies storage from // a source address with arbitrary storage, then copy existing arbitrary value. // If no arbitrary value generated yet, then the random one is saved and set. let rand_value = ccx.state.rng().gen(); - val.data = - ccx.state.arbitrary_storage.copy(ccx.ecx, target, slot.into(), rand_value); + val.data = ccx.state.arbitrary_storage.as_mut().unwrap().copy( + ccx.ecx, + target, + slot.into(), + rand_value, + ); } } diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index e09e1e8c8..779c4ac35 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -266,16 +266,6 @@ pub struct ArbitraryStorage { } impl ArbitraryStorage { - /// Whether the given address has arbitrary storage. - pub fn is_arbitrary(&self, address: &Address) -> bool { - self.values.contains_key(address) - } - - /// Whether the given address is a copy of an address with arbitrary storage. - pub fn is_copy(&self, address: &Address) -> bool { - self.copies.contains_key(address) - } - /// Marks an address with arbitrary storage. pub fn mark_arbitrary(&mut self, address: &Address) { self.values.insert(*address, HashMap::default()); @@ -283,7 +273,7 @@ impl ArbitraryStorage { /// Maps an address that copies storage with the arbitrary storage address. pub fn mark_copy(&mut self, from: &Address, to: &Address) { - if self.is_arbitrary(from) { + if self.values.contains_key(from) { self.copies.insert(*to, *from); } } @@ -456,7 +446,7 @@ pub struct Cheatcodes { pub ignored_traces: IgnoredTraces, /// Addresses with arbitrary storage. - pub arbitrary_storage: ArbitraryStorage, + pub arbitrary_storage: Option, } // This is not derived because calling this in `fn new` with `..Default::default()` creates a second @@ -1080,6 +1070,28 @@ impl Cheatcodes { None => StdRng::from_entropy(), }) } + + /// Returns existing or set a default `ArbitraryStorage` option. + /// Used by `setArbitraryStorage` cheatcode to track addresses with arbitrary storage. + pub fn arbitrary_storage(&mut self) -> &mut ArbitraryStorage { + self.arbitrary_storage.get_or_insert_with(ArbitraryStorage::default) + } + + /// Whether the given address has arbitrary storage. + pub fn has_arbitrary_storage(&self, address: &Address) -> bool { + match &self.arbitrary_storage { + Some(storage) => storage.values.contains_key(address), + None => false, + } + } + + /// Whether the given address is a copy of an address with arbitrary storage. + pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool { + match &self.arbitrary_storage { + Some(storage) => storage.copies.contains_key(address), + None => false, + } + } } impl Inspector for Cheatcodes { @@ -1146,10 +1158,7 @@ impl Inspector for Cheatcodes { } // `setArbitraryStorage` and `copyStorage`: add arbitrary values to storage. - if (self.arbitrary_storage.is_arbitrary(&interpreter.contract().target_address) || - self.arbitrary_storage.is_copy(&interpreter.contract().target_address)) && - interpreter.current_opcode() == op::SLOAD - { + if self.arbitrary_storage.is_some() { self.arbitrary_storage_end(interpreter, ecx); } } @@ -1577,26 +1586,33 @@ impl Cheatcodes { interpreter: &mut Interpreter, ecx: &mut EvmContext, ) { - let key = try_or_return!(interpreter.stack().peek(0)); - let target_address = interpreter.contract().target_address; - if let Ok(value) = ecx.sload(target_address, key) { - if value.is_cold && value.data.is_zero() { + let (key, target_address) = if interpreter.current_opcode() == op::SLOAD { + (try_or_return!(interpreter.stack().peek(0)), interpreter.contract().target_address) + } else { + return + }; + + let Ok(value) = ecx.sload(target_address, key) else { + return; + }; + + if value.is_cold && value.data.is_zero() { + if self.has_arbitrary_storage(&target_address) { let arbitrary_value = self.rng().gen(); - if self.arbitrary_storage.is_copy(&target_address) { - self.arbitrary_storage.copy( - &mut ecx.inner, - target_address, - key, - arbitrary_value, - ); - } else { - self.arbitrary_storage.save( - &mut ecx.inner, - target_address, - key, - arbitrary_value, - ); - } + self.arbitrary_storage.as_mut().unwrap().save( + &mut ecx.inner, + target_address, + key, + arbitrary_value, + ); + } else if self.is_arbitrary_storage_copy(&target_address) { + let arbitrary_value = self.rng().gen(); + self.arbitrary_storage.as_mut().unwrap().copy( + &mut ecx.inner, + target_address, + key, + arbitrary_value, + ); } } } diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index 0896f2b31..a96a44832 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -153,7 +153,7 @@ impl Cheatcode for resumeTracingCall { impl Cheatcode for setArbitraryStorageCall { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { target } = self; - ccx.state.arbitrary_storage.mark_arbitrary(target); + ccx.state.arbitrary_storage().mark_arbitrary(target); Ok(Default::default()) } @@ -162,15 +162,19 @@ impl Cheatcode for setArbitraryStorageCall { impl Cheatcode for copyStorageCall { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { from, to } = self; + ensure!( - !ccx.state.arbitrary_storage.is_arbitrary(to), + !ccx.state.has_arbitrary_storage(to), "target address cannot have arbitrary storage" ); + if let Ok(from_account) = ccx.load_account(*from) { let from_storage = from_account.storage.clone(); if let Ok(mut to_account) = ccx.load_account(*to) { to_account.storage = from_storage; - ccx.state.arbitrary_storage.mark_copy(from, to); + if let Some(ref mut arbitrary_storage) = &mut ccx.state.arbitrary_storage { + arbitrary_storage.mark_copy(from, to); + } } } From 898c9360175c2eff5a09cd87df016fe6f5c2181c Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 13 Sep 2024 10:02:10 +0300 Subject: [PATCH 162/184] feat(forge): support junit xml test reports (#8852) * feat(forge): support junit xml test reports * Update crates/forge/bin/cmd/test/mod.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * Changes after review * Fix clippy * Support skipped tests with message * Set reason msg only is Some --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- Cargo.lock | 64 ++++++++++++++++ crates/common/src/shell.rs | 4 +- crates/forge/Cargo.toml | 2 + crates/forge/bin/cmd/test/mod.rs | 63 +++++++++++++++- crates/forge/tests/cli/test_cmd.rs | 113 +++++++++++++++++++++++++++++ 5 files changed, 241 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6345afda7..20e4a36b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3261,6 +3261,7 @@ dependencies = [ "anvil", "async-trait", "axum", + "chrono", "clap", "clap_complete", "clap_complete_fig", @@ -3300,6 +3301,7 @@ dependencies = [ "paste", "path-slash", "proptest", + "quick-junit", "rayon", "regex", "reqwest", @@ -5717,6 +5719,15 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "newtype-uuid" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526cb7c660872e401beaf3297f95f548ce3b4b4bdd8121b7c0713771d7c4a6e" +dependencies = [ + "uuid 1.10.0", +] + [[package]] name = "nibble_vec" version = "0.1.0" @@ -6809,6 +6820,21 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quick-junit" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ffd2f9a162cfae131bed6d9d1ed60adced33be340a94f96952897d7cb0c240" +dependencies = [ + "chrono", + "indexmap 2.5.0", + "newtype-uuid", + "quick-xml 0.36.1", + "strip-ansi-escapes", + "thiserror", + "uuid 1.10.0", +] + [[package]] name = "quick-xml" version = "0.18.1" @@ -6827,6 +6853,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "quick-xml" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a05e2e8efddfa51a84ca47cec303fac86c8541b686d37cac5efc0e094417bc" +dependencies = [ + "memchr", +] + [[package]] name = "quinn" version = "0.11.5" @@ -8185,6 +8220,15 @@ dependencies = [ "quote", ] +[[package]] +name = "strip-ansi-escapes" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ff8ef943b384c414f54aefa961dd2bd853add74ec75e7ac74cf91dba62bcfa" +dependencies = [ + "vte", +] + [[package]] name = "strsim" version = "0.11.1" @@ -9158,6 +9202,26 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" +[[package]] +name = "vte" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" +dependencies = [ + "utf8parse", + "vte_generate_state_changes", +] + +[[package]] +name = "vte_generate_state_changes" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "wait-timeout" version = "0.2.0" diff --git a/crates/common/src/shell.rs b/crates/common/src/shell.rs index 5ce6d5b38..8ab98e64a 100644 --- a/crates/common/src/shell.rs +++ b/crates/common/src/shell.rs @@ -75,8 +75,8 @@ impl Shell { Self { output, verbosity } } - /// Returns a new shell that conforms to the specified verbosity arguments, where `json` takes - /// higher precedence + /// Returns a new shell that conforms to the specified verbosity arguments, where `json` + /// or `junit` takes higher precedence. pub fn from_args(silent: bool, json: bool) -> Self { match (silent, json) { (_, true) => Self::json(), diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 77034d619..f636e4c62 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -46,6 +46,7 @@ serde.workspace = true tracing.workspace = true yansi.workspace = true humantime-serde = "1.1.1" +chrono.workspace = true # bin forge-doc.workspace = true @@ -107,6 +108,7 @@ opener = "0.7" # soldeer soldeer.workspace = true +quick-junit = "0.5.0" [target.'cfg(unix)'.dependencies] tikv-jemallocator = { workspace = true, optional = true } diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 669413d78..3bc659ea0 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -1,5 +1,6 @@ use super::{install, test::filter::ProjectPathsAwareFilter, watch::WatchArgs}; use alloy_primitives::U256; +use chrono::Utc; use clap::{Parser, ValueHint}; use eyre::{Context, OptionExt, Result}; use forge::{ @@ -40,14 +41,17 @@ use foundry_evm::traces::identifier::TraceIdentifiers; use regex::Regex; use std::{ collections::{BTreeMap, BTreeSet}, + fmt::Write, path::PathBuf, sync::{mpsc::channel, Arc}, - time::Instant, + time::{Duration, Instant}, }; use yansi::Paint; mod filter; mod summary; + +use quick_junit::{NonSuccessKind, Report, TestCase, TestCaseStatus, TestSuite}; use summary::TestSummaryReporter; pub use filter::FilterArgs; @@ -115,6 +119,10 @@ pub struct TestArgs { #[arg(long, help_heading = "Display options")] json: bool, + /// Output test results as JUnit XML report. + #[arg(long, conflicts_with = "json", help_heading = "Display options")] + junit: bool, + /// Stop running tests after the first failure. #[arg(long)] pub fail_fast: bool, @@ -181,7 +189,7 @@ impl TestArgs { pub async fn run(self) -> Result { trace!(target: "forge::test", "executing test command"); - shell::set_shell(shell::Shell::from_args(self.opts.silent, self.json))?; + shell::set_shell(shell::Shell::from_args(self.opts.silent, self.json || self.junit))?; self.execute_tests().await } @@ -300,7 +308,7 @@ impl TestArgs { let sources_to_compile = self.get_sources_to_compile(&config, &filter)?; let compiler = ProjectCompiler::new() - .quiet_if(self.json || self.opts.silent) + .quiet_if(self.json || self.junit || self.opts.silent) .files(sources_to_compile); let output = compiler.compile(&project)?; @@ -494,6 +502,12 @@ impl TestArgs { return Ok(TestOutcome::new(results, self.allow_failure)); } + if self.junit { + let results = runner.test_collect(filter); + println!("{}", junit_xml_report(&results, verbosity).to_string()?); + return Ok(TestOutcome::new(results, self.allow_failure)); + } + let remote_chain_id = runner.evm_opts.get_remote_chain_id().await; let known_contracts = runner.known_contracts.clone(); @@ -814,6 +828,49 @@ fn persist_run_failures(config: &Config, outcome: &TestOutcome) { } } +/// Generate test report in JUnit XML report format. +fn junit_xml_report(results: &BTreeMap, verbosity: u8) -> Report { + let mut total_duration = Duration::default(); + let mut junit_report = Report::new("Test run"); + junit_report.set_timestamp(Utc::now()); + for (suite_name, suite_result) in results { + let mut test_suite = TestSuite::new(suite_name); + total_duration += suite_result.duration; + test_suite.set_time(suite_result.duration); + test_suite.set_system_out(suite_result.summary()); + for (test_name, test_result) in &suite_result.test_results { + let mut test_status = match test_result.status { + TestStatus::Success => TestCaseStatus::success(), + TestStatus::Failure => TestCaseStatus::non_success(NonSuccessKind::Failure), + TestStatus::Skipped => TestCaseStatus::skipped(), + }; + if let Some(reason) = &test_result.reason { + test_status.set_message(reason); + } + + let mut test_case = TestCase::new(test_name, test_status); + test_case.set_time(test_result.duration); + + let mut sys_out = String::new(); + let result_report = test_result.kind.report(); + write!(sys_out, "{test_result} {test_name} {result_report}").unwrap(); + if verbosity >= 2 && !test_result.logs.is_empty() { + write!(sys_out, "\\nLogs:\\n").unwrap(); + let console_logs = decode_console_logs(&test_result.logs); + for log in console_logs { + write!(sys_out, " {log}\\n").unwrap(); + } + } + + test_case.set_system_out(sys_out); + test_suite.add_test_case(test_case); + } + junit_report.add_test_suite(test_suite); + } + junit_report.set_time(total_duration); + junit_report +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index b6eec8903..09c7e40f8 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -1977,5 +1977,118 @@ Suite result: ok. 0 passed; 0 failed; 6 skipped; [ELAPSED] Ran 1 test suite [ELAPSED]: 0 tests passed, 0 failed, 6 skipped (6 total tests) +"#]]); +}); + +forgetest_init!(should_generate_junit_xml_report, |prj, cmd| { + prj.wipe_contracts(); + prj.insert_ds_test(); + prj.insert_vm(); + prj.clear(); + + prj.add_source( + "JunitReportTest.t.sol", + r#" + import {Vm} from "./Vm.sol"; + import {DSTest} from "./test.sol"; + + contract AJunitReportTest is DSTest { + function test_junit_assert_fail() public { + assert(1 > 2); + } + + function test_junit_revert_fail() public { + require(1 > 2, "Revert"); + } + } + + contract BJunitReportTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + function test_junit_pass() public { + require(1 < 2, "Revert"); + } + + function test_junit_skip() public { + vm.skip(true); + } + + function test_junit_skip_with_message() public { + vm.skip(true, "skipped test"); + } + + function test_junit_pass_fuzz(uint256 a) public { + } + } + "#, + ) + .unwrap(); + + cmd.args(["test", "--junit"]).assert_failure().stdout_eq(str![[r#" + + + + + + [FAIL: panic: assertion failed (0x01)] test_junit_assert_fail() ([GAS]) + + + + [FAIL: revert: Revert] test_junit_revert_fail() ([GAS]) + + Suite result: FAILED. 0 passed; 2 failed; 0 skipped; [ELAPSED] + + + + [PASS] test_junit_pass() ([GAS]) + + + [PASS] test_junit_pass_fuzz(uint256) (runs: 256, [AVG_GAS]) + + + + [SKIP] test_junit_skip() ([GAS]) + + + + [SKIP: skipped test] test_junit_skip_with_message() ([GAS]) + + Suite result: ok. 2 passed; 0 failed; 2 skipped; [ELAPSED] + + + + +"#]]); +}); + +forgetest_init!(should_generate_junit_xml_report_with_logs, |prj, cmd| { + prj.wipe_contracts(); + prj.add_source( + "JunitReportTest.t.sol", + r#" +import "forge-std/Test.sol"; +contract JunitReportTest is Test { + function test_junit_with_logs() public { + console.log("Step1"); + console.log("Step2"); + console.log("Step3"); + assert(2 > 1); + } +} + "#, + ) + .unwrap(); + + cmd.args(["test", "--junit", "-vvvv"]).assert_success().stdout_eq(str![[r#" + + + + + [PASS] test_junit_with_logs() ([GAS])/nLogs:/n Step1/n Step2/n Step3/n + + Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + + + + "#]]); }); From 6c0a15b10aaa276050f8f4d53c79d9f0de53b16a Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 13 Sep 2024 13:58:20 +0200 Subject: [PATCH 163/184] test: ignore 3703 (#8861) ignore 3703 --- crates/forge/tests/it/repros.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index 73310f011..0d1e15052 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -22,32 +22,35 @@ use foundry_test_utils::Filter; /// Creates a test that runs `testdata/repros/Issue{issue}.t.sol`. macro_rules! test_repro { - ($issue_number:literal $(,)?) => { - test_repro!($issue_number, false, None); + ($(#[$attr:meta])* $issue_number:literal $(,)?) => { + test_repro!($(#[$attr])* $issue_number, false, None); }; - ($issue_number:literal, $should_fail:expr $(,)?) => { - test_repro!($issue_number, $should_fail, None); + ($(#[$attr:meta])* $issue_number:literal, $should_fail:expr $(,)?) => { + test_repro!($(#[$attr])* $issue_number, $should_fail, None); }; - ($issue_number:literal, $should_fail:expr, $sender:expr $(,)?) => { + ($(#[$attr:meta])* $issue_number:literal, $should_fail:expr, $sender:expr $(,)?) => { paste::paste! { #[tokio::test(flavor = "multi_thread")] + $(#[$attr])* async fn [< issue_ $issue_number >]() { repro_config($issue_number, $should_fail, $sender.into(), &*TEST_DATA_DEFAULT).await.run().await; } } }; - ($issue_number:literal, $should_fail:expr, $sender:expr, |$res:ident| $e:expr $(,)?) => { + ($(#[$attr:meta])* $issue_number:literal, $should_fail:expr, $sender:expr, |$res:ident| $e:expr $(,)?) => { paste::paste! { #[tokio::test(flavor = "multi_thread")] + $(#[$attr])* async fn [< issue_ $issue_number >]() { let mut $res = repro_config($issue_number, $should_fail, $sender.into(), &*TEST_DATA_DEFAULT).await.test(); $e } } }; - ($issue_number:literal; |$config:ident| $e:expr $(,)?) => { + ($(#[$attr:meta])* $issue_number:literal; |$config:ident| $e:expr $(,)?) => { paste::paste! { #[tokio::test(flavor = "multi_thread")] + $(#[$attr])* async fn [< issue_ $issue_number >]() { let mut $config = repro_config($issue_number, false, None, &*TEST_DATA_DEFAULT).await; $e @@ -167,7 +170,10 @@ test_repro!(3674, false, address!("F0959944122fb1ed4CfaBA645eA06EED30427BAA")); test_repro!(3685); // https://github.com/foundry-rs/foundry/issues/3703 -test_repro!(3703); +test_repro!( + #[ignore = "flaky polygon RPCs"] + 3703 +); // https://github.com/foundry-rs/foundry/issues/3708 test_repro!(3708); From 84e63fe5b1d907e9d914f422e006cab8dd44b713 Mon Sep 17 00:00:00 2001 From: Fulum <112524347+AxelAramburu@users.noreply.github.com> Date: Fri, 13 Sep 2024 14:04:41 +0200 Subject: [PATCH 164/184] Fix truncated hex in anvil dump_state (#8216) * Fix truncated hex in anvil dump_state * Change type of key * fixs * rustfmt * fix --------- Co-authored-by: unknown Co-authored-by: Matthias Seitz Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> --- crates/anvil/src/eth/backend/db.rs | 8 ++++---- crates/anvil/src/eth/backend/genesis.rs | 2 +- crates/anvil/src/eth/backend/mem/fork_db.rs | 4 ++-- crates/anvil/src/eth/backend/mem/in_memory_db.rs | 16 +++++++++------- crates/anvil/src/eth/backend/mem/mod.rs | 4 ++-- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index cf8f1f5c9..6c16f694d 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -112,7 +112,7 @@ pub trait Db: } /// Sets the balance of the given address - fn set_storage_at(&mut self, address: Address, slot: U256, val: U256) -> DatabaseResult<()>; + fn set_storage_at(&mut self, address: Address, slot: B256, val: B256) -> DatabaseResult<()>; /// inserts a blockhash for the given number fn insert_block_hash(&mut self, number: U256, hash: B256); @@ -184,8 +184,8 @@ impl + Send + Sync + Clone + fmt::Debug> D self.insert_account_info(address, account) } - fn set_storage_at(&mut self, address: Address, slot: U256, val: U256) -> DatabaseResult<()> { - self.insert_account_storage(address, slot, val) + fn set_storage_at(&mut self, address: Address, slot: B256, val: B256) -> DatabaseResult<()> { + self.insert_account_storage(address, slot.into(), val.into()) } fn insert_block_hash(&mut self, number: U256, hash: B256) { @@ -356,7 +356,7 @@ pub struct SerializableAccountRecord { pub nonce: u64, pub balance: U256, pub code: Bytes, - pub storage: BTreeMap, + pub storage: BTreeMap, } /// Defines a backwards-compatible enum for transactions. diff --git a/crates/anvil/src/eth/backend/genesis.rs b/crates/anvil/src/eth/backend/genesis.rs index ee459204d..e5e76ff49 100644 --- a/crates/anvil/src/eth/backend/genesis.rs +++ b/crates/anvil/src/eth/backend/genesis.rs @@ -49,7 +49,7 @@ impl GenesisConfig { db.insert_account(addr, self.genesis_to_account_info(&acc)); // insert all storage values for (k, v) in storage.unwrap_or_default().iter() { - db.set_storage_at(addr, U256::from_be_bytes(k.0), U256::from_be_bytes(v.0))?; + db.set_storage_at(addr, *k, *v)?; } } } diff --git a/crates/anvil/src/eth/backend/mem/fork_db.rs b/crates/anvil/src/eth/backend/mem/fork_db.rs index a179f50c3..bd879f8f4 100644 --- a/crates/anvil/src/eth/backend/mem/fork_db.rs +++ b/crates/anvil/src/eth/backend/mem/fork_db.rs @@ -22,7 +22,7 @@ impl Db for ForkedDatabase { self.database_mut().insert_account(address, account) } - fn set_storage_at(&mut self, address: Address, slot: U256, val: U256) -> DatabaseResult<()> { + fn set_storage_at(&mut self, address: Address, slot: B256, val: B256) -> DatabaseResult<()> { // this ensures the account is loaded first let _ = Database::basic(self, address)?; self.database_mut().set_storage_at(address, slot, val) @@ -57,7 +57,7 @@ impl Db for ForkedDatabase { nonce: v.info.nonce, balance: v.info.balance, code: code.original_bytes(), - storage: v.storage.into_iter().collect(), + storage: v.storage.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), }, )) }) diff --git a/crates/anvil/src/eth/backend/mem/in_memory_db.rs b/crates/anvil/src/eth/backend/mem/in_memory_db.rs index 059c00f32..2abee72ef 100644 --- a/crates/anvil/src/eth/backend/mem/in_memory_db.rs +++ b/crates/anvil/src/eth/backend/mem/in_memory_db.rs @@ -24,8 +24,8 @@ impl Db for MemDb { self.inner.insert_account_info(address, account) } - fn set_storage_at(&mut self, address: Address, slot: U256, val: U256) -> DatabaseResult<()> { - self.inner.insert_account_storage(address, slot, val) + fn set_storage_at(&mut self, address: Address, slot: B256, val: B256) -> DatabaseResult<()> { + self.inner.insert_account_storage(address, slot.into(), val.into()) } fn insert_block_hash(&mut self, number: U256, hash: B256) { @@ -56,7 +56,7 @@ impl Db for MemDb { nonce: v.info.nonce, balance: v.info.balance, code: code.original_bytes(), - storage: v.storage.into_iter().collect(), + storage: v.storage.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), }, )) }) @@ -159,7 +159,9 @@ mod tests { nonce: 1234, }, ); - dump_db.set_storage_at(test_addr, U256::from(1234567), U256::from(1)).unwrap(); + dump_db + .set_storage_at(test_addr, U256::from(1234567).into(), U256::from(1).into()) + .unwrap(); // blocks dumping/loading tested in storage.rs let state = dump_db @@ -202,8 +204,8 @@ mod tests { }, ); - db.set_storage_at(test_addr, U256::from(1234567), U256::from(1)).unwrap(); - db.set_storage_at(test_addr, U256::from(1234568), U256::from(2)).unwrap(); + db.set_storage_at(test_addr, U256::from(1234567).into(), U256::from(1).into()).unwrap(); + db.set_storage_at(test_addr, U256::from(1234568).into(), U256::from(2).into()).unwrap(); let mut new_state = SerializableState::default(); @@ -218,7 +220,7 @@ mod tests { ); let mut new_storage = BTreeMap::default(); - new_storage.insert(U256::from(1234568), U256::from(5)); + new_storage.insert(U256::from(1234568).into(), U256::from(5).into()); new_state.accounts.insert( test_addr, diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index dd10c672f..6a0aeadee 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -576,7 +576,7 @@ impl Backend { slot: U256, val: B256, ) -> DatabaseResult<()> { - self.db.write().await.set_storage_at(address, slot, U256::from_be_bytes(val.0)) + self.db.write().await.set_storage_at(address, slot.into(), val) } /// Returns the configured specid @@ -2498,7 +2498,7 @@ impl Backend { self.db.write().await.clear(); for (address, acc) in common_state { for (key, value) in acc.storage { - self.db.write().await.set_storage_at(address, key, value)?; + self.db.write().await.set_storage_at(address, key.into(), value.into())?; } self.db.write().await.insert_account(address, acc.info); } From e16a75b615f812db6127ea22e23c3ee65504c1f1 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Fri, 13 Sep 2024 21:03:39 +0400 Subject: [PATCH 165/184] fix(script): correctly fill metadata + prompt on noop transactions (#8833) * refactor metadata handling * warn on noop transactions * infer is_fixed_gas_limit * add comments back * rm gas limit println * fix fixtures * retain --- crates/common/src/transactions.rs | 7 ++ crates/forge/tests/cli/script.rs | 8 +- crates/script/src/lib.rs | 9 ++- crates/script/src/simulate.rs | 118 ++++++++++++++---------------- crates/script/src/transaction.rs | 67 +++++++++-------- 5 files changed, 112 insertions(+), 97 deletions(-) diff --git a/crates/common/src/transactions.rs b/crates/common/src/transactions.rs index c927d575d..9df8f896f 100644 --- a/crates/common/src/transactions.rs +++ b/crates/common/src/transactions.rs @@ -216,6 +216,13 @@ impl TransactionMaybeSigned { Self::Unsigned(tx) => tx.gas, } } + + pub fn nonce(&self) -> Option { + match self { + Self::Signed { tx, .. } => Some(tx.nonce()), + Self::Unsigned(tx) => tx.nonce, + } + } } impl From for TransactionMaybeSigned { diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index ee803147a..2aefd985c 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -239,7 +239,6 @@ Script ran successfully. ========================== Simulated On-chain Traces: -Gas limit was set in script to 500000 [45299] → new GasWaster@[..] └─ ← [Return] 226 bytes of code @@ -347,7 +346,6 @@ Script ran successfully. ========================== Simulated On-chain Traces: -Gas limit was set in script to 500000 [45299] → new GasWaster@[..] └─ ← [Return] 226 bytes of code @@ -1684,6 +1682,7 @@ contract SimpleScript is Script { "2000000", "--priority-gas-price", "100000", + "--non-interactive", ]) .assert_success() .stdout_eq(str![[r#" @@ -1697,6 +1696,7 @@ Script ran successfully. success: bool true ## Setting up 1 EVM. +Script contains a transaction to 0x0000000000000000000000000000000000000000 which does not contain any code. ========================== @@ -1733,6 +1733,7 @@ ONCHAIN EXECUTION COMPLETE & SUCCESSFUL. format!("{dev:?}").as_str(), "--broadcast", "--unlocked", + "--non-interactive", ]) .assert_success() .stdout_eq(str![[r#" @@ -1744,6 +1745,7 @@ Script ran successfully. success: bool true ## Setting up 1 EVM. +Script contains a transaction to 0x0000000000000000000000000000000000000000 which does not contain any code. ========================== @@ -1797,6 +1799,7 @@ contract SimpleScript is Script { &handle.http_endpoint(), "--broadcast", "--unlocked", + "--non-interactive", ]) .assert_success() .stdout_eq(str![[r#" @@ -1810,6 +1813,7 @@ Script ran successfully. success: bool true ## Setting up 1 EVM. +Script contains a transaction to 0x0000000000000000000000000000000000000000 which does not contain any code. ========================== diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 6af605690..a20a884f0 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -276,7 +276,7 @@ impl ScriptArgs { }; // Exit early in case user didn't provide any broadcast/verify related flags. - if !bundled.args.broadcast && !bundled.args.resume && !bundled.args.verify { + if !bundled.args.should_broadcast() { shell::println("\nSIMULATION COMPLETE. To broadcast these transactions, add --broadcast and wallet configuration(s) to the previous command. See forge script --help for more.")?; return Ok(()); } @@ -419,7 +419,7 @@ impl ScriptArgs { let deployment_size = deployed_code.len(); if deployment_size > max_size { - prompt_user = self.broadcast; + prompt_user = self.should_broadcast(); shell::println(format!( "{}", format!( @@ -440,6 +440,11 @@ impl ScriptArgs { Ok(()) } + + /// We only broadcast transactions if --broadcast or --resume was passed. + fn should_broadcast(&self) -> bool { + self.broadcast || self.resume + } } impl Provider for ScriptArgs { diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 077c507e2..eea660bcd 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -14,8 +14,9 @@ use crate::{ }; use alloy_network::TransactionBuilder; use alloy_primitives::{utils::format_units, Address, Bytes, TxKind, U256}; +use dialoguer::Confirm; use eyre::{Context, Result}; -use foundry_cheatcodes::{BroadcastableTransactions, ScriptWallets}; +use foundry_cheatcodes::ScriptWallets; use foundry_cli::utils::{has_different_gas_calc, now}; use foundry_common::{get_contract_name, shell, ContractData}; use foundry_evm::traces::{decode_trace_arena, render_trace_arena}; @@ -25,6 +26,7 @@ use std::{ collections::{BTreeMap, HashMap, VecDeque}, sync::Arc, }; +use yansi::Paint; /// Same as [ExecutedState](crate::execute::ExecutedState), but also contains [ExecutionArtifacts] /// which are obtained from [ScriptResult]. @@ -48,16 +50,30 @@ impl PreSimulationState { /// /// Both modes will panic if any of the transactions have None for the `rpc` field. pub async fn fill_metadata(self) -> Result { - let transactions = if let Some(txs) = self.execution_result.transactions.as_ref() { - if self.args.skip_simulation { - shell::println("\nSKIPPING ON CHAIN SIMULATION.")?; - self.no_simulation(txs.clone())? - } else { - self.onchain_simulation(txs.clone()).await? - } + let address_to_abi = self.build_address_to_abi_map(); + + let mut transactions = self + .execution_result + .transactions + .clone() + .unwrap_or_default() + .into_iter() + .map(|tx| { + let rpc = tx.rpc.expect("missing broadcastable tx rpc url"); + TransactionWithMetadata::new( + tx.transaction, + rpc, + &address_to_abi, + &self.execution_artifacts.decoder, + ) + }) + .collect::>>()?; + + if self.args.skip_simulation { + shell::println("\nSKIPPING ON CHAIN SIMULATION.")?; } else { - VecDeque::new() - }; + transactions = self.simulate_and_fill(transactions).await?; + } Ok(FilledTransactionsState { args: self.args, @@ -73,9 +89,9 @@ impl PreSimulationState { /// transactions in those environments. /// /// Collects gas usage and metadata for each transaction. - pub async fn onchain_simulation( + pub async fn simulate_and_fill( &self, - transactions: BroadcastableTransactions, + transactions: VecDeque, ) -> Result> { trace!(target: "script", "executing onchain simulation"); @@ -87,18 +103,15 @@ impl PreSimulationState { .collect::>(), ); - let address_to_abi = self.build_address_to_abi_map(); - let mut final_txs = VecDeque::new(); // Executes all transactions from the different forks concurrently. let futs = transactions .into_iter() - .map(|transaction| async { - let rpc = transaction.rpc.expect("missing broadcastable tx rpc url"); - let mut runner = runners.get(&rpc).expect("invalid rpc url").write(); + .map(|mut transaction| async { + let mut runner = runners.get(&transaction.rpc).expect("invalid rpc url").write(); - let mut tx = transaction.transaction; + let tx = &mut transaction.transaction; let to = if let Some(TxKind::Call(to)) = tx.to() { Some(to) } else { None }; let result = runner .simulate( @@ -111,46 +124,24 @@ impl PreSimulationState { .wrap_err("Internal EVM error during simulation")?; if !result.success { - return Ok((None, result.traces)); + return Ok((None, false, result.traces)); } - let created_contracts = result.get_created_contracts(); - // Simulate mining the transaction if the user passes `--slow`. if self.args.slow { runner.executor.env_mut().block.number += U256::from(1); } - let is_fixed_gas_limit = if let Some(tx) = tx.as_unsigned_mut() { - match tx.gas { - // If tx.gas is already set that means it was specified in script - Some(gas) => { - println!("Gas limit was set in script to {gas}"); - true - } - // We inflate the gas used by the user specified percentage - None => { - let gas = result.gas_used * self.args.gas_estimate_multiplier / 100; - tx.gas = Some(gas as u128); - false - } - } + let is_noop_tx = if let Some(to) = to { + runner.executor.is_empty_code(to)? && tx.value().unwrap_or_default().is_zero() } else { - // for pre-signed transactions we can't alter gas limit - true + false }; - let tx = TransactionWithMetadata::new( - tx, - rpc, - &result, - &address_to_abi, - &self.execution_artifacts.decoder, - created_contracts, - is_fixed_gas_limit, - )?; + let transaction = + transaction.with_execution_result(&result, self.args.gas_estimate_multiplier); - eyre::Ok((Some(tx), result.traces)) + eyre::Ok((Some(transaction), is_noop_tx, result.traces)) }) .collect::>(); @@ -161,7 +152,7 @@ impl PreSimulationState { let mut abort = false; for res in join_all(futs).await { - let (tx, mut traces) = res?; + let (tx, is_noop_tx, mut traces) = res?; // Transaction will be `None`, if execution didn't pass. if tx.is_none() || self.script_config.evm_opts.verbosity > 3 { @@ -172,6 +163,21 @@ impl PreSimulationState { } if let Some(tx) = tx { + if is_noop_tx { + let to = tx.contract_address.unwrap(); + shell::println(format!("Script contains a transaction to {to} which does not contain any code.").yellow())?; + + // Only prompt if we're broadcasting and we've not disabled interactivity. + if self.args.should_broadcast() && + !self.args.non_interactive && + !Confirm::new() + .with_prompt("Do you wish to continue?".to_string()) + .interact()? + { + eyre::bail!("User canceled the script."); + } + } + final_txs.push_back(tx); } else { abort = true; @@ -220,22 +226,6 @@ impl PreSimulationState { }); try_join_all(futs).await } - - /// If simulation is disabled, converts transactions into [TransactionWithMetadata] type - /// skipping metadata filling. - fn no_simulation( - &self, - transactions: BroadcastableTransactions, - ) -> Result> { - Ok(transactions - .into_iter() - .map(|btx| { - let mut tx = TransactionWithMetadata::from_tx_request(btx.transaction); - tx.rpc = btx.rpc.expect("missing broadcastable tx rpc url"); - tx - }) - .collect()) - } } /// At this point we have converted transactions collected during script execution to diff --git a/crates/script/src/transaction.rs b/crates/script/src/transaction.rs index 0ef2559d9..bbf84ff0f 100644 --- a/crates/script/src/transaction.rs +++ b/crates/script/src/transaction.rs @@ -1,7 +1,7 @@ use super::ScriptResult; use alloy_dyn_abi::JsonAbiExt; use alloy_primitives::{hex, Address, Bytes, TxKind, B256}; -use eyre::{ContextCompat, Result, WrapErr}; +use eyre::{Result, WrapErr}; use foundry_common::{fmt::format_token_raw, ContractData, TransactionMaybeSigned, SELECTOR_LEN}; use foundry_evm::{constants::DEFAULT_CREATE2_DEPLOYER, traces::CallTraceDecoder}; use itertools::Itertools; @@ -70,53 +70,62 @@ impl TransactionWithMetadata { pub fn new( transaction: TransactionMaybeSigned, rpc: String, - result: &ScriptResult, local_contracts: &BTreeMap, decoder: &CallTraceDecoder, - additional_contracts: Vec, - is_fixed_gas_limit: bool, ) -> Result { let mut metadata = Self::from_tx_request(transaction); metadata.rpc = rpc; - metadata.is_fixed_gas_limit = is_fixed_gas_limit; + // If tx.gas is already set that means it was specified in script + metadata.is_fixed_gas_limit = metadata.tx().gas().is_some(); - // Specify if any contract was directly created with this transaction if let Some(TxKind::Call(to)) = metadata.transaction.to() { if to == DEFAULT_CREATE2_DEPLOYER { - metadata.set_create( - true, - Address::from_slice(&result.returned), - local_contracts, - )?; + if let Some(input) = metadata.transaction.input() { + let (salt, init_code) = input.split_at(32); + metadata.set_create( + true, + DEFAULT_CREATE2_DEPLOYER + .create2_from_code(B256::from_slice(salt), init_code), + local_contracts, + )?; + } } else { metadata .set_call(to, local_contracts, decoder) .wrap_err("Could not decode transaction type.")?; } } else { - metadata.set_create( - false, - result.address.wrap_err("There should be a contract address from CREATE.")?, - local_contracts, - )?; + let sender = + metadata.transaction.from().expect("all transactions should have a sender"); + let nonce = metadata.transaction.nonce().expect("all transactions should have a nonce"); + metadata.set_create(false, sender.create(nonce), local_contracts)?; } + Ok(metadata) + } + + /// Populates additional data from the transaction execution result. + pub fn with_execution_result( + mut self, + result: &ScriptResult, + gas_estimate_multiplier: u64, + ) -> Self { + let mut created_contracts = result.get_created_contracts(); + // Add the additional contracts created in this transaction, so we can verify them later. - if let Some(tx_address) = metadata.contract_address { - metadata.additional_contracts = additional_contracts - .into_iter() - .filter_map(|contract| { - // Filter out the transaction contract repeated init_code. - if contract.address != tx_address { - Some(contract) - } else { - None - } - }) - .collect(); + created_contracts.retain(|contract| { + // Filter out the contract that was created by the transaction itself. + self.contract_address.map_or(true, |addr| addr != contract.address) + }); + + if !self.is_fixed_gas_limit { + if let Some(unsigned) = self.transaction.as_unsigned_mut() { + // We inflate the gas used by the user specified percentage + unsigned.gas = Some((result.gas_used * gas_estimate_multiplier / 100) as u128); + } } - Ok(metadata) + self } /// Populate the transaction as CREATE tx From 280aa26c3a6d71c442d32b45920d30c62f9d6fbb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 15 Sep 2024 11:11:59 +0000 Subject: [PATCH 166/184] chore(deps): weekly `cargo update` (#8868) Locking 72 packages to latest compatible versions Updating alloy-chains v0.1.30 -> v0.1.31 Updating alloy-consensus v0.3.3 -> v0.3.5 Updating alloy-contract v0.3.3 -> v0.3.5 Updating alloy-dyn-abi v0.8.2 -> v0.8.3 Updating alloy-eips v0.3.3 -> v0.3.5 Updating alloy-genesis v0.3.3 -> v0.3.5 Updating alloy-json-abi v0.8.2 -> v0.8.3 Updating alloy-json-rpc v0.3.3 -> v0.3.5 Updating alloy-network v0.3.3 -> v0.3.5 Updating alloy-network-primitives v0.3.3 -> v0.3.5 Updating alloy-primitives v0.8.2 -> v0.8.3 Updating alloy-provider v0.3.3 -> v0.3.5 Updating alloy-pubsub v0.3.3 -> v0.3.5 Updating alloy-rpc-client v0.3.3 -> v0.3.5 Updating alloy-rpc-types v0.3.3 -> v0.3.5 Updating alloy-rpc-types-anvil v0.3.3 -> v0.3.5 Updating alloy-rpc-types-engine v0.3.3 -> v0.3.5 Updating alloy-rpc-types-eth v0.3.3 -> v0.3.5 Updating alloy-rpc-types-trace v0.3.3 -> v0.3.5 Updating alloy-rpc-types-txpool v0.3.3 -> v0.3.5 Updating alloy-serde v0.3.3 -> v0.3.5 Updating alloy-signer v0.3.3 -> v0.3.5 Updating alloy-signer-aws v0.3.3 -> v0.3.5 Updating alloy-signer-gcp v0.3.3 -> v0.3.5 Updating alloy-signer-ledger v0.3.3 -> v0.3.5 Updating alloy-signer-local v0.3.3 -> v0.3.5 Updating alloy-signer-trezor v0.3.3 -> v0.3.5 Updating alloy-sol-macro v0.8.2 -> v0.8.3 Updating alloy-sol-macro-expander v0.8.2 -> v0.8.3 Updating alloy-sol-macro-input v0.8.2 -> v0.8.3 Updating alloy-sol-type-parser v0.8.2 -> v0.8.3 Updating alloy-sol-types v0.8.2 -> v0.8.3 Updating alloy-transport v0.3.3 -> v0.3.5 Updating alloy-transport-http v0.3.3 -> v0.3.5 Updating alloy-transport-ipc v0.3.3 -> v0.3.5 Updating alloy-transport-ws v0.3.3 -> v0.3.5 Updating anyhow v1.0.87 -> v1.0.88 Updating aws-config v1.5.5 -> v1.5.6 Updating aws-runtime v1.4.2 -> v1.4.3 Updating aws-sdk-kms v1.42.0 -> v1.43.0 Updating aws-sdk-sso v1.41.0 -> v1.42.0 Updating aws-sdk-ssooidc v1.42.0 -> v1.43.0 Updating aws-sdk-sts v1.41.0 -> v1.42.0 Updating aws-sigv4 v1.2.3 -> v1.2.4 Updating aws-smithy-http v0.60.10 -> v0.60.11 Updating aws-smithy-types v1.2.4 -> v1.2.6 Updating aws-smithy-xml v0.60.8 -> v0.60.9 Updating error-code v3.2.0 -> v3.3.1 Updating globset v0.4.14 -> v0.4.15 Updating hyper-util v0.1.7 -> v0.1.8 Updating ignore v0.4.22 -> v0.4.23 Updating keccak-asm v0.1.3 -> v0.1.4 Updating memmap2 v0.9.4 -> v0.9.5 Updating once_cell v1.19.0 -> v1.20.0 Updating op-alloy-consensus v0.2.9 -> v0.2.11 Updating op-alloy-rpc-types v0.2.9 -> v0.2.11 Updating plotters v0.3.6 -> v0.3.7 Updating plotters-backend v0.3.6 -> v0.3.7 Updating plotters-svg v0.3.6 -> v0.3.7 Updating redox_syscall v0.5.3 -> v0.5.4 Updating rgb v0.8.48 -> v0.8.50 Updating rustix v0.38.36 -> v0.38.37 Updating rustls v0.23.12 -> v0.23.13 Updating rustls-webpki v0.102.7 -> v0.102.8 Updating scc v2.1.16 -> v2.1.17 Updating sdd v3.0.2 -> v3.0.3 Updating secp256k1-sys v0.10.0 -> v0.10.1 Updating sha3-asm v0.1.3 -> v0.1.4 Updating syn-solidity v0.8.2 -> v0.8.3 Updating tower v0.5.0 -> v0.5.1 Updating unicode-ident v1.0.12 -> v1.0.13 Updating unicode-segmentation v1.11.0 -> v1.12.0 note: pass `--verbose` to see 137 unchanged dependencies behind latest Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> --- Cargo.lock | 323 +++++++++++++++++++++++++++-------------------------- 1 file changed, 162 insertions(+), 161 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 20e4a36b9..c11f775fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,9 +74,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-chains" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b4f201b0ac8f81315fbdc55269965a8ddadbc04ab47fa65a1a468f9a40f7a5f" +checksum = "b68b94c159bcc2ca5f758b8663d7b00fc7c5e40569984595ddf2221b0f7f7f6e" dependencies = [ "num_enum", "serde", @@ -85,9 +85,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1468e3128e07c7afe4ff13c17e8170c330d12c322f8924b8bf6986a27e0aad3d" +checksum = "c28ddd17ffb7e4d66ef3a84e7b179072a9320cdc4b26c7f6f44cbf1081631b36" dependencies = [ "alloy-eips", "alloy-primitives", @@ -99,9 +99,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335d62de1a887f1b780441f8a3037f39c9fb26839cc9acd891c9b80396145cd5" +checksum = "a69257e2ffe1a9f15f20a89cd54d1ca758468c5b3e87979191b8b5fc24d39b37" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -120,9 +120,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03f58cfae4d41b624effe1f11624ee40499449174b20a6d6505fd72ef0d547d" +checksum = "4004925bff5ba0a11739ae84dbb6601a981ea692f3bd45b626935ee90a6b8471" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -168,9 +168,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c35df7b972b06f1b2f4e8b7a53328522fa788054a9d3e556faf2411c5a51d5a" +checksum = "2f6c5c0a383f14519531cf58d8440e74f10b938e289f803af870be6f79223110" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -187,9 +187,9 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7210f9206c0fa2a83c824cf8cb6c962126bc9fdc4f41ade1932f14150ef5f6" +checksum = "7db0ddc76399bb1a4010f630767f027cafe65ab406cfee8e6040128cd65e8325" dependencies = [ "alloy-primitives", "alloy-serde", @@ -198,9 +198,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28ecae8b5315daecd0075084eb47f4374b3037777346ca52fc8d9c327693f02" +checksum = "9996daf962fd0a90d3c93b388033228865953b92de7bb1959b891d78750a4091" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -210,9 +210,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8866562186d237f1dfeaf989ef941a24764f764bf5c33311e37ead3519c6a429" +checksum = "7111af869909275cffc5c84d16b6c892d6d512773e40cbe83187d0b9c5235e91" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -224,9 +224,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe714e233f9eaf410de95a9af6bcd05d3a7f8c8de7a0817221e95a6b642a080" +checksum = "342028392a2d5050b7b93dd32a0715d3b3b9ce30072ecb69a35dd4895c005495" dependencies = [ "alloy-consensus", "alloy-eips", @@ -245,9 +245,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c5a38117974c5776a45e140226745a0b664f79736aa900995d8e4121558e064" +checksum = "a6e66d78c049dcadd065a926a9f2d9a9b2b10981a7889449e694fac7bccd2c6f" dependencies = [ "alloy-eips", "alloy-primitives", @@ -257,9 +257,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccb865df835f851b367ae439d6c82b117ded971628c8888b24fed411a290e38a" +checksum = "411aff151f2a73124ee473708e82ed51b2535f68928b6a1caa8bc1246ae6f7cd" dependencies = [ "alloy-rlp", "arbitrary", @@ -283,9 +283,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c65633d6ef83c3626913c004eaf166a6dd50406f724772ea8567135efd6dc5d3" +checksum = "79f14ccc2a3c575cb17b1b4af8c772cf9b5b93b7ce7047d6640e53954abb558d" dependencies = [ "alloy-chains", "alloy-consensus", @@ -322,9 +322,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949db89abae6193b44cc90ebf2eeb74eb8d2a474383c5e62b45bdcd362e84f8f" +checksum = "34b9f5e85120aab30b8da23354592f7bd2b208d33d3204ffa1d44ac2e3dd5691" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -335,7 +335,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower 0.5.0", + "tower 0.5.1", "tracing", ] @@ -363,9 +363,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fc328bb5d440599ba1b5aa44c0b9ab0625fbc3a403bb5ee94ed4a01ba23e07" +checksum = "4dc79aeca84abb122a2fffbc1c91fdf958dca5c95be3875977bc99672bde0027" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -381,16 +381,16 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower 0.5.0", + "tower 0.5.1", "tracing", "url", ] [[package]] name = "alloy-rpc-types" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f8ff679f94c497a8383f2cd09e2a099266e5f3d5e574bc82b4b379865707dbb" +checksum = "22045187a5ebf5b2af3f8b6831b66735b6556c5750ec5790aeeb45935260c1c2" dependencies = [ "alloy-rpc-types-anvil", "alloy-rpc-types-engine", @@ -403,9 +403,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d430bf98148565e67b2c08f033dd5fb27ce901c3481018941ce1524b9ad4fba" +checksum = "578d9ccad4e8510d32cc2810d05e01a232ccd79a4a6df60d257466897b43b013" dependencies = [ "alloy-primitives", "alloy-serde", @@ -414,27 +414,26 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66bb45f4c5efe227bcb51d89c97221225169976e18097671a0bd4393d8248a4" +checksum = "1c031a91e94a39f928244bc837c953817be5b8cc61759e1a9123b3abd17560dd" dependencies = [ "alloy-consensus", "alloy-eips", "alloy-primitives", "alloy-rlp", - "alloy-rpc-types-eth", "alloy-serde", + "derive_more 1.0.0", "jsonwebtoken", "rand", "serde", - "thiserror", ] [[package]] name = "alloy-rpc-types-eth" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a59b1d7c86e0a653e7f3d29954f6de5a2878d8cfd1f010ff93be5c2c48cd3b1" +checksum = "238f494727ff861a803bd73b1274ef788a615bf8f8c4bfada4e6df42afa275d2" dependencies = [ "alloy-consensus", "alloy-eips", @@ -443,17 +442,19 @@ dependencies = [ "alloy-rlp", "alloy-serde", "alloy-sol-types", + "cfg-if", + "derive_more 1.0.0", + "hashbrown 0.14.5", "itertools 0.13.0", "serde", "serde_json", - "thiserror", ] [[package]] name = "alloy-rpc-types-trace" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c54375e5a34ec5a2cf607f9ce98c0ece30dc76ad623afeb25d3953a8d7d30f20" +checksum = "64ca08b0ccc0861055ceb83a1db009c4c8a7f52a259e7cda7ca6ca36ec2b5ce8" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -465,9 +466,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65ae88491edfc8bbd55ba2b22b2ff24d4c522bacd8808461c4a232715fee3d22" +checksum = "192ad94fe34c12be8ac4413ea00b1170202faa6fdebaa756b6a33555bf86d902" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -477,9 +478,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51db8a6428a2159e01b7a43ec7aac801edd0c4db1d4de06f310c288940f16fd3" +checksum = "6b95b6f024a558593dd3b8628af03f7df2ca50e4c56839293ad0a7546e471db0" dependencies = [ "alloy-primitives", "arbitrary", @@ -489,9 +490,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebc1760c13592b7ba3fcd964abba546b8d6a9f10d15e8d92a8263731be33f36" +checksum = "da64740ff0518606c514eb0e03dd0a1daa8ff94d6d491a626fd8e50efd6c4f18" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -505,9 +506,9 @@ dependencies = [ [[package]] name = "alloy-signer-aws" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa0d52968220ddfdb8d332cfddc6c7ae8147779aebb3c463d6023f1d458471b" +checksum = "f187534919dcccebaf92774ad6b17645087b49a8716d49e2db29a2e3229b03f2" dependencies = [ "alloy-consensus", "alloy-network", @@ -523,9 +524,9 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d369e98958cf6e2a41b1b1fcee21d73185f358db23751636e3d90b1f4aa184d" +checksum = "907e56e2153e785c36e4197f4e254b5087e073a12b686f486d3bd6245c811313" dependencies = [ "alloy-consensus", "alloy-network", @@ -541,9 +542,9 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f03b98771184d733139f0475e562f6f8cb8143d0a3765674078c0a3ed00c3b" +checksum = "221d828bdf1df1e7d490c304eb1425c7adacd6897a4baa2316e3f3054fc8863f" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -561,9 +562,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bfb3508485aa798efb5725322e414313239274d3780079b7f8c6746b8ee6e1b" +checksum = "99e250010dce0e3caf6a6033e809718e5921391d937d1cbbcffe52653b37cc63" dependencies = [ "alloy-consensus", "alloy-network", @@ -581,9 +582,9 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "423c62e0596be68f55a7c03ec4b961d46c74e92ba2d0143fe147608a5c8fe4a4" +checksum = "586b4c42aa61b47591813fca23098e21530470986a00e3dc588baa2d5b8dd111" dependencies = [ "alloy-consensus", "alloy-network", @@ -598,9 +599,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2dc5201ca0018afb7a3e0cd8bd15f7ca6aca924333b5f3bb87463b41d0c4ef2" +checksum = "0458ccb02a564228fcd76efb8eb5a520521a8347becde37b402afec9a1b83859" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -612,9 +613,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "155f63dc6945885aa4532601800201fddfaa3b20901fda8e8c2570327242fe0e" +checksum = "2bc65475025fc1e84bf86fc840f04f63fcccdcf3cf12053c99918e4054dfbc69" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -631,9 +632,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "847700aa9cb59d3c7b290b2d05976cd8d76b64d73bb63116a9533132d995586b" +checksum = "6ed10f0715a0b69fde3236ff3b9ae5f6f7c97db5a387747100070d3016b9266b" dependencies = [ "alloy-json-abi", "const-hex", @@ -648,9 +649,9 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6b5d462d4520bd9ed70d8364c6280aeff13baa46ea26be1ddd33538dbbe6ac" +checksum = "3edae8ea1de519ccba896b6834dec874230f72fe695ff3c9c118e90ec7cff783" dependencies = [ "serde", "winnow", @@ -658,9 +659,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83665e5607725a7a1aab3cb0dea708f4a05e70776954ec7f0a9461439175c957" +checksum = "1eb88e4da0a1b697ed6a9f811fdba223cf4d5c21410804fd1707836af73a462b" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -671,9 +672,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd5dc4e902f1860d54952446d246ac05386311ad61030a2b906ae865416d36e0" +checksum = "3c7a669caa427abe8802184c8776f5103302f9337bb30a5b36bdebc332946c14" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -683,31 +684,31 @@ dependencies = [ "serde_json", "thiserror", "tokio", - "tower 0.5.0", + "tower 0.5.1", "tracing", "url", ] [[package]] name = "alloy-transport-http" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1742b94bb814f1ca6b322a6f9dd38a0252ff45a3119e40e888fb7029afa500ce" +checksum = "4433ffa97aab6ae643de81c7bde9a2f043496f27368a607405a5c78a610caf74" dependencies = [ "alloy-json-rpc", "alloy-transport", "reqwest", "serde_json", - "tower 0.5.0", + "tower 0.5.1", "tracing", "url", ] [[package]] name = "alloy-transport-ipc" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be321aac6f06d86855d41d4ce9ff9feb877fe7e9fe1cafce7380b981c12398c7" +checksum = "9aa02db8751f9c0c37caf8c38ad3eb7aa1cfb09cfea3451a13aacaf06846c7a5" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -726,15 +727,15 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8ed861e7030001364c8ffa2db63541f7bae275a6e636de7616c20f2fd3dc0c3" +checksum = "b0a5c4a0929479bcb85a2df628c01173618a71c807b2f499939a236dbde5d008" dependencies = [ "alloy-pubsub", "alloy-transport", "futures", "http 1.1.0", - "rustls 0.23.12", + "rustls 0.23.13", "serde_json", "tokio", "tokio-tungstenite 0.23.1", @@ -963,9 +964,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.87" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8" +checksum = "4e1496f8fb1fbf272686b8d37f523dab3e4a7443300055e74cdaa449f3114356" [[package]] name = "arbitrary" @@ -1252,9 +1253,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "aws-config" -version = "1.5.5" +version = "1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e95816a168520d72c0e7680c405a5a8c1fb6a035b4bc4b9d7b0de8e1a941697" +checksum = "848d7b9b605720989929279fa644ce8f244d0ce3146fcca5b70e4eb7b3c020fc" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1294,9 +1295,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2424565416eef55906f9f8cece2072b6b6a76075e3ff81483ebe938a89a4c05f" +checksum = "a10d5c055aa540164d9561a0e2e74ad30f0dcf7393c3a92f6733ddf9c5762468" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -1319,9 +1320,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.42.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704ab31904cf70104a3bb023079e201b1353cf132ca674b26ba6f23acbbb53c9" +checksum = "d9f7cb482caa5444d445c94417b9c74e49a849beb09ede4f2f4c3c15f8157387" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1341,9 +1342,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.41.0" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af0a3f676cba2c079c9563acc9233998c8951cdbe38629a0bef3c8c1b02f3658" +checksum = "27bf24cd0d389daa923e974b0e7c38daf308fc21e963c049f57980235017175e" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1363,9 +1364,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.42.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91b6a04495547162cf52b075e3c15a17ab6608bf9c5785d3e5a5509b3f09f5c" +checksum = "3b43b3220f1c46ac0e9dcc0a97d94b93305dacb36d1dd393996300c6b9b74364" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1385,9 +1386,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.41.0" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99c56bcd6a56cab7933980a54148b476a5a69a7694e3874d9aa2a566f150447d" +checksum = "d1c46924fb1add65bba55636e12812cae2febf68c0f37361766f627ddcca91ce" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1408,9 +1409,9 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.2.3" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5df1b0fa6be58efe9d4ccc257df0a53b89cd8909e86591a13ca54817c87517be" +checksum = "cc8db6904450bafe7473c6ca9123f88cc11089e41a025408f992db4e22d3be68" dependencies = [ "aws-credential-types", "aws-smithy-http", @@ -1442,9 +1443,9 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.60.10" +version = "0.60.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01dbcb6e2588fd64cfb6d7529661b06466419e4c54ed1c62d6510d2d0350a728" +checksum = "5c8bc3e8fdc6b8d07d976e301c02fe553f72a39b7a9fea820e023268467d7ab6" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", @@ -1525,9 +1526,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.2.4" +version = "1.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "273dcdfd762fae3e1650b8024624e7cd50e484e37abdab73a7a706188ad34543" +checksum = "03701449087215b5369c7ea17fef0dd5d24cb93439ec5af0c7615f58c3f22605" dependencies = [ "base64-simd", "bytes", @@ -1548,9 +1549,9 @@ dependencies = [ [[package]] name = "aws-smithy-xml" -version = "0.60.8" +version = "0.60.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d123fbc2a4adc3c301652ba8e149bf4bc1d1725affb9784eb20c953ace06bf55" +checksum = "ab0b0166827aa700d3dc519f72f8b3a91c35d0b8d042dc5d643a91e6f80648fc" dependencies = [ "xmlparser", ] @@ -2942,9 +2943,9 @@ dependencies = [ [[package]] name = "error-code" -version = "3.2.0" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" +checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" [[package]] name = "eth-keystore" @@ -4275,7 +4276,7 @@ dependencies = [ "serde_json", "tokio", "tonic", - "tower 0.5.0", + "tower 0.5.1", "tower-layer", "tower-util", "tracing", @@ -4556,9 +4557,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" dependencies = [ "aho-corasick", "bstr", @@ -4895,7 +4896,7 @@ dependencies = [ "http 1.1.0", "hyper 1.4.1", "hyper-util", - "rustls 0.23.12", + "rustls 0.23.13", "rustls-native-certs 0.8.0", "rustls-pki-types", "tokio", @@ -4935,9 +4936,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" dependencies = [ "bytes", "futures-channel", @@ -4994,9 +4995,9 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" dependencies = [ "crossbeam-deque", "globset", @@ -5306,9 +5307,9 @@ dependencies = [ [[package]] name = "keccak-asm" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "422fbc7ff2f2f5bdffeb07718e5a5324dca72b0c9293d50df4026652385e3314" +checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" dependencies = [ "digest 0.10.7", "sha3-asm", @@ -5555,9 +5556,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" dependencies = [ "libc", ] @@ -5994,9 +5995,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "33ea5043e58958ee56f3e15a90aee535795cd7dfd319846288d93c5b57d85cbe" [[package]] name = "oorandom" @@ -6006,9 +6007,9 @@ checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" [[package]] name = "op-alloy-consensus" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad134a77fdfebac469526756b207c7889593657eeaca374200332ec89175e27a" +checksum = "3ef361231f72bea90365e441bd97d9c8fd3875603f18bbcad0e04874c6158e53" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6022,9 +6023,9 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbd440cd8a5ccfa7c4c085b29fd0f0a1574fb53e1572f8e070cc105039e42b" +checksum = "e5a65da5f7591acba3d2304b25145ca9942ea90481213269aed9cb28add8e3ff" dependencies = [ "alloy-eips", "alloy-network", @@ -6493,9 +6494,9 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plotters" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" dependencies = [ "num-traits", "plotters-backend", @@ -6506,15 +6507,15 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" [[package]] name = "plotters-svg" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" dependencies = [ "plotters-backend", ] @@ -6873,7 +6874,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.23.12", + "rustls 0.23.13", "socket2", "thiserror", "tokio", @@ -6890,7 +6891,7 @@ dependencies = [ "rand", "ring", "rustc-hash", - "rustls 0.23.12", + "rustls 0.23.13", "slab", "thiserror", "tinyvec", @@ -7023,9 +7024,9 @@ checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" dependencies = [ "bitflags 2.6.0", ] @@ -7120,7 +7121,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.12", + "rustls 0.23.13", "rustls-native-certs 0.7.3", "rustls-pemfile 2.1.3", "rustls-pki-types", @@ -7237,9 +7238,9 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.48" +version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f86ae463694029097b846d8f99fd5536740602ae00022c0c50c5600720b2f71" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" dependencies = [ "bytemuck", ] @@ -7390,9 +7391,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.36" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags 2.6.0", "errno", @@ -7415,15 +7416,15 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.12" +version = "0.23.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" dependencies = [ "log", "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.102.7", + "rustls-webpki 0.102.8", "subtle", "zeroize", ] @@ -7503,9 +7504,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.7" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84678086bd54edf2b415183ed7a94d0efb049f1b646a33e22a36f3794be6ae56" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring", "rustls-pki-types", @@ -7612,9 +7613,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.1.16" +version = "2.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeb7ac86243095b70a7920639507b71d51a63390d1ba26c4f60a552fbb914a37" +checksum = "0c947adb109a8afce5fc9c7bf951f87f146e9147b3a6a58413105628fb1d1e66" dependencies = [ "sdd", ] @@ -7688,9 +7689,9 @@ dependencies = [ [[package]] name = "sdd" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0495e4577c672de8254beb68d01a9b62d0e8a13c099edecdbedccce3223cd29f" +checksum = "60a7b59a5d9b0099720b417b6325d91a52cbf5b3dcb5041d864be53eefa58abc" [[package]] name = "sec1" @@ -7718,9 +7719,9 @@ dependencies = [ [[package]] name = "secp256k1-sys" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1433bd67156263443f14d603720b082dd3121779323fce20cba2aa07b874bc1b" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" dependencies = [ "cc", ] @@ -7955,9 +7956,9 @@ dependencies = [ [[package]] name = "sha3-asm" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d79b758b7cb2085612b11a235055e485605a5103faccdd633f35bd7aee69dd" +checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" dependencies = [ "cc", "cfg-if", @@ -8333,9 +8334,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1e1355d44af21638c8e05d45097db6cb5ec2aa3e970c51cb2901605cf3344fa" +checksum = "4b95156f8b577cb59dc0b1df15c6f29a10afc5f8a7ac9786b0b5c68c19149278" dependencies = [ "paste", "proc-macro2", @@ -8609,7 +8610,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.12", + "rustls 0.23.13", "rustls-pki-types", "tokio", ] @@ -8658,7 +8659,7 @@ checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" dependencies = [ "futures-util", "log", - "rustls 0.23.12", + "rustls 0.23.13", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -8784,9 +8785,9 @@ dependencies = [ [[package]] name = "tower" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36b837f86b25d7c0d7988f00a54e74739be6477f2aac6201b8f429a7569991b7" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" dependencies = [ "futures-core", "futures-util", @@ -9000,7 +9001,7 @@ dependencies = [ "httparse", "log", "rand", - "rustls 0.23.12", + "rustls 0.23.13", "rustls-pki-types", "sha1", "thiserror", @@ -9069,9 +9070,9 @@ checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" @@ -9084,9 +9085,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-truncate" From f141a4386aa24ddafa90ad50cd9ee5e93bf0d025 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Mon, 16 Sep 2024 04:49:04 -0700 Subject: [PATCH 167/184] feat: transaction access list option (#8818) * add access list * fix lint * improve * set access list even if legacy is true * update grammar * update comment to doc string * call access list if no string provided * address comments * update docs * update docs again * address comments * refactor --- crates/cast/bin/tx.rs | 23 +++++++++++++++++++++-- crates/cli/src/opts/transaction.rs | 8 ++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/crates/cast/bin/tx.rs b/crates/cast/bin/tx.rs index 419ad5bb6..08ee6d182 100644 --- a/crates/cast/bin/tx.rs +++ b/crates/cast/bin/tx.rs @@ -4,12 +4,12 @@ use alloy_network::{AnyNetwork, TransactionBuilder}; use alloy_primitives::{hex, Address, Bytes, TxKind, U256}; use alloy_provider::Provider; use alloy_rlp::Decodable; -use alloy_rpc_types::{Authorization, TransactionInput, TransactionRequest}; +use alloy_rpc_types::{AccessList, Authorization, TransactionInput, TransactionRequest}; use alloy_serde::WithOtherFields; use alloy_signer::Signer; use alloy_transport::Transport; use cast::revm::primitives::SignedAuthorization; -use eyre::Result; +use eyre::{Result, WrapErr}; use foundry_cli::{ opts::TransactionOpts, utils::{self, parse_function_args}, @@ -17,6 +17,7 @@ use foundry_cli::{ use foundry_common::ens::NameOrAddress; use foundry_config::{Chain, Config}; use foundry_wallets::{WalletOpts, WalletSigner}; +use serde_json; /// Different sender kinds used by [`CastTxBuilder`]. pub enum SenderKind<'a> { @@ -134,6 +135,7 @@ pub struct CastTxBuilder { auth: Option, chain: Chain, etherscan_api_key: Option, + access_list: Option>, state: S, _t: std::marker::PhantomData, } @@ -190,6 +192,7 @@ where chain, etherscan_api_key, auth: tx_opts.auth, + access_list: tx_opts.access_list, state: InitState, _t: std::marker::PhantomData, }) @@ -206,6 +209,7 @@ where chain: self.chain, etherscan_api_key: self.etherscan_api_key, auth: self.auth, + access_list: self.access_list, state: ToState { to }, _t: self._t, }) @@ -266,6 +270,7 @@ where chain: self.chain, etherscan_api_key: self.etherscan_api_key, auth: self.auth, + access_list: self.access_list, state: InputState { kind: self.state.to.into(), input, func }, _t: self._t, }) @@ -316,6 +321,20 @@ where return Ok((self.tx, self.state.func)); } + if let Some(access_list) = match self.access_list { + None => None, + // --access-list provided with no value, call the provider to create it + Some(None) => Some(self.provider.create_access_list(&self.tx).await?.access_list), + // Access list provided as a string, attempt to parse it + Some(Some(ref s)) => Some( + serde_json::from_str::(s) + .map(AccessList::from) + .wrap_err("Failed to parse access list from string")?, + ), + } { + self.tx.set_access_list(access_list); + } + if self.legacy && self.tx.gas_price.is_none() { self.tx.gas_price = Some(self.provider.get_gas_price().await?); } diff --git a/crates/cli/src/opts/transaction.rs b/crates/cli/src/opts/transaction.rs index c443146b5..6730902f9 100644 --- a/crates/cli/src/opts/transaction.rs +++ b/crates/cli/src/opts/transaction.rs @@ -62,6 +62,14 @@ pub struct TransactionOpts { /// Can be either a hex-encoded signed authorization or an address. #[arg(long, conflicts_with_all = &["legacy", "blob"])] pub auth: Option, + + /// EIP-2930 access list. + /// + /// Accepts either a JSON-encoded access list or an empty value to create the access list + /// via an RPC call to `eth_createAccessList`. To retrieve only the access list portion, use + /// the `cast access-list` command. + #[arg(long)] + pub access_list: Option>, } #[cfg(test)] From 011dd09d3daf035f6771c7dbce705a484c98bf00 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 16 Sep 2024 16:41:01 +0300 Subject: [PATCH 168/184] chore(tests): fix isolate CI + all targets / features tests (#8871) chore(tests): fix all targets / features tests --- crates/forge/tests/cli/config.rs | 6 ++- crates/forge/tests/cli/ext_integration.rs | 2 +- crates/forge/tests/cli/script.rs | 50 +++++++++++------------ crates/forge/tests/cli/test_cmd.rs | 21 ++++++---- 4 files changed, 45 insertions(+), 34 deletions(-) diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index b597ce439..1ddd40272 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -505,6 +505,7 @@ forgetest!(can_set_gas_price, |prj, cmd| { // test that we can detect remappings from foundry.toml forgetest_init!(can_detect_lib_foundry_toml, |prj, cmd| { + std::env::remove_var("DAPP_REMAPPINGS"); let config = cmd.config(); let remappings = config.remappings.iter().cloned().map(Remapping::from).collect::>(); similar_asserts::assert_eq!( @@ -545,6 +546,7 @@ forgetest_init!(can_detect_lib_foundry_toml, |prj, cmd| { let toml_file = nested.join("foundry.toml"); pretty_err(&toml_file, fs::write(&toml_file, config.to_string_pretty().unwrap())); + std::env::remove_var("DAPP_REMAPPINGS"); let another_config = cmd.config(); let remappings = another_config.remappings.iter().cloned().map(Remapping::from).collect::>(); @@ -564,6 +566,7 @@ forgetest_init!(can_detect_lib_foundry_toml, |prj, cmd| { config.src = "custom-source-dir".into(); pretty_err(&toml_file, fs::write(&toml_file, config.to_string_pretty().unwrap())); + std::env::remove_var("DAPP_REMAPPINGS"); let config = cmd.config(); let remappings = config.remappings.iter().cloned().map(Remapping::from).collect::>(); similar_asserts::assert_eq!( @@ -689,7 +692,8 @@ forgetest_init!(can_parse_default_fs_permissions, |_prj, cmd| { let config = cmd.config(); assert_eq!(config.fs_permissions.len(), 1); - let out_permission = config.fs_permissions.find_permission(Path::new("out")).unwrap(); + let permissions = config.fs_permissions.joined(Path::new("test")); + let out_permission = permissions.find_permission(Path::new("test/out")).unwrap(); assert_eq!(FsAccessPermission::Read, out_permission); }); diff --git a/crates/forge/tests/cli/ext_integration.rs b/crates/forge/tests/cli/ext_integration.rs index 8aadad62f..1cf7b54ad 100644 --- a/crates/forge/tests/cli/ext_integration.rs +++ b/crates/forge/tests/cli/ext_integration.rs @@ -99,7 +99,7 @@ fn lil_web3() { #[test] #[cfg_attr(windows, ignore = "Windows cannot find installed programs")] fn snekmate() { - ExtTester::new("pcaversaccio", "snekmate", "316088761ca7605216b5bfbbecca8d694c61ed98") + ExtTester::new("pcaversaccio", "snekmate", "df226f4a45e86c8f8c3ff1f9fa3443d260002050") .install_command(&["pnpm", "install", "--prefer-offline"]) // Try npm if pnpm fails / is not installed. .install_command(&["npm", "install", "--prefer-offline"]) diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 2aefd985c..9fc398c90 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -223,12 +223,12 @@ contract DeployScript is Script { [SOLC_VERSION] [ELAPSED] Compiler run successful! Traces: - [81034] DeployScript::run() + [..] DeployScript::run() ├─ [0] VM::startBroadcast() │ └─ ← [Return] - ├─ [45299] → new GasWaster@[..] + ├─ [..] → new GasWaster@[..] │ └─ ← [Return] 226 bytes of code - ├─ [226] GasWaster::wasteGas(200000 [2e5]) + ├─ [..] GasWaster::wasteGas(200000 [2e5]) │ └─ ← [Stop] └─ ← [Stop] @@ -330,12 +330,12 @@ Warning (2018): Function state mutability can be restricted to view | ^ (Relevant source part starts here and spans across multiple lines). Traces: - [81034] DeployScript::run() + [..] DeployScript::run() ├─ [0] VM::startBroadcast() │ └─ ← [Return] - ├─ [45299] → new GasWaster@[..] + ├─ [..] → new GasWaster@[..] │ └─ ← [Return] 226 bytes of code - ├─ [226] GasWaster::wasteGas(200000 [2e5]) + ├─ [..] GasWaster::wasteGas(200000 [2e5]) │ └─ ← [Stop] └─ ← [Stop] @@ -515,10 +515,10 @@ contract DeployScript is Script { [SOLC_VERSION] [ELAPSED] Compiler run successful! Traces: - [116040] DeployScript::run() + [..] DeployScript::run() ├─ [0] VM::startBroadcast() │ └─ ← [Return] - ├─ [75723] → new HashChecker@[..] + ├─ [..] → new HashChecker@[..] │ └─ ← [Return] 378 bytes of code └─ ← [Stop] @@ -595,58 +595,58 @@ contract RunScript is Script { [SOLC_VERSION] [ELAPSED] Compiler run successful! Traces: - [51327] RunScript::run() + [..] RunScript::run() ├─ [0] VM::startBroadcast() │ └─ ← [Return] ├─ [0] VM::roll([..]) │ └─ ← [Return] ├─ [0] VM::roll([..]) │ └─ ← [Return] - ├─ [22394] [..]::update() + ├─ [..] [..]::update() │ └─ ← [Stop] - ├─ [239] [..]::checkLastHash() [staticcall] + ├─ [..] [..]::checkLastHash() [staticcall] │ └─ ← [Stop] ├─ [0] VM::roll([..]) │ └─ ← [Return] - ├─ [494] [..]::update() + ├─ [..] [..]::update() │ └─ ← [Stop] - ├─ [239] [..]::checkLastHash() [staticcall] + ├─ [..] [..]::checkLastHash() [staticcall] │ └─ ← [Stop] ├─ [0] VM::roll([..]) │ └─ ← [Return] - ├─ [494] [..]::update() + ├─ [..] [..]::update() │ └─ ← [Stop] - ├─ [239] [..]::checkLastHash() [staticcall] + ├─ [..] [..]::checkLastHash() [staticcall] │ └─ ← [Stop] ├─ [0] VM::roll([..]) │ └─ ← [Return] - ├─ [494] [..]::update() + ├─ [..] [..]::update() │ └─ ← [Stop] - ├─ [239] [..]::checkLastHash() [staticcall] + ├─ [..] [..]::checkLastHash() [staticcall] │ └─ ← [Stop] ├─ [0] VM::roll([..]) │ └─ ← [Return] - ├─ [494] [..]::update() + ├─ [..] [..]::update() │ └─ ← [Stop] - ├─ [239] [..]::checkLastHash() [staticcall] + ├─ [..] [..]::checkLastHash() [staticcall] │ └─ ← [Stop] ├─ [0] VM::roll([..]) │ └─ ← [Return] - ├─ [494] [..]::update() + ├─ [..] [..]::update() │ └─ ← [Stop] - ├─ [239] [..]::checkLastHash() [staticcall] + ├─ [..] [..]::checkLastHash() [staticcall] │ └─ ← [Stop] ├─ [0] VM::roll([..]) │ └─ ← [Return] - ├─ [494] [..]::update() + ├─ [..] [..]::update() │ └─ ← [Stop] - ├─ [239] [..]::checkLastHash() [staticcall] + ├─ [..] [..]::checkLastHash() [staticcall] │ └─ ← [Stop] ├─ [0] VM::roll([..]) │ └─ ← [Return] - ├─ [494] [..]::update() + ├─ [..] [..]::update() │ └─ ← [Stop] - ├─ [239] [..]::checkLastHash() [staticcall] + ├─ [..] [..]::checkLastHash() [staticcall] │ └─ ← [Stop] └─ ← [Stop] diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 09c7e40f8..0254f251b 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -1453,10 +1453,17 @@ contract ATest is Test { ) .unwrap(); - cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" -... -[PASS] testSelfMeteringRevert() (gas: 3299) -... + cmd.args(["test"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/ATest.t.sol:ATest +[PASS] testSelfMeteringRevert() ([GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + "#]]); }); @@ -1732,12 +1739,12 @@ contract ATest is DSTest { [PASS] testResetGas() (gas: 40) [PASS] testResetGas1() (gas: 40) [PASS] testResetGas2() (gas: 40) -[PASS] testResetGas3() (gas: 134476) -[PASS] testResetGas4() (gas: 56302) +[PASS] testResetGas3() (gas: [..]) +[PASS] testResetGas4() (gas: [..]) [PASS] testResetGas5() (gas: 40) [PASS] testResetGas6() (gas: 40) [PASS] testResetGas7() (gas: 49) -[PASS] testResetGas8() (gas: 622) +[PASS] testResetGas8() (gas: [..]) [PASS] testResetGas9() (gas: 40) [PASS] testResetNegativeGas() (gas: 0) ... From 78a8d492ec95f98d9e35a81ca37511b6ffc4604c Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Mon, 16 Sep 2024 19:11:58 +0530 Subject: [PATCH 169/184] feat(`anvil`): optionally preserve historical states on dump_state (#8864) --- crates/anvil/core/src/eth/mod.rs | 19 ++-- crates/anvil/src/cmd.rs | 32 +++++-- crates/anvil/src/eth/api.rs | 19 +++- crates/anvil/src/eth/backend/db.rs | 60 ++++++++++++ crates/anvil/src/eth/backend/mem/fork_db.rs | 16 +++- .../anvil/src/eth/backend/mem/in_memory_db.rs | 10 +- crates/anvil/src/eth/backend/mem/mod.rs | 30 +++++- crates/anvil/src/eth/backend/mem/storage.rs | 34 ++++++- crates/anvil/tests/it/state.rs | 94 ++++++++++++++++++- 9 files changed, 286 insertions(+), 28 deletions(-) diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index 11342b817..70c62ed56 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -523,11 +523,8 @@ pub enum EthRequest { /// Serializes the current state (including contracts code, contract's storage, accounts /// properties, etc.) into a savable data blob - #[cfg_attr( - feature = "serde", - serde(rename = "anvil_dumpState", alias = "hardhat_dumpState", with = "empty_params") - )] - DumpState(()), + #[cfg_attr(feature = "serde", serde(rename = "anvil_dumpState", alias = "hardhat_dumpState"))] + DumpState(#[cfg_attr(feature = "serde", serde(default))] Option>>), /// Adds state previously dumped with `DumpState` to the current chain #[cfg_attr( @@ -1210,9 +1207,19 @@ mod tests { #[test] fn test_serde_custom_dump_state() { - let s = r#"{"method": "anvil_dumpState", "params": [] }"#; + let s = r#"{"method": "anvil_dumpState", "params": [true]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); + + let s = r#"{"method": "anvil_dumpState"}"#; + let value: serde_json::Value = serde_json::from_str(s).unwrap(); + let req = serde_json::from_value::(value).unwrap(); + match req { + EthRequest::DumpState(param) => { + assert!(param.is_none()); + } + _ => unreachable!(), + } } #[test] diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 1238e5906..3b7778a72 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -149,6 +149,15 @@ pub struct NodeArgs { #[arg(long, value_name = "PATH", conflicts_with = "init")] pub dump_state: Option, + /// Preserve historical state snapshots when dumping the state. + /// + /// This will save the in-memory states of the chain at particular block hashes. + /// + /// These historical states will be loaded into the memory when `--load-state` / `--state`, and + /// aids in RPC calls beyond the block at which state was dumped. + #[arg(long, conflicts_with = "init", default_value = "false")] + pub preserve_historical_states: bool, + /// Initialize the chain from a previously saved state snapshot. #[arg( long, @@ -306,6 +315,7 @@ impl NodeArgs { let dump_state = self.dump_state_path(); let dump_interval = self.state_interval.map(Duration::from_secs).unwrap_or(DEFAULT_DUMP_INTERVAL); + let preserve_historical_states = self.preserve_historical_states; let (api, mut handle) = crate::try_spawn(self.into_node_config()?).await?; @@ -320,7 +330,8 @@ impl NodeArgs { let task_manager = handle.task_manager(); let mut on_shutdown = task_manager.on_shutdown(); - let mut state_dumper = PeriodicStateDumper::new(api, dump_state, dump_interval); + let mut state_dumper = + PeriodicStateDumper::new(api, dump_state, dump_interval, preserve_historical_states); task_manager.spawn(async move { // wait for the SIGTERM signal on unix systems @@ -588,11 +599,17 @@ struct PeriodicStateDumper { in_progress_dump: Option + Send + Sync + 'static>>>, api: EthApi, dump_state: Option, + preserve_historical_states: bool, interval: Interval, } impl PeriodicStateDumper { - fn new(api: EthApi, dump_state: Option, interval: Duration) -> Self { + fn new( + api: EthApi, + dump_state: Option, + interval: Duration, + preserve_historical_states: bool, + ) -> Self { let dump_state = dump_state.map(|mut dump_state| { if dump_state.is_dir() { dump_state = dump_state.join("state.json"); @@ -602,19 +619,19 @@ impl PeriodicStateDumper { // periodically flush the state let interval = tokio::time::interval_at(Instant::now() + interval, interval); - Self { in_progress_dump: None, api, dump_state, interval } + Self { in_progress_dump: None, api, dump_state, preserve_historical_states, interval } } async fn dump(&self) { if let Some(state) = self.dump_state.clone() { - Self::dump_state(self.api.clone(), state).await + Self::dump_state(self.api.clone(), state, self.preserve_historical_states).await } } /// Infallible state dump - async fn dump_state(api: EthApi, dump_state: PathBuf) { + async fn dump_state(api: EthApi, dump_state: PathBuf, preserve_historical_states: bool) { trace!(path=?dump_state, "Dumping state on shutdown"); - match api.serialized_state().await { + match api.serialized_state(preserve_historical_states).await { Ok(state) => { if let Err(err) = foundry_common::fs::write_json_file(&dump_state, &state) { error!(?err, "Failed to dump state"); @@ -655,7 +672,8 @@ impl Future for PeriodicStateDumper { if this.interval.poll_tick(cx).is_ready() { let api = this.api.clone(); let path = this.dump_state.clone().expect("exists; see above"); - this.in_progress_dump = Some(Box::pin(Self::dump_state(api, path))); + this.in_progress_dump = + Some(Box::pin(Self::dump_state(api, path, this.preserve_historical_states))); } else { break } diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 90cdc1006..f856fef42 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -351,7 +351,10 @@ impl EthApi { EthRequest::SetNextBlockBaseFeePerGas(gas) => { self.anvil_set_next_block_base_fee_per_gas(gas).await.to_rpc_result() } - EthRequest::DumpState(_) => self.anvil_dump_state().await.to_rpc_result(), + EthRequest::DumpState(preserve_historical_states) => self + .anvil_dump_state(preserve_historical_states.and_then(|s| s.params)) + .await + .to_rpc_result(), EthRequest::LoadState(buf) => self.anvil_load_state(buf).await.to_rpc_result(), EthRequest::NodeInfo(_) => self.anvil_node_info().await.to_rpc_result(), EthRequest::AnvilMetadata(_) => self.anvil_metadata().await.to_rpc_result(), @@ -1839,14 +1842,20 @@ impl EthApi { /// process by calling `anvil_loadState` /// /// Handler for RPC call: `anvil_dumpState` - pub async fn anvil_dump_state(&self) -> Result { + pub async fn anvil_dump_state( + &self, + preserve_historical_states: Option, + ) -> Result { node_info!("anvil_dumpState"); - self.backend.dump_state().await + self.backend.dump_state(preserve_historical_states.unwrap_or(false)).await } /// Returns the current state - pub async fn serialized_state(&self) -> Result { - self.backend.serialized_state().await + pub async fn serialized_state( + &self, + preserve_historical_states: bool, + ) -> Result { + self.backend.serialized_state(preserve_historical_states).await } /// Append chain state buffer to current chain. Will overwrite any conflicting addresses or diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index 6c16f694d..39edbd8cd 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -32,6 +32,11 @@ pub trait MaybeFullDatabase: DatabaseRef { /// Clear the state and move it into a new `StateSnapshot` fn clear_into_snapshot(&mut self) -> StateSnapshot; + /// Read the state snapshot + /// + /// This clones all the states and returns a new `StateSnapshot` + fn read_as_snapshot(&self) -> StateSnapshot; + /// Clears the entire database fn clear(&mut self); @@ -51,6 +56,10 @@ where unreachable!("never called for DatabaseRef") } + fn read_as_snapshot(&self) -> StateSnapshot { + unreachable!("never called for DatabaseRef") + } + fn clear(&mut self) {} fn init_from_snapshot(&mut self, _snapshot: StateSnapshot) {} @@ -124,6 +133,7 @@ pub trait Db: best_number: U64, blocks: Vec, transactions: Vec, + historical_states: Option, ) -> DatabaseResult>; /// Deserialize and add all chain data to the backend storage @@ -198,6 +208,7 @@ impl + Send + Sync + Clone + fmt::Debug> D _best_number: U64, _blocks: Vec, _transaction: Vec, + _historical_states: Option, ) -> DatabaseResult> { Ok(None) } @@ -235,6 +246,22 @@ impl> MaybeFullDatabase for CacheDB { StateSnapshot { accounts, storage: account_storage, block_hashes } } + fn read_as_snapshot(&self) -> StateSnapshot { + let db_accounts = self.accounts.clone(); + let mut accounts = HashMap::new(); + let mut account_storage = HashMap::new(); + + for (addr, acc) in db_accounts { + account_storage.insert(addr, acc.storage.clone()); + let mut info = acc.info; + info.code = self.contracts.get(&info.code_hash).cloned(); + accounts.insert(addr, info); + } + + let block_hashes = self.block_hashes.clone(); + StateSnapshot { accounts, storage: account_storage, block_hashes } + } + fn clear(&mut self) { self.clear_into_snapshot(); } @@ -280,6 +307,12 @@ impl StateDb { pub fn new(db: impl MaybeFullDatabase + Send + Sync + 'static) -> Self { Self(Box::new(db)) } + + pub fn serialize_state(&mut self) -> StateSnapshot { + // Using read_as_snapshot makes sures we don't clear the historical state from the current + // instance. + self.read_as_snapshot() + } } impl DatabaseRef for StateDb { @@ -310,6 +343,10 @@ impl MaybeFullDatabase for StateDb { self.0.clear_into_snapshot() } + fn read_as_snapshot(&self) -> StateSnapshot { + self.0.read_as_snapshot() + } + fn clear(&mut self) { self.0.clear() } @@ -332,6 +369,11 @@ pub struct SerializableState { pub blocks: Vec, #[serde(default)] pub transactions: Vec, + /// Historical states of accounts and storage at particular block hashes. + /// + /// Note: This is an Option for backwards compatibility. + #[serde(default)] + pub historical_states: Option, } impl SerializableState { @@ -444,3 +486,21 @@ impl From for MinedTransaction { } } } + +#[derive(Clone, Debug, Serialize, Deserialize, Default)] +pub struct SerializableHistoricalStates(Vec<(B256, StateSnapshot)>); + +impl SerializableHistoricalStates { + pub const fn new(states: Vec<(B256, StateSnapshot)>) -> Self { + Self(states) + } +} + +impl IntoIterator for SerializableHistoricalStates { + type Item = (B256, StateSnapshot); + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} diff --git a/crates/anvil/src/eth/backend/mem/fork_db.rs b/crates/anvil/src/eth/backend/mem/fork_db.rs index bd879f8f4..1329de724 100644 --- a/crates/anvil/src/eth/backend/mem/fork_db.rs +++ b/crates/anvil/src/eth/backend/mem/fork_db.rs @@ -1,7 +1,7 @@ use crate::{ eth::backend::db::{ Db, MaybeForkedDatabase, MaybeFullDatabase, SerializableAccountRecord, SerializableBlock, - SerializableState, SerializableTransaction, StateDb, + SerializableHistoricalStates, SerializableState, SerializableTransaction, StateDb, }, revm::primitives::AccountInfo, }; @@ -38,6 +38,7 @@ impl Db for ForkedDatabase { best_number: U64, blocks: Vec, transactions: Vec, + historical_states: Option, ) -> DatabaseResult> { let mut db = self.database().clone(); let accounts = self @@ -68,6 +69,7 @@ impl Db for ForkedDatabase { best_block_number: Some(best_number), blocks, transactions, + historical_states, })) } @@ -93,6 +95,14 @@ impl MaybeFullDatabase for ForkedDatabase { StateSnapshot { accounts, storage, block_hashes } } + fn read_as_snapshot(&self) -> StateSnapshot { + let db = self.inner().db(); + let accounts = db.accounts.read().clone(); + let storage = db.storage.read().clone(); + let block_hashes = db.block_hashes.read().clone(); + StateSnapshot { accounts, storage, block_hashes } + } + fn clear(&mut self) { self.flush_cache(); self.clear_into_snapshot(); @@ -112,6 +122,10 @@ impl MaybeFullDatabase for ForkDbSnapshot { std::mem::take(&mut self.snapshot) } + fn read_as_snapshot(&self) -> StateSnapshot { + self.snapshot.clone() + } + fn clear(&mut self) { std::mem::take(&mut self.snapshot); self.local.clear() diff --git a/crates/anvil/src/eth/backend/mem/in_memory_db.rs b/crates/anvil/src/eth/backend/mem/in_memory_db.rs index 2abee72ef..56cd3815c 100644 --- a/crates/anvil/src/eth/backend/mem/in_memory_db.rs +++ b/crates/anvil/src/eth/backend/mem/in_memory_db.rs @@ -3,7 +3,7 @@ use crate::{ eth::backend::db::{ Db, MaybeForkedDatabase, MaybeFullDatabase, SerializableAccountRecord, SerializableBlock, - SerializableState, SerializableTransaction, StateDb, + SerializableHistoricalStates, SerializableState, SerializableTransaction, StateDb, }, mem::state::state_root, revm::{db::DbAccount, primitives::AccountInfo}, @@ -38,6 +38,7 @@ impl Db for MemDb { best_number: U64, blocks: Vec, transactions: Vec, + historical_states: Option, ) -> DatabaseResult> { let accounts = self .inner @@ -68,6 +69,7 @@ impl Db for MemDb { best_block_number: Some(best_number), blocks, transactions, + historical_states, })) } @@ -110,6 +112,10 @@ impl MaybeFullDatabase for MemDb { self.inner.clear_into_snapshot() } + fn read_as_snapshot(&self) -> StateSnapshot { + self.inner.read_as_snapshot() + } + fn clear(&mut self) { self.inner.clear(); } @@ -165,7 +171,7 @@ mod tests { // blocks dumping/loading tested in storage.rs let state = dump_db - .dump_state(Default::default(), U64::ZERO, Vec::new(), Vec::new()) + .dump_state(Default::default(), U64::ZERO, Vec::new(), Vec::new(), Default::default()) .unwrap() .unwrap(); diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 6a0aeadee..5e71892a3 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -758,12 +758,27 @@ impl Backend { } /// Get the current state. - pub async fn serialized_state(&self) -> Result { + pub async fn serialized_state( + &self, + preserve_historical_states: bool, + ) -> Result { let at = self.env.read().block.clone(); let best_number = self.blockchain.storage.read().best_number; let blocks = self.blockchain.storage.read().serialized_blocks(); let transactions = self.blockchain.storage.read().serialized_transactions(); - let state = self.db.read().await.dump_state(at, best_number, blocks, transactions)?; + let historical_states = if preserve_historical_states { + Some(self.states.write().serialized_states()) + } else { + None + }; + + let state = self.db.read().await.dump_state( + at, + best_number, + blocks, + transactions, + historical_states, + )?; state.ok_or_else(|| { RpcError::invalid_params("Dumping state not supported with the current configuration") .into() @@ -771,8 +786,11 @@ impl Backend { } /// Write all chain data to serialized bytes buffer - pub async fn dump_state(&self) -> Result { - let state = self.serialized_state().await?; + pub async fn dump_state( + &self, + preserve_historical_states: bool, + ) -> Result { + let state = self.serialized_state(preserve_historical_states).await?; let mut encoder = GzEncoder::new(Vec::new(), Compression::default()); encoder .write_all(&serde_json::to_vec(&state).unwrap_or_default()) @@ -802,6 +820,10 @@ impl Backend { self.blockchain.storage.write().load_blocks(state.blocks.clone()); self.blockchain.storage.write().load_transactions(state.transactions.clone()); + if let Some(historical_states) = state.historical_states { + self.states.write().load_states(historical_states); + } + Ok(true) } diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 2b5886a6f..f999a512a 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -1,7 +1,10 @@ //! In-memory blockchain storage use crate::eth::{ backend::{ - db::{MaybeFullDatabase, SerializableBlock, SerializableTransaction, StateDb}, + db::{ + MaybeFullDatabase, SerializableBlock, SerializableHistoricalStates, + SerializableTransaction, StateDb, + }, mem::cache::DiskStateCache, }, error::BlockchainError, @@ -25,6 +28,7 @@ use anvil_core::eth::{ }; use anvil_rpc::error::RpcError; use foundry_evm::{ + backend::MemDb, revm::primitives::Env, traces::{ CallKind, FourByteInspector, GethTraceBuilder, ParityTraceBuilder, TracingInspectorConfig, @@ -188,6 +192,34 @@ impl InMemoryBlockStates { self.disk_cache.remove(on_disk) } } + + /// Serialize all states to a list of serializable historical states + pub fn serialized_states(&mut self) -> SerializableHistoricalStates { + // Get in-memory states + let mut states = self + .states + .iter_mut() + .map(|(hash, state)| (*hash, state.serialize_state())) + .collect::>(); + + // Get on-disk state snapshots + self.on_disk_states.iter().for_each(|(hash, _)| { + if let Some(snapshot) = self.disk_cache.read(*hash) { + states.push((*hash, snapshot)); + } + }); + + SerializableHistoricalStates::new(states) + } + + /// Load states from serialized data + pub fn load_states(&mut self, states: SerializableHistoricalStates) { + for (hash, snapshot) in states { + let mut state_db = StateDb::new(MemDb::default()); + state_db.init_from_snapshot(snapshot); + self.insert(hash, state_db); + } + } } impl fmt::Debug for InMemoryBlockStates { diff --git a/crates/anvil/tests/it/state.rs b/crates/anvil/tests/it/state.rs index 4e96ab0f1..7ed11ca46 100644 --- a/crates/anvil/tests/it/state.rs +++ b/crates/anvil/tests/it/state.rs @@ -1,6 +1,9 @@ //! general eth api tests -use alloy_primitives::Uint; +use crate::abi::Greeter; +use alloy_primitives::{Bytes, Uint}; +use alloy_provider::Provider; +use alloy_rpc_types::BlockId; use anvil::{spawn, NodeConfig}; #[tokio::test(flavor = "multi_thread")] @@ -14,7 +17,7 @@ async fn can_load_state() { let num = api.block_number().unwrap(); - let state = api.serialized_state().await.unwrap(); + let state = api.serialized_state(false).await.unwrap(); foundry_common::fs::write_json_file(&state_file, &state).unwrap(); let (api, _handle) = spawn(NodeConfig::test().with_init_state_path(state_file)).await; @@ -42,3 +45,90 @@ async fn can_load_existing_state() { let block_number = api.block_number().unwrap(); assert_eq!(block_number, Uint::from(2)); } + +#[tokio::test(flavor = "multi_thread")] +async fn test_make_sure_historical_state_is_not_cleared_on_dump() { + let tmp = tempfile::tempdir().unwrap(); + let state_file = tmp.path().join("state.json"); + + let (api, handle) = spawn(NodeConfig::test()).await; + + let provider = handle.http_provider(); + + let greeter = Greeter::deploy(&provider, "Hello".to_string()).await.unwrap(); + + let address = greeter.address(); + + let _tx = greeter + .setGreeting("World!".to_string()) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + api.mine_one().await; + + let ser_state = api.serialized_state(true).await.unwrap(); + foundry_common::fs::write_json_file(&state_file, &ser_state).unwrap(); + + let block_number = api.block_number().unwrap(); + assert_eq!(block_number, Uint::from(3)); + + // Makes sure historical states of the new instance are not cleared. + let code = provider.get_code_at(*address).block_id(BlockId::number(2)).await.unwrap(); + + assert_ne!(code, Bytes::new()); +} + +#[tokio::test(flavor = "multi_thread")] +async fn can_preserve_historical_states_between_dump_and_load() { + let tmp = tempfile::tempdir().unwrap(); + let state_file = tmp.path().join("state.json"); + + let (api, handle) = spawn(NodeConfig::test()).await; + + let provider = handle.http_provider(); + + let greeter = Greeter::deploy(&provider, "Hello".to_string()).await.unwrap(); + + let address = greeter.address(); + + let deploy_blk_num = provider.get_block_number().await.unwrap(); + + let tx = greeter + .setGreeting("World!".to_string()) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + let change_greeting_blk_num = tx.block_number.unwrap(); + + api.mine_one().await; + + let ser_state = api.serialized_state(true).await.unwrap(); + foundry_common::fs::write_json_file(&state_file, &ser_state).unwrap(); + + let (api, handle) = spawn(NodeConfig::test().with_init_state_path(state_file)).await; + + let block_number = api.block_number().unwrap(); + assert_eq!(block_number, Uint::from(3)); + + let provider = handle.http_provider(); + + let greeter = Greeter::new(*address, provider); + + let greeting_at_init = + greeter.greet().block(BlockId::number(deploy_blk_num)).call().await.unwrap()._0; + + assert_eq!(greeting_at_init, "Hello"); + + let greeting_after_change = + greeter.greet().block(BlockId::number(change_greeting_blk_num)).call().await.unwrap()._0; + + assert_eq!(greeting_after_change, "World!"); +} From 81349edd7b1d35df77a7e12ab955b237c1a33171 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 16 Sep 2024 15:50:00 +0200 Subject: [PATCH 170/184] docs(dev): update cheatcode docs (#8872) --- crates/cheatcodes/src/lib.rs | 3 ++- docs/dev/cheatcodes.md | 28 ++++++++-------------------- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index 4bd0b22a3..4a06ff454 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -80,8 +80,9 @@ pub(crate) trait Cheatcode: CheatcodeDef + DynCheatcode { fn apply_full( &self, ccx: &mut CheatsCtxt, - _executor: &mut E, + executor: &mut E, ) -> Result { + let _ = executor; self.apply_stateful(ccx) } } diff --git a/docs/dev/cheatcodes.md b/docs/dev/cheatcodes.md index 9ca0368d3..1e95bf7f2 100644 --- a/docs/dev/cheatcodes.md +++ b/docs/dev/cheatcodes.md @@ -6,22 +6,14 @@ manipulate the environment in which the execution is run. Most of the time, simply testing your smart contracts outputs isn't enough. To manipulate the state of the EVM, as well as test for specific reverts and events, Foundry is shipped with a set of cheatcodes. -## [`revm::Inspector`](https://docs.rs/revm/3.3.0/revm/trait.Inspector.html) +## [`revm::Inspector`](https://docs.rs/revm/latest/revm/trait.Inspector.html) -To understand how cheatcodes are implemented, we first need to look at [`revm::Inspector`](https://docs.rs/revm/3.3.0/revm/trait.Inspector.html), +To understand how cheatcodes are implemented, we first need to look at [`revm::Inspector`](https://docs.rs/revm/latest/revm/trait.Inspector.html), a trait that provides a set of callbacks to be notified at certain stages of EVM execution. -For example, [`Inspector::call`](https://docs.rs/revm/3.3.0/revm/trait.Inspector.html#method.call) -is called when the EVM is about to execute a call: - -```rust -fn call( - &mut self, - data: &mut EVMData<'_, DB>, - inputs: &mut CallInputs, - is_static: bool, -) -> (InstructionResult, Gas, Bytes) { ... } -``` +For example, [`Inspector::call`](https://docs.rs/revm/latest/revm/trait.Inspector.html#method.call) +is called when the EVM is about to execute a call and is provided with the call's inputs and the +current state of the EVM. ## [Foundry inspectors](../../crates/evm/evm/src/inspectors/) @@ -45,12 +37,7 @@ Since cheatcodes are bound to a constant address, the cheatcode inspector listen ```rust impl Inspector for Cheatcodes { - fn call( - &mut self, - data: &mut EVMData<'_, DB>, - call: &mut CallInputs, - is_static: bool, - ) -> (Return, Gas, Bytes) { + fn call(&mut self, ...) -> ... { if call.contract == CHEATCODE_ADDRESS { // intercepted cheatcode call // --snip-- @@ -141,7 +128,8 @@ Multiple attributes can be specified by separating them with commas, e.g. `#[che This trait defines the interface that all cheatcode implementations must implement. There are two methods that can be implemented: - `apply`: implemented when the cheatcode is pure and does not need to access EVM data -- `apply_full`: implemented when the cheatcode needs to access EVM data +- `apply_stateful`: implemented when the cheatcode needs to access EVM data +- `apply_full`: implemented when the cheatcode needs to access EVM data and the EVM executor itself, for example to recursively call back into the EVM to execute an arbitrary transaction Only one of these methods can be implemented. From 41d4e5437107f6f42c7711123890147bc736a609 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Mon, 16 Sep 2024 19:09:25 +0200 Subject: [PATCH 171/184] docs: improve `optimizer_runs` documentation to be more accurate (#8875) improve optimizer_runs documentation --- crates/cli/src/opts/build/mod.rs | 6 +++++- crates/config/src/lib.rs | 11 ++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/crates/cli/src/opts/build/mod.rs b/crates/cli/src/opts/build/mod.rs index 4ffd1c3bf..dfd5d8366 100644 --- a/crates/cli/src/opts/build/mod.rs +++ b/crates/cli/src/opts/build/mod.rs @@ -30,7 +30,11 @@ pub struct CompilerArgs { #[serde(skip)] pub optimize: bool, - /// The number of optimizer runs. + /// The number of runs specifies roughly how often each opcode of the deployed code will be + /// executed across the life-time of the contract. This means it is a trade-off parameter + /// between code size (deploy cost) and code execution cost (cost after deployment). + /// An `optimizer_runs` parameter of `1` will produce short but expensive code. In contrast, a + /// larger `optimizer_runs` parameter will produce longer but more gas efficient code. #[arg(long, value_name = "RUNS")] #[serde(skip_serializing_if = "Option::is_none")] pub optimizer_runs: Option, diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 34ebe62f5..d77e30492 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -212,7 +212,16 @@ pub struct Config { pub offline: bool, /// Whether to activate optimizer pub optimizer: bool, - /// Sets the optimizer runs + /// The number of runs specifies roughly how often each opcode of the deployed code will be + /// executed across the life-time of the contract. This means it is a trade-off parameter + /// between code size (deploy cost) and code execution cost (cost after deployment). + /// An `optimizer_runs` parameter of `1` will produce short but expensive code. In contrast, a + /// larger `optimizer_runs` parameter will produce longer but more gas efficient code. The + /// maximum value of the parameter is `2**32-1`. + /// + /// A common misconception is that this parameter specifies the number of iterations of the + /// optimizer. This is not true: The optimizer will always run as many times as it can + /// still improve the code. pub optimizer_runs: usize, /// Switch optimizer components on or off in detail. /// The "enabled" switch above provides two defaults which can be From 7428b6f01d1b1db4a6f031e51eda6275035d0f7d Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Tue, 17 Sep 2024 16:58:54 +0530 Subject: [PATCH 172/184] fix(`cheatcodes`): apply_stateful in mockCallRevert (#8880) * fix(`cheatcodes`): apply_stateful in mockCallRevert * nit --- crates/cheatcodes/src/evm/mock.rs | 38 +++++++++++++++++++------------ 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/crates/cheatcodes/src/evm/mock.rs b/crates/cheatcodes/src/evm/mock.rs index cd7c459b6..01a02c5c2 100644 --- a/crates/cheatcodes/src/evm/mock.rs +++ b/crates/cheatcodes/src/evm/mock.rs @@ -1,6 +1,6 @@ use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_primitives::{Address, Bytes, U256}; -use revm::{interpreter::InstructionResult, primitives::Bytecode}; +use revm::{interpreter::InstructionResult, primitives::Bytecode, InnerEvmContext}; use std::cmp::Ordering; /// Mocked call data. @@ -49,15 +49,7 @@ impl Cheatcode for clearMockedCallsCall { impl Cheatcode for mockCall_0Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { callee, data, returnData } = self; - let acc = ccx.ecx.load_account(*callee)?; - - // Etches a single byte onto the account if it is empty to circumvent the `extcodesize` - // check Solidity might perform. - let empty_bytecode = acc.info.code.as_ref().map_or(true, Bytecode::is_empty); - if empty_bytecode { - let code = Bytecode::new_raw(Bytes::from_static(&[0u8])); - ccx.ecx.journaled_state.set_code(*callee, code); - } + let _ = make_acc_non_empty(callee, ccx.ecx)?; mock_call(ccx.state, callee, data, None, returnData, InstructionResult::Return); Ok(Default::default()) @@ -74,17 +66,21 @@ impl Cheatcode for mockCall_1Call { } impl Cheatcode for mockCallRevert_0Call { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { callee, data, revertData } = self; - mock_call(state, callee, data, None, revertData, InstructionResult::Revert); + let _ = make_acc_non_empty(callee, ccx.ecx)?; + + mock_call(ccx.state, callee, data, None, revertData, InstructionResult::Revert); Ok(Default::default()) } } impl Cheatcode for mockCallRevert_1Call { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { callee, msgValue, data, revertData } = self; - mock_call(state, callee, data, Some(msgValue), revertData, InstructionResult::Revert); + let _ = make_acc_non_empty(callee, ccx.ecx)?; + + mock_call(ccx.state, callee, data, Some(msgValue), revertData, InstructionResult::Revert); Ok(Default::default()) } } @@ -112,3 +108,17 @@ fn mock_call( MockCallReturnData { ret_type, data: Bytes::copy_from_slice(rdata) }, ); } + +// Etches a single byte onto the account if it is empty to circumvent the `extcodesize` +// check Solidity might perform. +fn make_acc_non_empty(callee: &Address, ecx: &mut InnerEvmContext) -> Result { + let acc = ecx.load_account(*callee)?; + + let empty_bytecode = acc.info.code.as_ref().map_or(true, Bytecode::is_empty); + if empty_bytecode { + let code = Bytecode::new_raw(Bytes::from_static(&[0u8])); + ecx.journaled_state.set_code(*callee, code); + } + + Ok(Default::default()) +} From 604ce1d60bd81fe8a7e5e51d1424121f631caf40 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 17 Sep 2024 15:42:59 +0300 Subject: [PATCH 173/184] fix(test): increment nonce for calls too when isolate (#8878) fix(test): increment nonce for calls too when isolate (#8854) --- crates/cheatcodes/src/inspector.rs | 12 +++++------ crates/evm/evm/src/inspectors/stack.rs | 30 +------------------------- testdata/default/cheats/Nonce.t.sol | 30 ++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 35 deletions(-) create mode 100644 testdata/default/cheats/Nonce.t.sol diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 779c4ac35..b5e96eeb2 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -993,12 +993,12 @@ impl Cheatcodes { }); debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call"); - let prev = account.info.nonce; - - // Touch account to ensure that incremented nonce is committed - account.mark_touch(); - account.info.nonce += 1; - debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce"); + // Explicitly increment nonce if calls are not isolated. + if !self.config.evm_opts.isolate { + let prev = account.info.nonce; + account.info.nonce += 1; + debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce"); + } } else if broadcast.single_call { let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead"; diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 3df8dc8f0..30271910b 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -257,15 +257,8 @@ pub struct InspectorData { /// non-isolated mode. For descriptions and workarounds for those changes see: #[derive(Debug, Clone)] pub struct InnerContextData { - /// The sender of the inner EVM context. - /// It is also an origin of the transaction that created the inner EVM context. - sender: Address, - /// Nonce of the sender before invocation of the inner EVM context. - original_sender_nonce: u64, /// Origin of the transaction in the outer EVM context. original_origin: Address, - /// Whether the inner context was created by a CREATE transaction. - is_create: bool, } /// An inspector that calls multiple inspectors in sequence. @@ -490,14 +483,6 @@ impl<'a> InspectorStackRefMut<'a> { fn adjust_evm_data_for_inner_context(&mut self, ecx: &mut EvmContext) { let inner_context_data = self.inner_context_data.as_ref().expect("should be called in inner context"); - let sender_acc = ecx - .journaled_state - .state - .get_mut(&inner_context_data.sender) - .expect("failed to load sender"); - if !inner_context_data.is_create { - sender_acc.info.nonce = inner_context_data.original_sender_nonce; - } ecx.env.tx.caller = inner_context_data.original_origin; } @@ -541,13 +526,6 @@ impl<'a> InspectorStackRefMut<'a> { ecx.db.commit(ecx.journaled_state.state.clone()); - let nonce = ecx - .journaled_state - .load_account(caller, &mut ecx.db) - .expect("failed to load caller") - .info - .nonce; - let cached_env = ecx.env.clone(); ecx.env.block.basefee = U256::ZERO; @@ -555,7 +533,6 @@ impl<'a> InspectorStackRefMut<'a> { ecx.env.tx.transact_to = transact_to; ecx.env.tx.data = input; ecx.env.tx.value = value; - ecx.env.tx.nonce = Some(nonce); // Add 21000 to the gas limit to account for the base cost of transaction. ecx.env.tx.gas_limit = gas_limit + 21000; // If we haven't disabled gas limit checks, ensure that transaction gas limit will not @@ -566,12 +543,7 @@ impl<'a> InspectorStackRefMut<'a> { } ecx.env.tx.gas_price = U256::ZERO; - self.inner_context_data = Some(InnerContextData { - sender: ecx.env.tx.caller, - original_origin: cached_env.tx.caller, - original_sender_nonce: nonce, - is_create: matches!(transact_to, TxKind::Create), - }); + self.inner_context_data = Some(InnerContextData { original_origin: cached_env.tx.caller }); self.in_inner_context = true; let env = EnvWithHandlerCfg::new_with_spec_id(ecx.env.clone(), ecx.spec_id()); diff --git a/testdata/default/cheats/Nonce.t.sol b/testdata/default/cheats/Nonce.t.sol new file mode 100644 index 000000000..5dd8b0c6a --- /dev/null +++ b/testdata/default/cheats/Nonce.t.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract Counter { + uint256 public count; + + function increment() public { + count += 1; + } +} + +contract NonceIsolatedTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function testIncrementNonce() public { + address bob = address(14); + vm.startPrank(bob); + Counter counter = new Counter(); + assertEq(vm.getNonce(bob), 1); + counter.increment(); + assertEq(vm.getNonce(bob), 2); + new Counter(); + assertEq(vm.getNonce(bob), 3); + counter.increment(); + assertEq(vm.getNonce(bob), 4); + } +} From 2b390940bcfe01d066cf71b67d5ccc43f975e6d0 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 17 Sep 2024 16:33:32 +0300 Subject: [PATCH 174/184] feat(cheatcodes): expectRevert with specific reverter address (#8770) * feat(cheatcodes): expectRevert with specific reverter address * Support assert revert of the given contract at any subcall of the next call --- crates/cheatcodes/assets/cheatcodes.json | 82 +++++++++++- crates/cheatcodes/spec/src/vm.rs | 16 +++ crates/cheatcodes/src/inspector.rs | 19 ++- crates/cheatcodes/src/test/expect.rs | 121 +++++++++++++++-- crates/evm/core/src/decode.rs | 20 +++ crates/forge/tests/it/fuzz.rs | 2 +- testdata/cheats/Vm.sol | 4 + testdata/default/cheats/ExpectRevert.t.sol | 148 +++++++++++++++++++++ 8 files changed, 390 insertions(+), 22 deletions(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 567167e21..88c65137c 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -4753,7 +4753,7 @@ }, { "func": { - "id": "expectPartialRevert", + "id": "expectPartialRevert_0", "description": "Expects an error on next call that starts with the revert data.", "declaration": "function expectPartialRevert(bytes4 revertData) external;", "visibility": "external", @@ -4771,6 +4771,26 @@ "status": "stable", "safety": "unsafe" }, + { + "func": { + "id": "expectPartialRevert_1", + "description": "Expects an error on next call to reverter address, that starts with the revert data.", + "declaration": "function expectPartialRevert(bytes4 revertData, address reverter) external;", + "visibility": "external", + "mutability": "", + "signature": "expectPartialRevert(bytes4,address)", + "selector": "0x51aa008a", + "selectorBytes": [ + 81, + 170, + 0, + 138 + ] + }, + "group": "testing", + "status": "stable", + "safety": "unsafe" + }, { "func": { "id": "expectRevert_0", @@ -4831,6 +4851,66 @@ "status": "stable", "safety": "unsafe" }, + { + "func": { + "id": "expectRevert_3", + "description": "Expects an error with any revert data on next call to reverter address.", + "declaration": "function expectRevert(address reverter) external;", + "visibility": "external", + "mutability": "", + "signature": "expectRevert(address)", + "selector": "0xd814f38a", + "selectorBytes": [ + 216, + 20, + 243, + 138 + ] + }, + "group": "testing", + "status": "stable", + "safety": "unsafe" + }, + { + "func": { + "id": "expectRevert_4", + "description": "Expects an error from reverter address on next call, with any revert data.", + "declaration": "function expectRevert(bytes4 revertData, address reverter) external;", + "visibility": "external", + "mutability": "", + "signature": "expectRevert(bytes4,address)", + "selector": "0x260bc5de", + "selectorBytes": [ + 38, + 11, + 197, + 222 + ] + }, + "group": "testing", + "status": "stable", + "safety": "unsafe" + }, + { + "func": { + "id": "expectRevert_5", + "description": "Expects an error from reverter address on next call, that exactly matches the revert data.", + "declaration": "function expectRevert(bytes calldata revertData, address reverter) external;", + "visibility": "external", + "mutability": "", + "signature": "expectRevert(bytes,address)", + "selector": "0x61ebcf12", + "selectorBytes": [ + 97, + 235, + 207, + 18 + ] + }, + "group": "testing", + "status": "stable", + "safety": "unsafe" + }, { "func": { "id": "expectSafeMemory", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 49aeaa211..9c19196c8 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -812,10 +812,26 @@ interface Vm { #[cheatcode(group = Testing, safety = Unsafe)] function expectRevert(bytes calldata revertData) external; + /// Expects an error with any revert data on next call to reverter address. + #[cheatcode(group = Testing, safety = Unsafe)] + function expectRevert(address reverter) external; + + /// Expects an error from reverter address on next call, with any revert data. + #[cheatcode(group = Testing, safety = Unsafe)] + function expectRevert(bytes4 revertData, address reverter) external; + + /// Expects an error from reverter address on next call, that exactly matches the revert data. + #[cheatcode(group = Testing, safety = Unsafe)] + function expectRevert(bytes calldata revertData, address reverter) external; + /// Expects an error on next call that starts with the revert data. #[cheatcode(group = Testing, safety = Unsafe)] function expectPartialRevert(bytes4 revertData) external; + /// Expects an error on next call to reverter address, that starts with the revert data. + #[cheatcode(group = Testing, safety = Unsafe)] + function expectPartialRevert(bytes4 revertData, address reverter) external; + /// Expects an error on next cheatcode call with any revert data. #[cheatcode(group = Testing, safety = Unsafe, status = Internal)] function _expectCheatcodeRevert() external; diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index b5e96eeb2..d254a9b66 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -729,8 +729,7 @@ impl Cheatcodes { return match expect::handle_expect_revert( false, true, - expected_revert.reason.as_deref(), - expected_revert.partial_match, + &expected_revert, outcome.result.result, outcome.result.output.clone(), &self.config.available_artifacts, @@ -1234,8 +1233,17 @@ impl Inspector for Cheatcodes { } } - // Handle expected reverts - if let Some(expected_revert) = &self.expected_revert { + // Handle expected reverts. + if let Some(expected_revert) = &mut self.expected_revert { + // Record current reverter address before processing the expect revert if call reverted, + // expect revert is set with expected reverter address and no actual reverter set yet. + if outcome.result.is_revert() && + expected_revert.reverter.is_some() && + expected_revert.reverted_by.is_none() + { + expected_revert.reverted_by = Some(call.target_address); + } + if ecx.journaled_state.depth() <= expected_revert.depth { let needs_processing = match expected_revert.kind { ExpectedRevertKind::Default => !cheatcode_call, @@ -1251,8 +1259,7 @@ impl Inspector for Cheatcodes { return match expect::handle_expect_revert( cheatcode_call, false, - expected_revert.reason.as_deref(), - expected_revert.partial_match, + &expected_revert, outcome.result.result, outcome.result.output.clone(), &self.config.available_artifacts, diff --git a/crates/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs index df9c480e3..066c900b3 100644 --- a/crates/cheatcodes/src/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -80,6 +80,10 @@ pub struct ExpectedRevert { pub kind: ExpectedRevertKind, /// If true then only the first 4 bytes of expected data returned by the revert are checked. pub partial_match: bool, + /// Contract expected to revert next call. + pub reverter: Option
, + /// Actual reverter of the call. + pub reverted_by: Option
, } #[derive(Clone, Debug)] @@ -288,7 +292,7 @@ impl Cheatcode for expectEmitAnonymous_3Call { impl Cheatcode for expectRevert_0Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; - expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), false, false) + expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), false, false, None) } } @@ -301,6 +305,7 @@ impl Cheatcode for expectRevert_1Call { ccx.ecx.journaled_state.depth(), false, false, + None, ) } } @@ -308,11 +313,60 @@ impl Cheatcode for expectRevert_1Call { impl Cheatcode for expectRevert_2Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData } = self; - expect_revert(ccx.state, Some(revertData), ccx.ecx.journaled_state.depth(), false, false) + expect_revert( + ccx.state, + Some(revertData), + ccx.ecx.journaled_state.depth(), + false, + false, + None, + ) } } -impl Cheatcode for expectPartialRevertCall { +impl Cheatcode for expectRevert_3Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { reverter } = self; + expect_revert( + ccx.state, + None, + ccx.ecx.journaled_state.depth(), + false, + false, + Some(*reverter), + ) + } +} + +impl Cheatcode for expectRevert_4Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { revertData, reverter } = self; + expect_revert( + ccx.state, + Some(revertData.as_ref()), + ccx.ecx.journaled_state.depth(), + false, + false, + Some(*reverter), + ) + } +} + +impl Cheatcode for expectRevert_5Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { revertData, reverter } = self; + expect_revert( + ccx.state, + Some(revertData), + ccx.ecx.journaled_state.depth(), + false, + false, + Some(*reverter), + ) + } +} + +impl Cheatcode for expectPartialRevert_0Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData } = self; expect_revert( @@ -321,13 +375,28 @@ impl Cheatcode for expectPartialRevertCall { ccx.ecx.journaled_state.depth(), false, true, + None, + ) + } +} + +impl Cheatcode for expectPartialRevert_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { revertData, reverter } = self; + expect_revert( + ccx.state, + Some(revertData.as_ref()), + ccx.ecx.journaled_state.depth(), + false, + true, + Some(*reverter), ) } } impl Cheatcode for _expectCheatcodeRevert_0Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { - expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), true, false) + expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), true, false, None) } } @@ -340,6 +409,7 @@ impl Cheatcode for _expectCheatcodeRevert_1Call { ccx.ecx.journaled_state.depth(), true, false, + None, ) } } @@ -347,7 +417,14 @@ impl Cheatcode for _expectCheatcodeRevert_1Call { impl Cheatcode for _expectCheatcodeRevert_2Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData } = self; - expect_revert(ccx.state, Some(revertData), ccx.ecx.journaled_state.depth(), true, false) + expect_revert( + ccx.state, + Some(revertData), + ccx.ecx.journaled_state.depth(), + true, + false, + None, + ) } } @@ -581,6 +658,7 @@ fn expect_revert( depth: u64, cheatcode: bool, partial_match: bool, + reverter: Option
, ) -> Result { ensure!( state.expected_revert.is_none(), @@ -595,6 +673,8 @@ fn expect_revert( ExpectedRevertKind::Default }, partial_match, + reverter, + reverted_by: None, }); Ok(Default::default()) } @@ -602,8 +682,7 @@ fn expect_revert( pub(crate) fn handle_expect_revert( is_cheatcode: bool, is_create: bool, - expected_revert: Option<&[u8]>, - partial_match: bool, + expected_revert: &ExpectedRevert, status: InstructionResult, retdata: Bytes, known_contracts: &Option, @@ -618,19 +697,33 @@ pub(crate) fn handle_expect_revert( ensure!(!matches!(status, return_ok!()), "next call did not revert as expected"); + // If expected reverter address is set then check it matches the actual reverter. + if let (Some(expected_reverter), Some(actual_reverter)) = + (expected_revert.reverter, expected_revert.reverted_by) + { + if expected_reverter != actual_reverter { + return Err(fmt_err!( + "Reverter != expected reverter: {} != {}", + actual_reverter, + expected_reverter + )); + } + } + + let expected_reason = expected_revert.reason.as_deref(); // If None, accept any revert. - let Some(expected_revert) = expected_revert else { + let Some(expected_reason) = expected_reason else { return Ok(success_return()); }; - if !expected_revert.is_empty() && retdata.is_empty() { + if !expected_reason.is_empty() && retdata.is_empty() { bail!("call reverted as expected, but without data"); } let mut actual_revert: Vec = retdata.into(); // Compare only the first 4 bytes if partial match. - if partial_match && actual_revert.get(..4) == expected_revert.get(..4) { + if expected_revert.partial_match && actual_revert.get(..4) == expected_reason.get(..4) { return Ok(success_return()) } @@ -644,8 +737,8 @@ pub(crate) fn handle_expect_revert( } } - if actual_revert == expected_revert || - (is_cheatcode && memchr::memmem::find(&actual_revert, expected_revert).is_some()) + if actual_revert == expected_reason || + (is_cheatcode && memchr::memmem::find(&actual_revert, expected_reason).is_some()) { Ok(success_return()) } else { @@ -653,7 +746,7 @@ pub(crate) fn handle_expect_revert( let decoder = RevertDecoder::new().with_abis(contracts.iter().map(|(_, c)| &c.abi)); ( &decoder.decode(actual_revert.as_slice(), Some(status)), - &decoder.decode(expected_revert, Some(status)), + &decoder.decode(expected_reason, Some(status)), ) } else { let stringify = |data: &[u8]| { @@ -665,7 +758,7 @@ pub(crate) fn handle_expect_revert( } hex::encode_prefixed(data) }; - (&stringify(&actual_revert), &stringify(expected_revert)) + (&stringify(&actual_revert), &stringify(expected_reason)) }; Err(fmt_err!("Error != expected error: {} != {}", actual, expected,)) } diff --git a/crates/evm/core/src/decode.rs b/crates/evm/core/src/decode.rs index b777ec453..5f3e86281 100644 --- a/crates/evm/core/src/decode.rs +++ b/crates/evm/core/src/decode.rs @@ -175,11 +175,31 @@ impl RevertDecoder { let e = Vm::expectRevert_2Call::abi_decode_raw(data, false).ok()?; return self.maybe_decode(&e.revertData[..], status); } + // `expectRevert(bytes,address)` + Vm::expectRevert_5Call::SELECTOR => { + let e = Vm::expectRevert_5Call::abi_decode_raw(data, false).ok()?; + return self.maybe_decode(&e.revertData[..], status); + } // `expectRevert(bytes4)` Vm::expectRevert_1Call::SELECTOR => { let e = Vm::expectRevert_1Call::abi_decode_raw(data, false).ok()?; return self.maybe_decode(&e.revertData[..], status); } + // `expectRevert(bytes4,address)` + Vm::expectRevert_4Call::SELECTOR => { + let e = Vm::expectRevert_4Call::abi_decode_raw(data, false).ok()?; + return self.maybe_decode(&e.revertData[..], status); + } + // `expectPartialRevert(bytes4)` + Vm::expectPartialRevert_0Call::SELECTOR => { + let e = Vm::expectPartialRevert_0Call::abi_decode_raw(data, false).ok()?; + return self.maybe_decode(&e.revertData[..], status); + } + // `expectPartialRevert(bytes4,address)` + Vm::expectPartialRevert_1Call::SELECTOR => { + let e = Vm::expectPartialRevert_1Call::abi_decode_raw(data, false).ok()?; + return self.maybe_decode(&e.revertData[..], status); + } _ => {} } diff --git a/crates/forge/tests/it/fuzz.rs b/crates/forge/tests/it/fuzz.rs index e3fc31b8b..1d810c9c5 100644 --- a/crates/forge/tests/it/fuzz.rs +++ b/crates/forge/tests/it/fuzz.rs @@ -160,7 +160,7 @@ async fn test_scrape_bytecode() { let filter = Filter::new(".*", ".*", ".*fuzz/FuzzScrapeBytecode.t.sol"); let mut runner = TEST_DATA_DEFAULT.runner(); runner.test_options.fuzz.runs = 2000; - runner.test_options.fuzz.seed = Some(U256::from(6u32)); + runner.test_options.fuzz.seed = Some(U256::from(100u32)); let suite_result = runner.test_collect(&filter); assert!(!suite_result.is_empty()); diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index eacf81bda..4b7f3fe42 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -234,9 +234,13 @@ interface Vm { function expectEmit() external; function expectEmit(address emitter) external; function expectPartialRevert(bytes4 revertData) external; + function expectPartialRevert(bytes4 revertData, address reverter) external; function expectRevert() external; function expectRevert(bytes4 revertData) external; function expectRevert(bytes calldata revertData) external; + function expectRevert(address reverter) external; + function expectRevert(bytes4 revertData, address reverter) external; + function expectRevert(bytes calldata revertData, address reverter) external; function expectSafeMemory(uint64 min, uint64 max) external; function expectSafeMemoryCall(uint64 min, uint64 max) external; function fee(uint256 newBasefee) external; diff --git a/testdata/default/cheats/ExpectRevert.t.sol b/testdata/default/cheats/ExpectRevert.t.sol index 0cc6cac59..60137adfa 100644 --- a/testdata/default/cheats/ExpectRevert.t.sol +++ b/testdata/default/cheats/ExpectRevert.t.sol @@ -203,3 +203,151 @@ contract ExpectRevertTest is DSTest { new ConstructorReverter("some message"); } } + +contract AContract { + BContract bContract; + CContract cContract; + + constructor(BContract _bContract, CContract _cContract) { + bContract = _bContract; + cContract = _cContract; + } + + function callAndRevert() public pure { + require(1 > 2, "Reverted by AContract"); + } + + function callAndRevertInBContract() public { + bContract.callAndRevert(); + } + + function callAndRevertInCContract() public { + cContract.callAndRevert(); + } + + function callAndRevertInCContractThroughBContract() public { + bContract.callAndRevertInCContract(); + } + + function createDContract() public { + new DContract(); + } + + function createDContractThroughBContract() public { + bContract.createDContract(); + } + + function createDContractThroughCContract() public { + cContract.createDContract(); + } + + function doNotRevert() public {} +} + +contract BContract { + CContract cContract; + + constructor(CContract _cContract) { + cContract = _cContract; + } + + function callAndRevert() public pure { + require(1 > 2, "Reverted by BContract"); + } + + function callAndRevertInCContract() public { + this.doNotRevert(); + cContract.doNotRevert(); + cContract.callAndRevert(); + } + + function createDContract() public { + this.doNotRevert(); + cContract.doNotRevert(); + new DContract(); + } + + function createDContractThroughCContract() public { + this.doNotRevert(); + cContract.doNotRevert(); + cContract.createDContract(); + } + + function doNotRevert() public {} +} + +contract CContract { + error CContractError(string reason); + + function callAndRevert() public pure { + revert CContractError("Reverted by CContract"); + } + + function createDContract() public { + new DContract(); + } + + function doNotRevert() public {} +} + +contract DContract { + constructor() { + require(1 > 2, "Reverted by DContract"); + } +} + +contract ExpectRevertWithReverterTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + error CContractError(string reason); + + AContract aContract; + BContract bContract; + CContract cContract; + + function setUp() public { + cContract = new CContract(); + bContract = new BContract(cContract); + aContract = new AContract(bContract, cContract); + } + + function testExpectRevertsWithReverter() public { + // Test expect revert with reverter at first call. + vm.expectRevert(address(aContract)); + aContract.callAndRevert(); + // Test expect revert with reverter at second subcall. + vm.expectRevert(address(bContract)); + aContract.callAndRevertInBContract(); + // Test expect revert with partial data match and reverter at third subcall. + vm.expectPartialRevert(CContractError.selector, address(cContract)); + aContract.callAndRevertInCContractThroughBContract(); + // Test expect revert with exact data match and reverter at second subcall. + vm.expectRevert(abi.encodeWithSelector(CContractError.selector, "Reverted by CContract"), address(cContract)); + aContract.callAndRevertInCContract(); + } + + function testExpectRevertsWithReverterInConstructor() public { + // Test expect revert with reverter when constructor reverts. + vm.expectRevert(abi.encodePacked("Reverted by DContract"), address(cContract)); + cContract.createDContract(); + + vm.expectRevert(address(bContract)); + bContract.createDContract(); + vm.expectRevert(address(cContract)); + bContract.createDContractThroughCContract(); + + vm.expectRevert(address(aContract)); + aContract.createDContract(); + vm.expectRevert(address(bContract)); + aContract.createDContractThroughBContract(); + vm.expectRevert(address(cContract)); + aContract.createDContractThroughCContract(); + } + + function testFailExpectRevertsNotOnImmediateNextCall() public { + // Test expect revert with reverter fails if next call doesn't revert. + vm.expectRevert(address(aContract)); + aContract.doNotRevert(); + aContract.callAndRevert(); + } +} From b3405e2582d13f4fa476eb4d7bbf4bbeaa37ca3b Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 17 Sep 2024 20:37:48 +0200 Subject: [PATCH 175/184] ci: build release artifacts with maxperf (#8884) --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9c846ed8c..cea54177f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -133,7 +133,7 @@ jobs: run: | set -eo pipefail target="${{ matrix.target }}" - flags=(--release --bins --no-default-features --features rustls,aws-kms,cli,asm-keccak) + flags=(--profile maxperf --bins --no-default-features --features rustls,aws-kms,cli,asm-keccak) # `jemalloc` is not fully supported on MSVC or aarch64 Linux. if [[ "$target" != *msvc* && "$target" != "aarch64-unknown-linux-gnu" ]]; then From 0b13f0d60257be379d5bf6d5db3db32c439124a1 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 18 Sep 2024 09:59:08 +0200 Subject: [PATCH 176/184] ci(release): also print size (#8885) --- .github/workflows/release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cea54177f..4780a5f2d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -147,7 +147,9 @@ jobs: bins=(anvil cast chisel forge) for name in "${bins[@]}"; do bin=./target/$target/release/$name$exe + echo "" file "$bin" || true + du -h "$bin" || true ldd "$bin" || true $bin --version || true done From 8a8116977b3937ff7e871743f1805157cf242db6 Mon Sep 17 00:00:00 2001 From: dbeal Date: Wed, 18 Sep 2024 18:56:15 +0900 Subject: [PATCH 177/184] Fix failure to load some legacy state dumps (#8879) * Fix failure to load some legacy state dumps There are some cases where state dump cannot be loaded with the recent truncated hex change. https://github.com/foundry-rs/foundry/commit/84e63fe5b1d907e9d914f422e006cab8dd44b713 The existing tests for legacy state dumps were not sufficient, so I added a new test which is based on actual state dumps that are used in production right now with Synthetix. Specifically, its a state dump of the `oracle-manager` build with Cannon. This is mostly designed to be a catchall in case the basic legacy test fails. * Update db.rs * switch to using deserializer * fix lint * more fmt * clippy fix --- crates/anvil/src/eth/backend/db.rs | 36 ++++++++++++++++++- .../test-data/state-dump-legacy-stress.json | 1 + crates/anvil/tests/it/state.rs | 10 ++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 crates/anvil/test-data/state-dump-legacy-stress.json diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index 39edbd8cd..60565c4d5 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -19,7 +19,10 @@ use foundry_evm::{ Database, DatabaseCommit, }, }; -use serde::{Deserialize, Serialize}; +use serde::{ + de::{MapAccess, Visitor}, + Deserialize, Deserializer, Serialize, +}; use std::{collections::BTreeMap, fmt, path::Path}; /// Helper trait get access to the full state data of the database @@ -398,9 +401,40 @@ pub struct SerializableAccountRecord { pub nonce: u64, pub balance: U256, pub code: Bytes, + + #[serde(deserialize_with = "deserialize_btree")] pub storage: BTreeMap, } +fn deserialize_btree<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + struct BTreeVisitor; + + impl<'de> Visitor<'de> for BTreeVisitor { + type Value = BTreeMap; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a mapping of hex encoded storage slots to hex encoded state data") + } + + fn visit_map(self, mut mapping: M) -> Result, M::Error> + where + M: MapAccess<'de>, + { + let mut btree = BTreeMap::new(); + while let Some((key, value)) = mapping.next_entry::()? { + btree.insert(B256::from(key), B256::from(value)); + } + + Ok(btree) + } + } + + deserializer.deserialize_map(BTreeVisitor) +} + /// Defines a backwards-compatible enum for transactions. /// This is essential for maintaining compatibility with state dumps /// created before the changes introduced in PR #8411. diff --git a/crates/anvil/test-data/state-dump-legacy-stress.json b/crates/anvil/test-data/state-dump-legacy-stress.json new file mode 100644 index 000000000..dff50a670 --- /dev/null +++ b/crates/anvil/test-data/state-dump-legacy-stress.json @@ -0,0 +1 @@ +{"block":{"number":"0x5","coinbase":"0x0000000000000000000000000000000000000000","timestamp":"0x66b200cb","gas_limit":"0x1c9c380","basefee":"0x12e09c7a","difficulty":"0x0","prevrandao":"0xe7ef87fc7c2090741a6749a087e4ca8092cb4d07136008799e4ebeac3b69e34a","blob_excess_gas_and_price":{"excess_blob_gas":0,"blob_gasprice":1}},"accounts":{"0x0000000000000000000000000000000000000000":{"nonce":0,"balance":"0x1088aa62285a00","code":"0x","storage":{}},"0x108f53faf774d7c4c56f5bce9ca6e605ce8aeadd":{"nonce":1,"balance":"0x0","code":"0x6080604052600080357fffffffff0000000000000000000000000000000000000000000000000000000016905060008160e01c610251565b60006379ba509782101561015e5781631627540c811461009857632a952b2d81146100b457633659cfe681146100d0576350c946fe81146100ec576353a47bb781146101085763625ca21c81146101245763718fe928811461014057610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc9150610158565b738138ef7cf908021d117e542120b7a390650161079150610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc9150610158565b738138ef7cf908021d117e542120b7a390650161079150610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc9150610158565b738138ef7cf908021d117e542120b7a390650161079150610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc91505b5061024c565b816379ba509781146101a657638da5cb5b81146101c25763aaf10f4281146101de5763c7f62cda81146101fa5763daa250be81146102165763deba1b9881146102325761024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b738138ef7cf908021d117e542120b7a39065016107915061024a565b738138ef7cf908021d117e542120b7a3906501610791505b505b919050565b61025a81610037565b915050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036102ce57816040517fc2a825f50000000000000000000000000000000000000000000000000000000081526004016102c5919061032f565b60405180910390fd5b3660008037600080366000845af43d6000803e80600081146102ef573d6000f35b3d6000fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b610329816102f4565b82525050565b60006020820190506103446000830184610320565b9291505056fea264697066735822122017a4b7fdaaab3897a7b47abaed8d2ee92d558883d3bb2a8454f9601b2ab2c3db64736f6c63430008150033","storage":{}},"0x14dc79964da2c08b23698b3d3cc7ca32193d9955":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x15d34aaf54267db7d7c367839aaf71a00a2c6a65":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x19ba1fac55eea44d12a01372a8eb0c2ebbf9ca21":{"nonce":1,"balance":"0x21e19df7c2963f0ac6b","code":"0x","storage":{}},"0x19c6ab860dbe2bc433574193a4409770a8748bf6":{"nonce":1,"balance":"0x21e19df8da6b7bdc410","code":"0x","storage":{}},"0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x40567ec443c1d1872af5155755ac3803cc3fe61e":{"nonce":1,"balance":"0x21e19da82562f921b40","code":"0x","storage":{}},"0x47d08dad17ccb558b3ea74b1a0e73a9cc804a9dc":{"nonce":1,"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100885760003560e01c806379ba50971161005b57806379ba5097146100ed5780638da5cb5b146100f7578063aaf10f4214610115578063c7f62cda1461013357610088565b80631627540c1461008d5780633659cfe6146100a957806353a47bb7146100c5578063718fe928146100e3575b600080fd5b6100a760048036038101906100a29190610d25565b61014f565b005b6100c360048036038101906100be9190610d25565b6102d0565b005b6100cd6102e4565b6040516100da9190610d61565b60405180910390f35b6100eb610317565b005b6100f56103fe565b005b6100ff61058b565b60405161010c9190610d61565b60405180910390f35b61011d6105be565b60405161012a9190610d61565b60405180910390f35b61014d60048036038101906101489190610d25565b6105f1565b005b61015761084c565b600061016161081b565b9050600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036101c9576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610252576040517fa88ee57700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22826040516102c49190610d61565b60405180910390a15050565b6102d861084c565b6102e1816108c5565b50565b60006102ee61081b565b60010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600061032161081b565b90503373ffffffffffffffffffffffffffffffffffffffff168160010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146103b757336040517fa0e5a0d70000000000000000000000000000000000000000000000000000000081526004016103ae9190610d61565b60405180910390fd5b60008160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600061040861081b565b905060008160010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104a357336040517fa0e5a0d700000000000000000000000000000000000000000000000000000000815260040161049a9190610d61565b60405180910390fd5b7fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c8260000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16826040516104f8929190610d7c565b60405180910390a1808260000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008260010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b600061059561081b565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60006105c8610b05565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60006105fb610b05565b905060018160000160146101000a81548160ff02191690831515021790555060008160000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050828260000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008373ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff16633659cfe6846040516024016106cc9190610d61565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161071b9190610e16565b600060405180830381855af49150503d8060008114610756576040519150601f19603f3d011682016040523d82523d6000602084013e61075b565b606091505b505090508015806107c357508173ffffffffffffffffffffffffffffffffffffffff16610786610b05565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b156107fa576040517fa1cfa5a800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008360000160146101000a81548160ff0219169083151502179055600080fd5b60008060405160200161082d90610eb0565b6040516020818303038152906040528051906020012090508091505090565b610854610b36565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146108c357336040517f8e4a23d60000000000000000000000000000000000000000000000000000000081526004016108ba9190610d61565b60405180910390fd5b565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361092b576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61093481610b69565b61097557806040517f8a8b41ec00000000000000000000000000000000000000000000000000000000815260040161096c9190610d61565b60405180910390fd5b600061097f610b05565b90508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610a0a576040517fa88ee57700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060000160149054906101000a900460ff16158015610a2e5750610a2d82610b7c565b5b15610a7057816040517f15504301000000000000000000000000000000000000000000000000000000008152600401610a679190610d61565b60405180910390fd5b818160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503073ffffffffffffffffffffffffffffffffffffffff167f5d611f318680d00598bb735d61bacf0c514c6b50e1e5ad30040a4df2b12791c783604051610af99190610d61565b60405180910390a25050565b600080604051602001610b1790610f42565b6040516020818303038152906040528051906020012090508091505090565b6000610b4061081b565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600080823b905060008111915050919050565b60008060003073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff1663c7f62cda86604051602401610bc59190610d61565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610c149190610e16565b600060405180830381855af49150503d8060008114610c4f576040519150601f19603f3d011682016040523d82523d6000602084013e610c54565b606091505b509150915081158015610cb9575063a1cfa5a860e01b604051602001610c7a9190610faf565b6040516020818303038152906040528051906020012081604051602001610ca19190610e16565b60405160208183030381529060405280519060200120145b92505050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610cf282610cc7565b9050919050565b610d0281610ce7565b8114610d0d57600080fd5b50565b600081359050610d1f81610cf9565b92915050565b600060208284031215610d3b57610d3a610cc2565b5b6000610d4984828501610d10565b91505092915050565b610d5b81610ce7565b82525050565b6000602082019050610d766000830184610d52565b92915050565b6000604082019050610d916000830185610d52565b610d9e6020830184610d52565b9392505050565b600081519050919050565b600081905092915050565b60005b83811015610dd9578082015181840152602081019050610dbe565b60008484015250505050565b6000610df082610da5565b610dfa8185610db0565b9350610e0a818560208601610dbb565b80840191505092915050565b6000610e228284610de5565b915081905092915050565b600082825260208201905092915050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e4f776e6160008201527f626c650000000000000000000000000000000000000000000000000000000000602082015250565b6000610e9a602383610e2d565b9150610ea582610e3e565b604082019050919050565b60006020820190508181036000830152610ec981610e8d565b9050919050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e50726f7860008201527f7900000000000000000000000000000000000000000000000000000000000000602082015250565b6000610f2c602183610e2d565b9150610f3782610ed0565b604082019050919050565b60006020820190508181036000830152610f5b81610f1f565b9050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6000819050919050565b610fa9610fa482610f62565b610f8e565b82525050565b6000610fbb8284610f98565b6004820191508190509291505056fea264697066735822122023a7c33d7b91dce35ffbcf8837693364ab22a3905d0fc00016833e5fac45ca2f64736f6c63430008110033","storage":{"0x5c7865864a2a990d80b5bb5c40e7b73a029960dc711fbb56120dfab976e92ea3":"0x0"}},"0x4e59b44847b379578588920ca78fbf26c0b4956c":{"nonce":2,"balance":"0x0","code":"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3","storage":{}},"0x70997970c51812dc3a010c7d01b50e0d17dc79c8":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x8138ef7cf908021d117e542120b7a39065016107":{"nonce":1,"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100575760003560e01c80632a952b2d1461005c57806350c946fe14610085578063625ca21c146100a5578063daa250be146100c6578063deba1b98146100d9575b600080fd5b61006f61006a366004613a63565b6100ec565b60405161007c9190613a7c565b60405180910390f35b610098610093366004613a63565b61011c565b60405161007c9190613b21565b6100b86100b3366004613c92565b610276565b60405190815260200161007c565b61006f6100d4366004613d5f565b6102bb565b6100b86100e7366004613c92565b6102d6565b6100f46139e4565b6040805160008082526020820190815281830190925261011691849190610310565b92915050565b6101416040805160608101909152806000815260200160608152602001606081525090565b61014a82610ab6565b60408051606081019091528154909190829060ff16600981111561017057610170613aa7565b600981111561018157610181613aa7565b815260200160018201805461019590613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546101c190613dc2565b801561020e5780601f106101e35761010080835404028352916020019161020e565b820191906000526020600020905b8154815290600101906020018083116101f157829003601f168201915b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561026657602002820191906000526020600020905b815481526020019060010190808311610252575b5050505050815250509050919050565b600080604051806060016040528086600981111561029657610296613aa7565b81526020018581526020018481525090506102b081610ac1565b9150505b9392505050565b6102c36139e4565b6102ce848484610310565b949350505050565b60008060405180606001604052808660098111156102f6576102f6613aa7565b81526020018581526020018481525090506102b081610acc565b6103186139e4565b81518351146103a05760408051634bab873760e11b81526004810191909152600d60448201526c72756e74696d6556616c75657360981b606482015260806024820152602260848201527f6d7573742062652073616d65206c656e6774682061732072756e74696d654b6560a482015261797360f01b60c482015260e4015b60405180910390fd5b60006103ab85610c26565b805490915060ff1660018160098111156103c7576103c7613aa7565b036104755761046c6103da838787610c84565b8360010180546103e990613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461041590613dc2565b80156104625780601f1061043757610100808354040283529160200191610462565b820191906000526020600020905b81548152906001019060200180831161044557829003601f168201915b5050505050610d46565b925050506102b4565b600281600981111561048957610489613aa7565b036105305761046c61049c838787610c84565b8360010180546104ab90613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546104d790613dc2565b80156105245780601f106104f957610100808354040283529160200191610524565b820191906000526020600020905b81548152906001019060200180831161050757829003601f168201915b50505050508787610ebb565b600381600981111561054457610544613aa7565b036105de5761046c82600101805461055b90613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461058790613dc2565b80156105d45780601f106105a9576101008083540402835291602001916105d4565b820191906000526020600020905b8154815290600101906020018083116105b757829003601f168201915b5050505050610f59565b60048160098111156105f2576105f2613aa7565b0361068c5761046c82600101805461060990613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461063590613dc2565b80156106825780601f1061065757610100808354040283529160200191610682565b820191906000526020600020905b81548152906001019060200180831161066557829003601f168201915b5050505050611087565b60058160098111156106a0576106a0613aa7565b0361073a5761046c8260010180546106b790613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546106e390613dc2565b80156107305780601f1061070557610100808354040283529160200191610730565b820191906000526020600020905b81548152906001019060200180831161071357829003601f168201915b505050505061131e565b600981600981111561074e5761074e613aa7565b036107ea5761046c82600101805461076590613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461079190613dc2565b80156107de5780601f106107b3576101008083540402835291602001916107de565b820191906000526020600020905b8154815290600101906020018083116107c157829003601f168201915b505050505086866114b5565b60068160098111156107fe576107fe613aa7565b036108a35761046c610811838787610c84565b83600101805461082090613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461084c90613dc2565b80156108995780601f1061086e57610100808354040283529160200191610899565b820191906000526020600020905b81548152906001019060200180831161087c57829003601f168201915b50505050506115c7565b60078160098111156108b7576108b7613aa7565b036109ec576040805160608101909152825461046c91908490829060ff1660098111156108e6576108e6613aa7565b60098111156108f7576108f7613aa7565b815260200160018201805461090b90613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461093790613dc2565b80156109845780601f1061095957610100808354040283529160200191610984565b820191906000526020600020905b81548152906001019060200180831161096757829003601f168201915b50505050508152602001600282018054806020026020016040519081016040528092919081815260200182805480156109dc57602002820191906000526020600020905b8154815260200190600101908083116109c8575b5050505050815250508686611728565b6008816009811115610a0057610a00613aa7565b03610a9a5761046c826001018054610a1790613dc2565b80601f0160208091040260200160405190810160405280929190818152602001828054610a4390613dc2565b8015610a905780601f10610a6557610100808354040283529160200191610a90565b820191906000526020600020905b815481529060010190602001808311610a7357829003601f168201915b50505050506118a5565b6040516323a9bbc960e01b815260048101879052602401610397565b600061011682610c26565b6000610116826118ea565b6000610ad782610ac1565b9050610ae28161192a565b15610b35577fcb64985827770858ec421ad26da7e558c757541643036ce44d6b4eb9e8e5dc5e81836000015184602001518560400151604051610b289493929190613e32565b60405180910390a1919050565b610b3e82611a8c565b610b5d578160405163382bbbc960e11b81526004016103979190613b21565b60005b826040015151811015610bd957610b9383604001518281518110610b8657610b86613e6a565b602002602001015161192a565b610bd15782604001518181518110610bad57610bad613e6a565b6020026020010151604051632f19f96160e11b815260040161039791815260200190565b600101610b60565b50610be382611c31565b8351602085015160408087015190519395507fcb64985827770858ec421ad26da7e558c757541643036ce44d6b4eb9e8e5dc5e9450610b28938693929190613e32565b604080516020808201839052606082018190527f696f2e73796e7468657469782e6f7261636c652d6d616e616765722e4e6f6465608080840191909152828401949094528251808303909401845260a0909101909152815191012090565b600283015460609067ffffffffffffffff811115610ca457610ca4613b9a565b604051908082528060200260200182016040528015610cdd57816020015b610cca6139e4565b815260200190600190039081610cc25790505b50905060005b6002850154811015610d3e57610d19856002018281548110610d0757610d07613e6a565b90600052602060002001548585610310565b828281518110610d2b57610d2b613e6a565b6020908102919091010152600101610ce3565b509392505050565b610d4e6139e4565b600082806020019051810190610d649190613e80565b90506000816008811115610d7a57610d7a613aa7565b03610d9057610d8884611ca5565b915050610116565b6001816008811115610da457610da4613aa7565b03610db257610d8884611d0d565b6002816008811115610dc657610dc6613aa7565b03610dd457610d8884611d90565b6003816008811115610de857610de8613aa7565b03610df657610d8884611e13565b6004816008811115610e0a57610e0a613aa7565b03610e1857610d8884611ec9565b6005816008811115610e2c57610e2c613aa7565b03610e3a57610d8884612009565b6006816008811115610e4e57610e4e613aa7565b03610e5c57610d88846120e4565b6007816008811115610e7057610e70613aa7565b03610e7e57610d888461220c565b6008816008811115610e9257610e92613aa7565b03610ea057610d88846122ce565b80604051631be413d360e11b81526004016103979190613ea1565b610ec36139e4565b600084806020019051810190610ed99190613ed3565b604051631ecba7c360e31b81529091506001600160a01b0382169063f65d3e1890610f0e908990899089908990600401613ef0565b608060405180830381865afa158015610f2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f4f9190613f91565b9695505050505050565b610f616139e4565b600080600084806020019051810190610f7a9190613fe8565b92509250925060008390506000806000836001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015610fc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fec9190614041565b509350509250925060008660001461100f5761100a8585858a6123c7565b611011565b825b905060128660ff161161103b5761103661102f60ff881660126140a7565b82906124c2565b611053565b61105361104c601260ff89166140a7565b82906124dc565b9050604051806080016040528082815260200183815260200160008152602001600081525098505050505050505050919050565b61108f6139e4565b600080600080600080878060200190518101906110ac91906140ba565b604080516002808252606082018352979d50959b50939950919750955093506000929060208301908036833701905050905081816000815181106110f2576110f2613e6a565b602002602001019063ffffffff16908163ffffffff168152505060008160018151811061112157611121613e6a565b63ffffffff9092166020928302919091019091015260405163883bdbfd60e01b81526000906001600160a01b0385169063883bdbfd90611165908590600401614143565b600060405180830381865afa158015611182573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526111aa91908101906141f5565b5090506000816000815181106111c2576111c2613e6a565b6020026020010151826001815181106111dd576111dd613e6a565b60200260200101516111ef91906142c1565b9050600061121761120563ffffffff87166124f6565b61120f9084614304565b60060b61252d565b905060008260060b12801561124c575061123b63ffffffff8616612569565b612569565b8260060b6112499190614342565b15155b1561125f578061125b81614356565b9150505b600061126d6012600a61445d565b9050600061128061123684848f8f612593565b905060006112908a60ff16612569565b61129c8c60ff16612569565b6112a6919061446c565b905060008082136112d1576112cc6112c56112c084614493565b612686565b84906124dc565b6112e4565b6112e46112dd83612686565b84906124c2565b905060405180608001604052808281526020014281526020016000815260200160008152509e505050505050505050505050505050919050565b6113266139e4565b60008060008480602001905181019061133f91906144bf565b91945092509050826000826113bc576040516396834ad360e01b8152600481018590526001600160a01b038316906396834ad390602401608060405180830381865afa158015611393573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113b791906144f5565b611425565b604051639474f45b60e01b8152600481018590526001600160a01b03831690639474f45b90602401608060405180830381865afa158015611401573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061142591906144f5565b90506000816040015160030b601261143d919061456f565b90506000808213611467576114626114576112c084614493565b845160070b906124dc565b61147e565b61147e61147383612686565b845160070b906124c2565b9050604051806080016040528082815260200184606001518152602001600081526020016000815250975050505050505050919050565b6114bd6139e4565b6000806000868060200190518101906114d69190614597565b92509250925060005b8651811015611545578681815181106114fa576114fa613e6a565b6020026020010151717374616c656e657373546f6c6572616e636560701b0361153d5785818151811061152f5761152f613e6a565b602002602001015160001c91505b6001016114df565b5060408051600180825281830190925260009160208083019080368337019050509050828160008151811061157c5761157c613e6a565b602002602001018181525050836001838360405160200161159f939291906145ce565b60408051601f198184030181529082905263cf2cabdf60e01b82526103979291600401614603565b6115cf6139e4565b6000828060200190518101906115e59190614627565b90506000846000815181106115fc576115fc613e6a565b602002602001015160000151905060008560018151811061161f5761161f613e6a565b6020026020010151600001519050808214611702576000611653601261164d611648858761446c565b6126a9565b906124c2565b905082158061167b5750611666836126a9565b6116709082614640565b61167985612569565b125b15611700576002875111156116b0578660028151811061169d5761169d613e6a565b6020026020010151945050505050610116565b826000036116d15760405163014cc07160e01b815260040160405180910390fd5b6116da836126a9565b6116e49082614640565b60405163dcac091960e01b815260040161039791815260200190565b505b8560008151811061171557611715613e6a565b6020026020010151935050505092915050565b6117306139e4565b6000846020015180602001905181019061174a9190614627565b905060005b84518110156117bc5784818151811061176a5761176a613e6a565b6020026020010151717374616c656e657373546f6c6572616e636560701b036117b4576117ad8482815181106117a2576117a2613e6a565b602002602001015190565b91506117bc565b60010161174f565b50600085604001516000815181106117d6576117d6613e6a565b6020026020010151905060006117ed828787610310565b60208101519091506117ff84426140a7565b1161180e5792506102b4915050565b86604001515160010361187157866040015160008151811061183257611832613e6a565b602002602001015181600001518260200151604051631808066560e21b8152600401610397939291909283526020830191909152604082015260600190565b61189a876040015160018151811061188b5761188b613e6a565b60200260200101518787610310565b979650505050505050565b6118ad6139e4565b6040518060800160405280838060200190518101906118cc9190614627565b81526020014281526020016000815260200160008152509050919050565b600081600001518260200151836040015160405160200161190d9392919061466e565b604051602081830303815290604052805190602001209050919050565b60008061193683610c26565b60408051606081019091528154909190829060ff16600981111561195c5761195c613aa7565b600981111561196d5761196d613aa7565b815260200160018201805461198190613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546119ad90613dc2565b80156119fa5780601f106119cf576101008083540402835291602001916119fa565b820191906000526020600020905b8154815290600101906020018083116119dd57829003601f168201915b5050505050815260200160028201805480602002602001604051908101604052809291908181526020018280548015611a5257602002820191906000526020600020905b815481526020019060010190808311611a3e575b505050505081525050905060006009811115611a7057611a70613aa7565b81516009811115611a8357611a83613aa7565b14159392505050565b6000600182516009811115611aa357611aa3613aa7565b1480611ac15750600682516009811115611abf57611abf613aa7565b145b80611ade5750600782516009811115611adc57611adc613aa7565b145b15611aee57611aec826126c1565b505b600182516009811115611b0357611b03613aa7565b03611b11576101168261284a565b600282516009811115611b2657611b26613aa7565b03611b3457610116826128a5565b600382516009811115611b4957611b49613aa7565b03611b575761011682612973565b600482516009811115611b6c57611b6c613aa7565b03611b7a5761011682612aae565b600582516009811115611b8f57611b8f613aa7565b03611b9d5761011682612e92565b600982516009811115611bb257611bb2613aa7565b03611bc05761011682612fcb565b600682516009811115611bd557611bd5613aa7565b03611be3576101168261300e565b600782516009811115611bf857611bf8613aa7565b03611c065761011682613052565b600882516009811115611c1b57611c1b613aa7565b03611c295761011682613078565b506000919050565b600080611c3d836118ea565b9050611c4881610c26565b8351815491935090839060ff19166001836009811115611c6a57611c6a613aa7565b021790555060208301516001830190611c8390826146ed565b5060408301518051611c9f916002850191602090910190613a0c565b50915091565b611cad6139e4565b60005b8251811015611d07578160200151838281518110611cd057611cd0613e6a565b6020026020010151602001511115611cff57828181518110611cf457611cf4613e6a565b602002602001015191505b600101611cb0565b50919050565b611d156139e4565b81600081518110611d2857611d28613e6a565b602002602001015190506000600190505b8251811015611d07578160000151838281518110611d5957611d59613e6a565b6020026020010151600001511215611d8857828181518110611d7d57611d7d613e6a565b602002602001015191505b600101611d39565b611d986139e4565b81600081518110611dab57611dab613e6a565b602002602001015190506000600190505b8251811015611d07578160000151838281518110611ddc57611ddc613e6a565b6020026020010151600001511315611e0b57828181518110611e0057611e00613e6a565b602002602001015191505b600101611dbc565b611e1b6139e4565b60005b8251811015611e9557828181518110611e3957611e39613e6a565b60200260200101516000015182600001818151611e56919061456f565b9052508251839082908110611e6d57611e6d613e6a565b60200260200101516020015182602001818151611e8a91906147ad565b905250600101611e1e565b50611ea08251612569565b8151611eac9190614640565b815281516020820151611ebf91906147c0565b6020820152919050565b611ed16139e4565b611eed826000611ee86001865161123691906140a7565b6130a4565b60028251611efb91906147d4565b600003611fd65760408051600280825260608201909252600091816020015b611f226139e4565b815260200190600190039081611f1a57905050905082600160028551611f4891906147c0565b611f5291906140a7565b81518110611f6257611f62613e6a565b602002602001015181600081518110611f7d57611f7d613e6a565b60200260200101819052508260028451611f9791906147c0565b81518110611fa757611fa7613e6a565b602002602001015181600181518110611fc257611fc2613e6a565b60200260200101819052506102b481611e13565b8160028351611fe591906147c0565b81518110611ff557611ff5613e6a565b60200260200101519050919050565b919050565b6120116139e4565b8160008151811061202457612024613e6a565b60209081029190910101515181528151829060009061204557612045613e6a565b6020908102919091018101518101519082015260015b82518110156120d25782818151811061207657612076613e6a565b6020026020010151600001518260000181815161209391906147e8565b90525082518390829081106120aa576120aa613e6a565b602002602001015160200151826020018181516120c791906147ad565b90525060010161205b565b5081518160200151611ebf91906147c0565b6120ec6139e4565b816000815181106120ff576120ff613e6a565b60209081029190910101515181528151829060009061212057612120613e6a565b6020908102919091018101518101519082015260015b82518110156120d25782818151811061215157612151613e6a565b60200260200101516000015160000361219e5782818151811061217657612176613e6a565b6020026020010151600001516040516338ee04a760e01b815260040161039791815260200190565b8281815181106121b0576121b0613e6a565b602002602001015160000151826000018181516121cd9190614640565b90525082518390829081106121e4576121e4613e6a565b6020026020010151602001518260200181815161220191906147ad565b905250600101612136565b6122146139e4565b8160008151811061222757612227613e6a565b60209081029190910101515181528151829060009061224857612248613e6a565b6020908102919091018101518101519082015260015b82518110156120d25761229083828151811061227c5761227c613e6a565b602090810291909101015151835190613264565b825282518390829081106122a6576122a6613e6a565b602002602001015160200151826020018181516122c391906147ad565b90525060010161225e565b6122d66139e4565b816000815181106122e9576122e9613e6a565b60209081029190910101515181528151829060009061230a5761230a613e6a565b6020908102919091018101518101519082015260015b82518110156120d25782818151811061233b5761233b613e6a565b6020026020010151600001516000036123605782818151811061217657612176613e6a565b61238983828151811061237557612375613e6a565b602090810291909101015151835190613283565b8252825183908290811061239f5761239f613e6a565b602002602001015160200151826020018181516123bc91906147ad565b905250600101612320565b6000826001826123d785426140a7565b90505b69ffffffffffffffffffff8716156124a3576001600160a01b038816639a6fc8f561240489614818565b6040516001600160e01b031960e084901b16815269ffffffffffffffffffff8216600482015290995060240160a060405180830381865afa925050508015612469575060408051601f3d908101601f1916820190925261246691810190614041565b60015b156124a357858210156124805750505050506124a3565b61248a848961456f565b97508661249681614834565b97505050505050506123da565b6124ac82612569565b6124b69084614640565b98975050505050505050565b60006124d261123683600a61484d565b6102b490846147e8565b60006124ec61123683600a61484d565b6102b49084614640565b6000667fffffffffffff66ffffffffffffff83161115612529576040516329d2678160e21b815260040160405180910390fd5b5090565b6000627fffff19600683900b128061254b5750627fffff600683900b135b1561252957604051630d962f7960e21b815260040160405180910390fd5b60006001600160ff1b038211156125295760405163677c430560e11b815260040160405180910390fd5b60008061259f86613298565b90506fffffffffffffffffffffffffffffffff6001600160a01b0382161161261c5760006125d66001600160a01b03831680614859565b9050836001600160a01b0316856001600160a01b03161061260557612600600160c01b87836136cd565b612614565b6126148187600160c01b6136cd565b92505061267d565b600061263b6001600160a01b03831680680100000000000000006136cd565b9050836001600160a01b0316856001600160a01b03161061266a57612665600160801b87836136cd565b612679565b6126798187600160801b6136cd565b9250505b50949350505050565b6000808212156125295760405163029f024d60e31b815260040160405180910390fd5b600080821215612529576126bc82614493565b610116565b6000805b8260400151518110156128415760006126fa846040015183815181106126ed576126ed613e6a565b6020026020010151610ab6565b60408051606081019091528154909190829060ff16600981111561272057612720613aa7565b600981111561273157612731613aa7565b815260200160018201805461274590613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461277190613dc2565b80156127be5780601f10612793576101008083540402835291602001916127be565b820191906000526020600020905b8154815290600101906020018083116127a157829003601f168201915b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561281657602002820191906000526020600020905b815481526020019060010190808311612802575b505050505081525050905061282a81611a8c565b612838575060009392505050565b506001016126c5565b50600192915050565b60006002826040015151101561286257506000919050565b81602001515160201461287757506000919050565b600082602001518060200190518101906128919190614627565b905060088111156128415750600092915050565b6000602082602001515110156128bd57506000919050565b600082602001518060200190518101906128d79190613ed3565b90506128ea816306e7ea3960e21b6138e2565b6128f75750600092915050565b604051633b70a5bf60e21b81526001600160a01b0382169063edc296fc90612923908690600401613b21565b6020604051808303816000875af1158015612942573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129669190614870565b6128415750600092915050565b6040810151516000901561298957506000919050565b81602001515160601461299e57506000919050565b60008083602001518060200190518101906129b99190613fe8565b92505091506000829050806001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015612a01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a259190614041565b5050505050806001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612a68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a8c919061488b565b60ff168260ff1614612aa357506000949350505050565b506001949350505050565b60408101515160009015612ac457506000919050565b81602001515160c014612ad957506000919050565b6000806000806000808760200151806020019051810190612afa91906140ba565b9550955095509550955095508360ff16866001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b48573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b6c919061488b565b60ff1614612b8257506000979650505050505050565b8260ff16856001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612bc4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612be8919061488b565b60ff1614612bfe57506000979650505050505050565b6000826001600160a01b0316630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c629190613ed3565b90506000836001600160a01b031663d21220a76040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ca4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cc89190613ed3565b9050876001600160a01b0316826001600160a01b0316148015612cfc5750866001600160a01b0316816001600160a01b0316145b158015612d385750866001600160a01b0316826001600160a01b0316148015612d365750876001600160a01b0316816001600160a01b0316145b155b15612d4d575060009998505050505050505050565b60128660ff161180612d62575060128560ff16115b15612d77575060009998505050505050505050565b8263ffffffff16600003612d95575060009998505050505050505050565b6040805160028082526060820183526000926020830190803683370190505090508381600081518110612dca57612dca613e6a565b602002602001019063ffffffff16908163ffffffff1681525050600081600181518110612df957612df9613e6a565b63ffffffff9092166020928302919091019091015260405163883bdbfd60e01b81526001600160a01b0386169063883bdbfd90612e3a908490600401614143565b600060405180830381865afa158015612e57573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612e7f91908101906141f5565b5060019c9b505050505050505050505050565b60408101515160009015612ea857506000919050565b816020015151606014612ebd57506000919050565b60008060008460200151806020019051810190612eda91906144bf565b919450925090508281612f55576040516396834ad360e01b8152600481018490526001600160a01b038216906396834ad390602401608060405180830381865afa158015612f2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f5091906144f5565b612fbe565b604051639474f45b60e01b8152600481018490526001600160a01b03821690639474f45b90602401608060405180830381865afa158015612f9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fbe91906144f5565b5060019695505050505050565b60408101515160009015612fe157506000919050565b816020015151606014612ff657506000919050565b8160200151806020019051810190612aa39190614597565b60008160400151516002148061302957508160400151516003145b61303557506000919050565b81602001515160201461304a57506000919050565b506001919050565b600081604001515160011480613029575081604001515160021461303557506000919050565b6040810151516000901561308e57506000919050565b6020826020015151101561304a57506000919050565b81818082036130b4575050505050565b6000856130da60026130c6888861446c565b6130d09190614640565b6112c0908861456f565b815181106130ea576130ea613e6a565b60200260200101516000015190505b818313613236575b808661310c85612686565b8151811061311c5761311c613e6a565b60200260200101516000015112156131405782613138816148a6565b935050613101565b8561314a83612686565b8151811061315a5761315a613e6a565b60200260200101516000015181121561317f5781613177816148be565b925050613140565b818313613231578561319083612686565b815181106131a0576131a0613e6a565b6020026020010151866131b285612686565b815181106131c2576131c2613e6a565b6020026020010151876131d486612686565b815181106131e4576131e4613e6a565b60200260200101886131f586612686565b8151811061320557613205613e6a565b602002602001018290528290525050828061321f906148a6565b935050818061322d906148be565b9250505b6130f9565b81851215613249576132498686846130a4565b8383121561325c5761325c8684866130a4565b505050505050565b6000670de0b6b3a764000061327983856147e8565b6102b49190614640565b600081613279670de0b6b3a7640000856147e8565b60008060008360020b126132b8576132b3600284900b612686565b6132c8565b6132c86112c0600285900b614493565b90506132e36112c06132dd620d89e7196148db565b60020b90565b8111156133165760405162461bcd60e51b81526020600482015260016024820152601560fa1b6044820152606401610397565b60008160011660000361332d57600160801b61333f565b6ffffcb933bd6fad37aa2d162d1a5940015b70ffffffffffffffffffffffffffffffffff169050600282161561337e576080613379826ffff97272373d413259a46990580e213a614859565b901c90505b60048216156133a85760806133a3826ffff2e50f5f656932ef12357cf3c7fdcc614859565b901c90505b60088216156133d25760806133cd826fffe5caca7e10e4e61c3624eaa0941cd0614859565b901c90505b60108216156133fc5760806133f7826fffcb9843d60f6159c9db58835c926644614859565b901c90505b6020821615613426576080613421826fff973b41fa98c081472e6896dfb254c0614859565b901c90505b604082161561345057608061344b826fff2ea16466c96a3843ec78b326b52861614859565b901c90505b608082161561347a576080613475826ffe5dee046a99a2a811c461f1969c3053614859565b901c90505b6101008216156134a55760806134a0826ffcbe86c7900a88aedcffc83b479aa3a4614859565b901c90505b6102008216156134d05760806134cb826ff987a7253ac413176f2b074cf7815e54614859565b901c90505b6104008216156134fb5760806134f6826ff3392b0822b70005940c7a398e4b70f3614859565b901c90505b610800821615613526576080613521826fe7159475a2c29b7443b29c7fa6e889d9614859565b901c90505b61100082161561355157608061354c826fd097f3bdfd2022b8845ad8f792aa5825614859565b901c90505b61200082161561357c576080613577826fa9f746462d870fdf8a65dc1f90e061e5614859565b901c90505b6140008216156135a75760806135a2826f70d869a156d2a1b890bb3df62baf32f7614859565b901c90505b6180008216156135d25760806135cd826f31be135f97d08fd981231505542fcfa6614859565b901c90505b620100008216156135fe5760806135f9826f09aa508b5b7a84e1c677de54f3e99bc9614859565b901c90505b62020000821615613629576080613624826e5d6af8dedb81196699c329225ee604614859565b901c90505b6204000082161561365357608061364e826d2216e584f5fa1ea926041bedfe98614859565b901c90505b6208000082161561367b576080613676826b048a170391f7dc42444e8fa2614859565b901c90505b60008460020b131561369657613693816000196147c0565b90505b6102ce6136a8640100000000836147d4565b156136b45760016136b7565b60005b6136c89060ff16602084901c6147ad565b6139ba565b6000808060001985870985870292508281108382030391505080600003613749576000841161373e5760405162461bcd60e51b815260206004820152601960248201527f48616e646c65206e6f6e2d6f766572666c6f77206361736573000000000000006044820152606401610397565b5082900490506102b4565b8084116137985760405162461bcd60e51b815260206004820152601960248201527f70726576656e74732064656e6f6d696e61746f72203d3d2030000000000000006044820152606401610397565b60008486880980840393811190920391905060006137d06137b887612569565b6137c188612569565b6137ca90614493565b16612686565b9586900495938490049360008190030460010190506137ef8184614859565b909317926000613800876003614859565b600218905061380f8188614859565b61381a9060026140a7565b6138249082614859565b90506138308188614859565b61383b9060026140a7565b6138459082614859565b90506138518188614859565b61385c9060026140a7565b6138669082614859565b90506138728188614859565b61387d9060026140a7565b6138879082614859565b90506138938188614859565b61389e9060026140a7565b6138a89082614859565b90506138b48188614859565b6138bf9060026140a7565b6138c99082614859565b90506138d58186614859565b9998505050505050505050565b604080516001600160e01b0319831660248083019190915282518083039091018152604490910182526020810180516001600160e01b03166301ffc9a760e01b1790529051600091829182916001600160a01b0387169161394391906148fd565b6000604051808303816000865af19150503d8060008114613980576040519150601f19603f3d011682016040523d82523d6000602084013e613985565b606091505b50915091508161399a57600092505050610116565b80516000036139ae57600092505050610116565b60200151949350505050565b60006001600160a01b038211156125295760405163dccde8ed60e01b815260040160405180910390fd5b6040518060800160405280600081526020016000815260200160008152602001600081525090565b828054828255906000526020600020908101928215613a47579160200282015b82811115613a47578251825591602001919060010190613a2c565b506125299291505b808211156125295760008155600101613a4f565b600060208284031215613a7557600080fd5b5035919050565b8151815260208083015190820152604080830151908201526060808301519082015260808101610116565b634e487b7160e01b600052602160045260246000fd5b600a8110613acd57613acd613aa7565b9052565b60005b83811015613aec578181015183820152602001613ad4565b50506000910152565b60008151808452613b0d816020860160208601613ad1565b601f01601f19169290920160200192915050565b60006020808352613b358184018551613abd565b8084015160606040850152613b4d6080850182613af5565b6040860151858203601f19016060870152805180835290840192506000918401905b80831015613b8f5783518252928401926001929092019190840190613b6f565b509695505050505050565b634e487b7160e01b600052604160045260246000fd5b6040516080810167ffffffffffffffff81118282101715613bd357613bd3613b9a565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715613c0257613c02613b9a565b604052919050565b600067ffffffffffffffff821115613c2457613c24613b9a565b5060051b60200190565b600082601f830112613c3f57600080fd5b81356020613c54613c4f83613c0a565b613bd9565b8083825260208201915060208460051b870101935086841115613c7657600080fd5b602086015b84811015613b8f5780358352918301918301613c7b565b600080600060608486031215613ca757600080fd5b8335600a8110613cb657600080fd5b925060208481013567ffffffffffffffff80821115613cd457600080fd5b818701915087601f830112613ce857600080fd5b813581811115613cfa57613cfa613b9a565b613d0c601f8201601f19168501613bd9565b8181528985838601011115613d2057600080fd5b818585018683013760009181019094015291935060408601359180831115613d4757600080fd5b5050613d5586828701613c2e565b9150509250925092565b600080600060608486031215613d7457600080fd5b83359250602084013567ffffffffffffffff80821115613d9357600080fd5b613d9f87838801613c2e565b93506040860135915080821115613db557600080fd5b50613d5586828701613c2e565b600181811c90821680613dd657607f821691505b602082108103611d0757634e487b7160e01b600052602260045260246000fd5b60008151808452602080850194506020840160005b83811015613e2757815187529582019590820190600101613e0b565b509495945050505050565b848152613e426020820185613abd565b608060408201526000613e586080830185613af5565b828103606084015261189a8185613df6565b634e487b7160e01b600052603260045260246000fd5b600060208284031215613e9257600080fd5b8151600981106102b457600080fd5b6020810160098310613eb557613eb5613aa7565b91905290565b6001600160a01b0381168114613ed057600080fd5b50565b600060208284031215613ee557600080fd5b81516102b481613ebb565b608080825285518282018190526000919060209060a0850190828a01855b82811015613f5257613f42848351805182526020810151602083015260408101516040830152606081015160608301525050565b9285019290840190600101613f0e565b5050508481036020860152613f678189613af5565b925050508281036040840152613f7d8186613df6565b9050828103606084015261189a8185613df6565b600060808284031215613fa357600080fd5b613fab613bb0565b825181526020830151602082015260408301516040820152606083015160608201528091505092915050565b805160ff8116811461200457600080fd5b600080600060608486031215613ffd57600080fd5b835161400881613ebb565b6020850151909350915061401e60408501613fd7565b90509250925092565b805169ffffffffffffffffffff8116811461200457600080fd5b600080600080600060a0868803121561405957600080fd5b61406286614027565b945060208601519350604086015192506060860151915061408560808701614027565b90509295509295909350565b634e487b7160e01b600052601160045260246000fd5b8181038181111561011657610116614091565b60008060008060008060c087890312156140d357600080fd5b86516140de81613ebb565b60208801519096506140ef81613ebb565b94506140fd60408801613fd7565b935061410b60608801613fd7565b9250608087015161411b81613ebb565b60a088015190925063ffffffff8116811461413557600080fd5b809150509295509295509295565b6020808252825182820181905260009190848201906040850190845b8181101561418157835163ffffffff168352928401929184019160010161415f565b50909695505050505050565b600082601f83011261419e57600080fd5b815160206141ae613c4f83613c0a565b8083825260208201915060208460051b8701019350868411156141d057600080fd5b602086015b84811015613b8f5780516141e881613ebb565b83529183019183016141d5565b6000806040838503121561420857600080fd5b825167ffffffffffffffff8082111561422057600080fd5b818501915085601f83011261423457600080fd5b81516020614244613c4f83613c0a565b82815260059290921b8401810191818101908984111561426357600080fd5b948201945b838610156142915785518060060b81146142825760008081fd5b82529482019490820190614268565b918801519196509093505050808211156142aa57600080fd5b506142b78582860161418d565b9150509250929050565b600682810b9082900b03667fffffffffffff198112667fffffffffffff8213171561011657610116614091565b634e487b7160e01b600052601260045260246000fd5b60008160060b8360060b8061431b5761431b6142ee565b667fffffffffffff1982146000198214161561433957614339614091565b90059392505050565b600082614351576143516142ee565b500790565b60008160020b627fffff19810361436f5761436f614091565b6000190192915050565b600181815b808511156143b457816000190482111561439a5761439a614091565b808516156143a757918102915b93841c939080029061437e565b509250929050565b6000826143cb57506001610116565b816143d857506000610116565b81600181146143ee57600281146143f857614414565b6001915050610116565b60ff84111561440957614409614091565b50506001821b610116565b5060208310610133831016604e8410600b8410161715614437575081810a610116565b6144418383614379565b806000190482111561445557614455614091565b029392505050565b60006102b460ff8416836143bc565b818103600083128015838313168383128216171561448c5761448c614091565b5092915050565b6000600160ff1b82016144a8576144a8614091565b5060000390565b8051801515811461200457600080fd5b6000806000606084860312156144d457600080fd5b83516144df81613ebb565b6020850151909350915061401e604085016144af565b60006080828403121561450757600080fd5b61450f613bb0565b82518060070b811461452057600080fd5b8152602083015167ffffffffffffffff8116811461453d57600080fd5b60208201526040830151600381900b811461455757600080fd5b60408201526060928301519281019290925250919050565b808201828112600083128015821682158216171561458f5761458f614091565b505092915050565b6000806000606084860312156145ac57600080fd5b83516145b781613ebb565b602085015160409095015190969495509392505050565b60ff8416815267ffffffffffffffff831660208201526060604082015260006145fa6060830184613df6565b95945050505050565b6001600160a01b03831681526040602082018190526000906102ce90830184613af5565b60006020828403121561463957600080fd5b5051919050565b60008261464f5761464f6142ee565b600160ff1b82146000198414161561466957614669614091565b500590565b6146788185613abd565b60606020820152600061468e6060830185613af5565b8281036040840152610f4f8185613df6565b601f8211156146e8576000816000526020600020601f850160051c810160208610156146c95750805b601f850160051c820191505b8181101561325c578281556001016146d5565b505050565b815167ffffffffffffffff81111561470757614707613b9a565b61471b816147158454613dc2565b846146a0565b602080601f83116001811461475057600084156147385750858301515b600019600386901b1c1916600185901b17855561325c565b600085815260208120601f198616915b8281101561477f57888601518255948401946001909101908401614760565b508582101561479d5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b8082018082111561011657610116614091565b6000826147cf576147cf6142ee565b500490565b6000826147e3576147e36142ee565b500690565b80820260008212600160ff1b8414161561480457614804614091565b818105831482151761011657610116614091565b600069ffffffffffffffffffff82168061436f5761436f614091565b60006001820161484657614846614091565b5060010190565b60006102b483836143bc565b808202811582820484141761011657610116614091565b60006020828403121561488257600080fd5b6102b4826144af565b60006020828403121561489d57600080fd5b6102b482613fd7565b60006001600160ff1b01820161484657614846614091565b6000600160ff1b82016148d3576148d3614091565b506000190190565b60008160020b627fffff1981036148f4576148f4614091565b60000392915050565b6000825161490f818460208701613ad1565b919091019291505056fea264697066735822122074f32fef384fdc296b0859f1c1f941c8e736c6cb972aa9e2b894956ebd6a80b364736f6c63430008160033","storage":{}},"0x83a0444b93927c3afcbe46e522280390f748e171":{"nonce":1,"balance":"0x0","code":"0x6080604052366100135761001161001d565b005b61001b61001d565b005b6000610027610093565b90503660008037600080366000845af43d6000803e806000811461004a573d6000f35b3d6000fd5b600080823b905060008111915050919050565b6000806040516020016100749061017a565b6040516020818303038152906040528051906020012090508091505090565b600061009d6100c6565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000806040516020016100d89061020c565b6040516020818303038152906040528051906020012090508091505090565b600082825260208201905092915050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e4f776e6160008201527f626c650000000000000000000000000000000000000000000000000000000000602082015250565b60006101646023836100f7565b915061016f82610108565b604082019050919050565b6000602082019050818103600083015261019381610157565b9050919050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e50726f7860008201527f7900000000000000000000000000000000000000000000000000000000000000602082015250565b60006101f66021836100f7565b91506102018261019a565b604082019050919050565b60006020820190508181036000830152610225816101e9565b905091905056fea2646970667358221220800da1f73cebd5e4afa07496d9bca6b6c4f526bdd3f4014ec15c70fe3a1c441364736f6c63430008110033","storage":{"0x5a648c35a2f5512218b4683cf10e03f5b7c9dc7346e1bf77d304ae97f60f592b":"0x108f53faf774d7c4c56f5bce9ca6e605ce8aeadd","0x5c7865864a2a990d80b5bb5c40e7b73a029960dc711fbb56120dfab976e92ea3":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"}},"0x90f79bf6eb2c4f870365e785982e1f101e93b906":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x976ea74026e726554db657fa54763abd0c3a0aa9":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xa0ee7a142d267c1f36714e4a8f75612f20a79720":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xc67e2bd3108604cf0168c0e5ef9cd6d78b9bb14b":{"nonce":1,"balance":"0x21e19c6edb7e2445f20","code":"0x","storage":{}},"0xeb045d78d273107348b0300c01d29b7552d622ab":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266":{"nonce":1,"balance":"0x21e19e08b86820a43ea","code":"0x","storage":{}}},"best_block_number":"0x5","blocks":[{"header":{"parentHash":"0x08abe6e453727534d8dd708843a7522b7d500338bdfe2402ca105dcdb05eebe9","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0xcd346446ed010523161f40a5f2b512def549bfb79e165b4354488738416481f2","transactionsRoot":"0xb3a4689832e0b599260ae70362ffcf224b60571b35ff8836904a3d81e2675d66","receiptsRoot":"0x2d13fdc120ab90536fed583939de7fb68b64926a306c1f629593ca9c2c93b198","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x1","gasLimit":"0x1c9c380","gasUsed":"0x3ea90d","timestamp":"0x66b200ca","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x2e0b6260","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"EIP1559":{"chainId":"0x343a","nonce":"0x0","gasLimit":"0x3ea90d","maxFeePerGas":"0x83215600","maxPriorityFeePerGas":"0x3b9aca00","value":"0x0","accessList":[],"input":"0x608060405234801561001057600080fd5b5061494f806100206000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80632a952b2d1461005c57806350c946fe14610085578063625ca21c146100a5578063daa250be146100c6578063deba1b98146100d9575b600080fd5b61006f61006a366004613a63565b6100ec565b60405161007c9190613a7c565b60405180910390f35b610098610093366004613a63565b61011c565b60405161007c9190613b21565b6100b86100b3366004613c92565b610276565b60405190815260200161007c565b61006f6100d4366004613d5f565b6102bb565b6100b86100e7366004613c92565b6102d6565b6100f46139e4565b6040805160008082526020820190815281830190925261011691849190610310565b92915050565b6101416040805160608101909152806000815260200160608152602001606081525090565b61014a82610ab6565b60408051606081019091528154909190829060ff16600981111561017057610170613aa7565b600981111561018157610181613aa7565b815260200160018201805461019590613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546101c190613dc2565b801561020e5780601f106101e35761010080835404028352916020019161020e565b820191906000526020600020905b8154815290600101906020018083116101f157829003601f168201915b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561026657602002820191906000526020600020905b815481526020019060010190808311610252575b5050505050815250509050919050565b600080604051806060016040528086600981111561029657610296613aa7565b81526020018581526020018481525090506102b081610ac1565b9150505b9392505050565b6102c36139e4565b6102ce848484610310565b949350505050565b60008060405180606001604052808660098111156102f6576102f6613aa7565b81526020018581526020018481525090506102b081610acc565b6103186139e4565b81518351146103a05760408051634bab873760e11b81526004810191909152600d60448201526c72756e74696d6556616c75657360981b606482015260806024820152602260848201527f6d7573742062652073616d65206c656e6774682061732072756e74696d654b6560a482015261797360f01b60c482015260e4015b60405180910390fd5b60006103ab85610c26565b805490915060ff1660018160098111156103c7576103c7613aa7565b036104755761046c6103da838787610c84565b8360010180546103e990613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461041590613dc2565b80156104625780601f1061043757610100808354040283529160200191610462565b820191906000526020600020905b81548152906001019060200180831161044557829003601f168201915b5050505050610d46565b925050506102b4565b600281600981111561048957610489613aa7565b036105305761046c61049c838787610c84565b8360010180546104ab90613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546104d790613dc2565b80156105245780601f106104f957610100808354040283529160200191610524565b820191906000526020600020905b81548152906001019060200180831161050757829003601f168201915b50505050508787610ebb565b600381600981111561054457610544613aa7565b036105de5761046c82600101805461055b90613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461058790613dc2565b80156105d45780601f106105a9576101008083540402835291602001916105d4565b820191906000526020600020905b8154815290600101906020018083116105b757829003601f168201915b5050505050610f59565b60048160098111156105f2576105f2613aa7565b0361068c5761046c82600101805461060990613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461063590613dc2565b80156106825780601f1061065757610100808354040283529160200191610682565b820191906000526020600020905b81548152906001019060200180831161066557829003601f168201915b5050505050611087565b60058160098111156106a0576106a0613aa7565b0361073a5761046c8260010180546106b790613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546106e390613dc2565b80156107305780601f1061070557610100808354040283529160200191610730565b820191906000526020600020905b81548152906001019060200180831161071357829003601f168201915b505050505061131e565b600981600981111561074e5761074e613aa7565b036107ea5761046c82600101805461076590613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461079190613dc2565b80156107de5780601f106107b3576101008083540402835291602001916107de565b820191906000526020600020905b8154815290600101906020018083116107c157829003601f168201915b505050505086866114b5565b60068160098111156107fe576107fe613aa7565b036108a35761046c610811838787610c84565b83600101805461082090613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461084c90613dc2565b80156108995780601f1061086e57610100808354040283529160200191610899565b820191906000526020600020905b81548152906001019060200180831161087c57829003601f168201915b50505050506115c7565b60078160098111156108b7576108b7613aa7565b036109ec576040805160608101909152825461046c91908490829060ff1660098111156108e6576108e6613aa7565b60098111156108f7576108f7613aa7565b815260200160018201805461090b90613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461093790613dc2565b80156109845780601f1061095957610100808354040283529160200191610984565b820191906000526020600020905b81548152906001019060200180831161096757829003601f168201915b50505050508152602001600282018054806020026020016040519081016040528092919081815260200182805480156109dc57602002820191906000526020600020905b8154815260200190600101908083116109c8575b5050505050815250508686611728565b6008816009811115610a0057610a00613aa7565b03610a9a5761046c826001018054610a1790613dc2565b80601f0160208091040260200160405190810160405280929190818152602001828054610a4390613dc2565b8015610a905780601f10610a6557610100808354040283529160200191610a90565b820191906000526020600020905b815481529060010190602001808311610a7357829003601f168201915b50505050506118a5565b6040516323a9bbc960e01b815260048101879052602401610397565b600061011682610c26565b6000610116826118ea565b6000610ad782610ac1565b9050610ae28161192a565b15610b35577fcb64985827770858ec421ad26da7e558c757541643036ce44d6b4eb9e8e5dc5e81836000015184602001518560400151604051610b289493929190613e32565b60405180910390a1919050565b610b3e82611a8c565b610b5d578160405163382bbbc960e11b81526004016103979190613b21565b60005b826040015151811015610bd957610b9383604001518281518110610b8657610b86613e6a565b602002602001015161192a565b610bd15782604001518181518110610bad57610bad613e6a565b6020026020010151604051632f19f96160e11b815260040161039791815260200190565b600101610b60565b50610be382611c31565b8351602085015160408087015190519395507fcb64985827770858ec421ad26da7e558c757541643036ce44d6b4eb9e8e5dc5e9450610b28938693929190613e32565b604080516020808201839052606082018190527f696f2e73796e7468657469782e6f7261636c652d6d616e616765722e4e6f6465608080840191909152828401949094528251808303909401845260a0909101909152815191012090565b600283015460609067ffffffffffffffff811115610ca457610ca4613b9a565b604051908082528060200260200182016040528015610cdd57816020015b610cca6139e4565b815260200190600190039081610cc25790505b50905060005b6002850154811015610d3e57610d19856002018281548110610d0757610d07613e6a565b90600052602060002001548585610310565b828281518110610d2b57610d2b613e6a565b6020908102919091010152600101610ce3565b509392505050565b610d4e6139e4565b600082806020019051810190610d649190613e80565b90506000816008811115610d7a57610d7a613aa7565b03610d9057610d8884611ca5565b915050610116565b6001816008811115610da457610da4613aa7565b03610db257610d8884611d0d565b6002816008811115610dc657610dc6613aa7565b03610dd457610d8884611d90565b6003816008811115610de857610de8613aa7565b03610df657610d8884611e13565b6004816008811115610e0a57610e0a613aa7565b03610e1857610d8884611ec9565b6005816008811115610e2c57610e2c613aa7565b03610e3a57610d8884612009565b6006816008811115610e4e57610e4e613aa7565b03610e5c57610d88846120e4565b6007816008811115610e7057610e70613aa7565b03610e7e57610d888461220c565b6008816008811115610e9257610e92613aa7565b03610ea057610d88846122ce565b80604051631be413d360e11b81526004016103979190613ea1565b610ec36139e4565b600084806020019051810190610ed99190613ed3565b604051631ecba7c360e31b81529091506001600160a01b0382169063f65d3e1890610f0e908990899089908990600401613ef0565b608060405180830381865afa158015610f2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f4f9190613f91565b9695505050505050565b610f616139e4565b600080600084806020019051810190610f7a9190613fe8565b92509250925060008390506000806000836001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015610fc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fec9190614041565b509350509250925060008660001461100f5761100a8585858a6123c7565b611011565b825b905060128660ff161161103b5761103661102f60ff881660126140a7565b82906124c2565b611053565b61105361104c601260ff89166140a7565b82906124dc565b9050604051806080016040528082815260200183815260200160008152602001600081525098505050505050505050919050565b61108f6139e4565b600080600080600080878060200190518101906110ac91906140ba565b604080516002808252606082018352979d50959b50939950919750955093506000929060208301908036833701905050905081816000815181106110f2576110f2613e6a565b602002602001019063ffffffff16908163ffffffff168152505060008160018151811061112157611121613e6a565b63ffffffff9092166020928302919091019091015260405163883bdbfd60e01b81526000906001600160a01b0385169063883bdbfd90611165908590600401614143565b600060405180830381865afa158015611182573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526111aa91908101906141f5565b5090506000816000815181106111c2576111c2613e6a565b6020026020010151826001815181106111dd576111dd613e6a565b60200260200101516111ef91906142c1565b9050600061121761120563ffffffff87166124f6565b61120f9084614304565b60060b61252d565b905060008260060b12801561124c575061123b63ffffffff8616612569565b612569565b8260060b6112499190614342565b15155b1561125f578061125b81614356565b9150505b600061126d6012600a61445d565b9050600061128061123684848f8f612593565b905060006112908a60ff16612569565b61129c8c60ff16612569565b6112a6919061446c565b905060008082136112d1576112cc6112c56112c084614493565b612686565b84906124dc565b6112e4565b6112e46112dd83612686565b84906124c2565b905060405180608001604052808281526020014281526020016000815260200160008152509e505050505050505050505050505050919050565b6113266139e4565b60008060008480602001905181019061133f91906144bf565b91945092509050826000826113bc576040516396834ad360e01b8152600481018590526001600160a01b038316906396834ad390602401608060405180830381865afa158015611393573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113b791906144f5565b611425565b604051639474f45b60e01b8152600481018590526001600160a01b03831690639474f45b90602401608060405180830381865afa158015611401573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061142591906144f5565b90506000816040015160030b601261143d919061456f565b90506000808213611467576114626114576112c084614493565b845160070b906124dc565b61147e565b61147e61147383612686565b845160070b906124c2565b9050604051806080016040528082815260200184606001518152602001600081526020016000815250975050505050505050919050565b6114bd6139e4565b6000806000868060200190518101906114d69190614597565b92509250925060005b8651811015611545578681815181106114fa576114fa613e6a565b6020026020010151717374616c656e657373546f6c6572616e636560701b0361153d5785818151811061152f5761152f613e6a565b602002602001015160001c91505b6001016114df565b5060408051600180825281830190925260009160208083019080368337019050509050828160008151811061157c5761157c613e6a565b602002602001018181525050836001838360405160200161159f939291906145ce565b60408051601f198184030181529082905263cf2cabdf60e01b82526103979291600401614603565b6115cf6139e4565b6000828060200190518101906115e59190614627565b90506000846000815181106115fc576115fc613e6a565b602002602001015160000151905060008560018151811061161f5761161f613e6a565b6020026020010151600001519050808214611702576000611653601261164d611648858761446c565b6126a9565b906124c2565b905082158061167b5750611666836126a9565b6116709082614640565b61167985612569565b125b15611700576002875111156116b0578660028151811061169d5761169d613e6a565b6020026020010151945050505050610116565b826000036116d15760405163014cc07160e01b815260040160405180910390fd5b6116da836126a9565b6116e49082614640565b60405163dcac091960e01b815260040161039791815260200190565b505b8560008151811061171557611715613e6a565b6020026020010151935050505092915050565b6117306139e4565b6000846020015180602001905181019061174a9190614627565b905060005b84518110156117bc5784818151811061176a5761176a613e6a565b6020026020010151717374616c656e657373546f6c6572616e636560701b036117b4576117ad8482815181106117a2576117a2613e6a565b602002602001015190565b91506117bc565b60010161174f565b50600085604001516000815181106117d6576117d6613e6a565b6020026020010151905060006117ed828787610310565b60208101519091506117ff84426140a7565b1161180e5792506102b4915050565b86604001515160010361187157866040015160008151811061183257611832613e6a565b602002602001015181600001518260200151604051631808066560e21b8152600401610397939291909283526020830191909152604082015260600190565b61189a876040015160018151811061188b5761188b613e6a565b60200260200101518787610310565b979650505050505050565b6118ad6139e4565b6040518060800160405280838060200190518101906118cc9190614627565b81526020014281526020016000815260200160008152509050919050565b600081600001518260200151836040015160405160200161190d9392919061466e565b604051602081830303815290604052805190602001209050919050565b60008061193683610c26565b60408051606081019091528154909190829060ff16600981111561195c5761195c613aa7565b600981111561196d5761196d613aa7565b815260200160018201805461198190613dc2565b80601f01602080910402602001604051908101604052809291908181526020018280546119ad90613dc2565b80156119fa5780601f106119cf576101008083540402835291602001916119fa565b820191906000526020600020905b8154815290600101906020018083116119dd57829003601f168201915b5050505050815260200160028201805480602002602001604051908101604052809291908181526020018280548015611a5257602002820191906000526020600020905b815481526020019060010190808311611a3e575b505050505081525050905060006009811115611a7057611a70613aa7565b81516009811115611a8357611a83613aa7565b14159392505050565b6000600182516009811115611aa357611aa3613aa7565b1480611ac15750600682516009811115611abf57611abf613aa7565b145b80611ade5750600782516009811115611adc57611adc613aa7565b145b15611aee57611aec826126c1565b505b600182516009811115611b0357611b03613aa7565b03611b11576101168261284a565b600282516009811115611b2657611b26613aa7565b03611b3457610116826128a5565b600382516009811115611b4957611b49613aa7565b03611b575761011682612973565b600482516009811115611b6c57611b6c613aa7565b03611b7a5761011682612aae565b600582516009811115611b8f57611b8f613aa7565b03611b9d5761011682612e92565b600982516009811115611bb257611bb2613aa7565b03611bc05761011682612fcb565b600682516009811115611bd557611bd5613aa7565b03611be3576101168261300e565b600782516009811115611bf857611bf8613aa7565b03611c065761011682613052565b600882516009811115611c1b57611c1b613aa7565b03611c295761011682613078565b506000919050565b600080611c3d836118ea565b9050611c4881610c26565b8351815491935090839060ff19166001836009811115611c6a57611c6a613aa7565b021790555060208301516001830190611c8390826146ed565b5060408301518051611c9f916002850191602090910190613a0c565b50915091565b611cad6139e4565b60005b8251811015611d07578160200151838281518110611cd057611cd0613e6a565b6020026020010151602001511115611cff57828181518110611cf457611cf4613e6a565b602002602001015191505b600101611cb0565b50919050565b611d156139e4565b81600081518110611d2857611d28613e6a565b602002602001015190506000600190505b8251811015611d07578160000151838281518110611d5957611d59613e6a565b6020026020010151600001511215611d8857828181518110611d7d57611d7d613e6a565b602002602001015191505b600101611d39565b611d986139e4565b81600081518110611dab57611dab613e6a565b602002602001015190506000600190505b8251811015611d07578160000151838281518110611ddc57611ddc613e6a565b6020026020010151600001511315611e0b57828181518110611e0057611e00613e6a565b602002602001015191505b600101611dbc565b611e1b6139e4565b60005b8251811015611e9557828181518110611e3957611e39613e6a565b60200260200101516000015182600001818151611e56919061456f565b9052508251839082908110611e6d57611e6d613e6a565b60200260200101516020015182602001818151611e8a91906147ad565b905250600101611e1e565b50611ea08251612569565b8151611eac9190614640565b815281516020820151611ebf91906147c0565b6020820152919050565b611ed16139e4565b611eed826000611ee86001865161123691906140a7565b6130a4565b60028251611efb91906147d4565b600003611fd65760408051600280825260608201909252600091816020015b611f226139e4565b815260200190600190039081611f1a57905050905082600160028551611f4891906147c0565b611f5291906140a7565b81518110611f6257611f62613e6a565b602002602001015181600081518110611f7d57611f7d613e6a565b60200260200101819052508260028451611f9791906147c0565b81518110611fa757611fa7613e6a565b602002602001015181600181518110611fc257611fc2613e6a565b60200260200101819052506102b481611e13565b8160028351611fe591906147c0565b81518110611ff557611ff5613e6a565b60200260200101519050919050565b919050565b6120116139e4565b8160008151811061202457612024613e6a565b60209081029190910101515181528151829060009061204557612045613e6a565b6020908102919091018101518101519082015260015b82518110156120d25782818151811061207657612076613e6a565b6020026020010151600001518260000181815161209391906147e8565b90525082518390829081106120aa576120aa613e6a565b602002602001015160200151826020018181516120c791906147ad565b90525060010161205b565b5081518160200151611ebf91906147c0565b6120ec6139e4565b816000815181106120ff576120ff613e6a565b60209081029190910101515181528151829060009061212057612120613e6a565b6020908102919091018101518101519082015260015b82518110156120d25782818151811061215157612151613e6a565b60200260200101516000015160000361219e5782818151811061217657612176613e6a565b6020026020010151600001516040516338ee04a760e01b815260040161039791815260200190565b8281815181106121b0576121b0613e6a565b602002602001015160000151826000018181516121cd9190614640565b90525082518390829081106121e4576121e4613e6a565b6020026020010151602001518260200181815161220191906147ad565b905250600101612136565b6122146139e4565b8160008151811061222757612227613e6a565b60209081029190910101515181528151829060009061224857612248613e6a565b6020908102919091018101518101519082015260015b82518110156120d25761229083828151811061227c5761227c613e6a565b602090810291909101015151835190613264565b825282518390829081106122a6576122a6613e6a565b602002602001015160200151826020018181516122c391906147ad565b90525060010161225e565b6122d66139e4565b816000815181106122e9576122e9613e6a565b60209081029190910101515181528151829060009061230a5761230a613e6a565b6020908102919091018101518101519082015260015b82518110156120d25782818151811061233b5761233b613e6a565b6020026020010151600001516000036123605782818151811061217657612176613e6a565b61238983828151811061237557612375613e6a565b602090810291909101015151835190613283565b8252825183908290811061239f5761239f613e6a565b602002602001015160200151826020018181516123bc91906147ad565b905250600101612320565b6000826001826123d785426140a7565b90505b69ffffffffffffffffffff8716156124a3576001600160a01b038816639a6fc8f561240489614818565b6040516001600160e01b031960e084901b16815269ffffffffffffffffffff8216600482015290995060240160a060405180830381865afa925050508015612469575060408051601f3d908101601f1916820190925261246691810190614041565b60015b156124a357858210156124805750505050506124a3565b61248a848961456f565b97508661249681614834565b97505050505050506123da565b6124ac82612569565b6124b69084614640565b98975050505050505050565b60006124d261123683600a61484d565b6102b490846147e8565b60006124ec61123683600a61484d565b6102b49084614640565b6000667fffffffffffff66ffffffffffffff83161115612529576040516329d2678160e21b815260040160405180910390fd5b5090565b6000627fffff19600683900b128061254b5750627fffff600683900b135b1561252957604051630d962f7960e21b815260040160405180910390fd5b60006001600160ff1b038211156125295760405163677c430560e11b815260040160405180910390fd5b60008061259f86613298565b90506fffffffffffffffffffffffffffffffff6001600160a01b0382161161261c5760006125d66001600160a01b03831680614859565b9050836001600160a01b0316856001600160a01b03161061260557612600600160c01b87836136cd565b612614565b6126148187600160c01b6136cd565b92505061267d565b600061263b6001600160a01b03831680680100000000000000006136cd565b9050836001600160a01b0316856001600160a01b03161061266a57612665600160801b87836136cd565b612679565b6126798187600160801b6136cd565b9250505b50949350505050565b6000808212156125295760405163029f024d60e31b815260040160405180910390fd5b600080821215612529576126bc82614493565b610116565b6000805b8260400151518110156128415760006126fa846040015183815181106126ed576126ed613e6a565b6020026020010151610ab6565b60408051606081019091528154909190829060ff16600981111561272057612720613aa7565b600981111561273157612731613aa7565b815260200160018201805461274590613dc2565b80601f016020809104026020016040519081016040528092919081815260200182805461277190613dc2565b80156127be5780601f10612793576101008083540402835291602001916127be565b820191906000526020600020905b8154815290600101906020018083116127a157829003601f168201915b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561281657602002820191906000526020600020905b815481526020019060010190808311612802575b505050505081525050905061282a81611a8c565b612838575060009392505050565b506001016126c5565b50600192915050565b60006002826040015151101561286257506000919050565b81602001515160201461287757506000919050565b600082602001518060200190518101906128919190614627565b905060088111156128415750600092915050565b6000602082602001515110156128bd57506000919050565b600082602001518060200190518101906128d79190613ed3565b90506128ea816306e7ea3960e21b6138e2565b6128f75750600092915050565b604051633b70a5bf60e21b81526001600160a01b0382169063edc296fc90612923908690600401613b21565b6020604051808303816000875af1158015612942573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129669190614870565b6128415750600092915050565b6040810151516000901561298957506000919050565b81602001515160601461299e57506000919050565b60008083602001518060200190518101906129b99190613fe8565b92505091506000829050806001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015612a01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a259190614041565b5050505050806001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612a68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a8c919061488b565b60ff168260ff1614612aa357506000949350505050565b506001949350505050565b60408101515160009015612ac457506000919050565b81602001515160c014612ad957506000919050565b6000806000806000808760200151806020019051810190612afa91906140ba565b9550955095509550955095508360ff16866001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b48573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b6c919061488b565b60ff1614612b8257506000979650505050505050565b8260ff16856001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612bc4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612be8919061488b565b60ff1614612bfe57506000979650505050505050565b6000826001600160a01b0316630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c629190613ed3565b90506000836001600160a01b031663d21220a76040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ca4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cc89190613ed3565b9050876001600160a01b0316826001600160a01b0316148015612cfc5750866001600160a01b0316816001600160a01b0316145b158015612d385750866001600160a01b0316826001600160a01b0316148015612d365750876001600160a01b0316816001600160a01b0316145b155b15612d4d575060009998505050505050505050565b60128660ff161180612d62575060128560ff16115b15612d77575060009998505050505050505050565b8263ffffffff16600003612d95575060009998505050505050505050565b6040805160028082526060820183526000926020830190803683370190505090508381600081518110612dca57612dca613e6a565b602002602001019063ffffffff16908163ffffffff1681525050600081600181518110612df957612df9613e6a565b63ffffffff9092166020928302919091019091015260405163883bdbfd60e01b81526001600160a01b0386169063883bdbfd90612e3a908490600401614143565b600060405180830381865afa158015612e57573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612e7f91908101906141f5565b5060019c9b505050505050505050505050565b60408101515160009015612ea857506000919050565b816020015151606014612ebd57506000919050565b60008060008460200151806020019051810190612eda91906144bf565b919450925090508281612f55576040516396834ad360e01b8152600481018490526001600160a01b038216906396834ad390602401608060405180830381865afa158015612f2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f5091906144f5565b612fbe565b604051639474f45b60e01b8152600481018490526001600160a01b03821690639474f45b90602401608060405180830381865afa158015612f9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fbe91906144f5565b5060019695505050505050565b60408101515160009015612fe157506000919050565b816020015151606014612ff657506000919050565b8160200151806020019051810190612aa39190614597565b60008160400151516002148061302957508160400151516003145b61303557506000919050565b81602001515160201461304a57506000919050565b506001919050565b600081604001515160011480613029575081604001515160021461303557506000919050565b6040810151516000901561308e57506000919050565b6020826020015151101561304a57506000919050565b81818082036130b4575050505050565b6000856130da60026130c6888861446c565b6130d09190614640565b6112c0908861456f565b815181106130ea576130ea613e6a565b60200260200101516000015190505b818313613236575b808661310c85612686565b8151811061311c5761311c613e6a565b60200260200101516000015112156131405782613138816148a6565b935050613101565b8561314a83612686565b8151811061315a5761315a613e6a565b60200260200101516000015181121561317f5781613177816148be565b925050613140565b818313613231578561319083612686565b815181106131a0576131a0613e6a565b6020026020010151866131b285612686565b815181106131c2576131c2613e6a565b6020026020010151876131d486612686565b815181106131e4576131e4613e6a565b60200260200101886131f586612686565b8151811061320557613205613e6a565b602002602001018290528290525050828061321f906148a6565b935050818061322d906148be565b9250505b6130f9565b81851215613249576132498686846130a4565b8383121561325c5761325c8684866130a4565b505050505050565b6000670de0b6b3a764000061327983856147e8565b6102b49190614640565b600081613279670de0b6b3a7640000856147e8565b60008060008360020b126132b8576132b3600284900b612686565b6132c8565b6132c86112c0600285900b614493565b90506132e36112c06132dd620d89e7196148db565b60020b90565b8111156133165760405162461bcd60e51b81526020600482015260016024820152601560fa1b6044820152606401610397565b60008160011660000361332d57600160801b61333f565b6ffffcb933bd6fad37aa2d162d1a5940015b70ffffffffffffffffffffffffffffffffff169050600282161561337e576080613379826ffff97272373d413259a46990580e213a614859565b901c90505b60048216156133a85760806133a3826ffff2e50f5f656932ef12357cf3c7fdcc614859565b901c90505b60088216156133d25760806133cd826fffe5caca7e10e4e61c3624eaa0941cd0614859565b901c90505b60108216156133fc5760806133f7826fffcb9843d60f6159c9db58835c926644614859565b901c90505b6020821615613426576080613421826fff973b41fa98c081472e6896dfb254c0614859565b901c90505b604082161561345057608061344b826fff2ea16466c96a3843ec78b326b52861614859565b901c90505b608082161561347a576080613475826ffe5dee046a99a2a811c461f1969c3053614859565b901c90505b6101008216156134a55760806134a0826ffcbe86c7900a88aedcffc83b479aa3a4614859565b901c90505b6102008216156134d05760806134cb826ff987a7253ac413176f2b074cf7815e54614859565b901c90505b6104008216156134fb5760806134f6826ff3392b0822b70005940c7a398e4b70f3614859565b901c90505b610800821615613526576080613521826fe7159475a2c29b7443b29c7fa6e889d9614859565b901c90505b61100082161561355157608061354c826fd097f3bdfd2022b8845ad8f792aa5825614859565b901c90505b61200082161561357c576080613577826fa9f746462d870fdf8a65dc1f90e061e5614859565b901c90505b6140008216156135a75760806135a2826f70d869a156d2a1b890bb3df62baf32f7614859565b901c90505b6180008216156135d25760806135cd826f31be135f97d08fd981231505542fcfa6614859565b901c90505b620100008216156135fe5760806135f9826f09aa508b5b7a84e1c677de54f3e99bc9614859565b901c90505b62020000821615613629576080613624826e5d6af8dedb81196699c329225ee604614859565b901c90505b6204000082161561365357608061364e826d2216e584f5fa1ea926041bedfe98614859565b901c90505b6208000082161561367b576080613676826b048a170391f7dc42444e8fa2614859565b901c90505b60008460020b131561369657613693816000196147c0565b90505b6102ce6136a8640100000000836147d4565b156136b45760016136b7565b60005b6136c89060ff16602084901c6147ad565b6139ba565b6000808060001985870985870292508281108382030391505080600003613749576000841161373e5760405162461bcd60e51b815260206004820152601960248201527f48616e646c65206e6f6e2d6f766572666c6f77206361736573000000000000006044820152606401610397565b5082900490506102b4565b8084116137985760405162461bcd60e51b815260206004820152601960248201527f70726576656e74732064656e6f6d696e61746f72203d3d2030000000000000006044820152606401610397565b60008486880980840393811190920391905060006137d06137b887612569565b6137c188612569565b6137ca90614493565b16612686565b9586900495938490049360008190030460010190506137ef8184614859565b909317926000613800876003614859565b600218905061380f8188614859565b61381a9060026140a7565b6138249082614859565b90506138308188614859565b61383b9060026140a7565b6138459082614859565b90506138518188614859565b61385c9060026140a7565b6138669082614859565b90506138728188614859565b61387d9060026140a7565b6138879082614859565b90506138938188614859565b61389e9060026140a7565b6138a89082614859565b90506138b48188614859565b6138bf9060026140a7565b6138c99082614859565b90506138d58186614859565b9998505050505050505050565b604080516001600160e01b0319831660248083019190915282518083039091018152604490910182526020810180516001600160e01b03166301ffc9a760e01b1790529051600091829182916001600160a01b0387169161394391906148fd565b6000604051808303816000865af19150503d8060008114613980576040519150601f19603f3d011682016040523d82523d6000602084013e613985565b606091505b50915091508161399a57600092505050610116565b80516000036139ae57600092505050610116565b60200151949350505050565b60006001600160a01b038211156125295760405163dccde8ed60e01b815260040160405180910390fd5b6040518060800160405280600081526020016000815260200160008152602001600081525090565b828054828255906000526020600020908101928215613a47579160200282015b82811115613a47578251825591602001919060010190613a2c565b506125299291505b808211156125295760008155600101613a4f565b600060208284031215613a7557600080fd5b5035919050565b8151815260208083015190820152604080830151908201526060808301519082015260808101610116565b634e487b7160e01b600052602160045260246000fd5b600a8110613acd57613acd613aa7565b9052565b60005b83811015613aec578181015183820152602001613ad4565b50506000910152565b60008151808452613b0d816020860160208601613ad1565b601f01601f19169290920160200192915050565b60006020808352613b358184018551613abd565b8084015160606040850152613b4d6080850182613af5565b6040860151858203601f19016060870152805180835290840192506000918401905b80831015613b8f5783518252928401926001929092019190840190613b6f565b509695505050505050565b634e487b7160e01b600052604160045260246000fd5b6040516080810167ffffffffffffffff81118282101715613bd357613bd3613b9a565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715613c0257613c02613b9a565b604052919050565b600067ffffffffffffffff821115613c2457613c24613b9a565b5060051b60200190565b600082601f830112613c3f57600080fd5b81356020613c54613c4f83613c0a565b613bd9565b8083825260208201915060208460051b870101935086841115613c7657600080fd5b602086015b84811015613b8f5780358352918301918301613c7b565b600080600060608486031215613ca757600080fd5b8335600a8110613cb657600080fd5b925060208481013567ffffffffffffffff80821115613cd457600080fd5b818701915087601f830112613ce857600080fd5b813581811115613cfa57613cfa613b9a565b613d0c601f8201601f19168501613bd9565b8181528985838601011115613d2057600080fd5b818585018683013760009181019094015291935060408601359180831115613d4757600080fd5b5050613d5586828701613c2e565b9150509250925092565b600080600060608486031215613d7457600080fd5b83359250602084013567ffffffffffffffff80821115613d9357600080fd5b613d9f87838801613c2e565b93506040860135915080821115613db557600080fd5b50613d5586828701613c2e565b600181811c90821680613dd657607f821691505b602082108103611d0757634e487b7160e01b600052602260045260246000fd5b60008151808452602080850194506020840160005b83811015613e2757815187529582019590820190600101613e0b565b509495945050505050565b848152613e426020820185613abd565b608060408201526000613e586080830185613af5565b828103606084015261189a8185613df6565b634e487b7160e01b600052603260045260246000fd5b600060208284031215613e9257600080fd5b8151600981106102b457600080fd5b6020810160098310613eb557613eb5613aa7565b91905290565b6001600160a01b0381168114613ed057600080fd5b50565b600060208284031215613ee557600080fd5b81516102b481613ebb565b608080825285518282018190526000919060209060a0850190828a01855b82811015613f5257613f42848351805182526020810151602083015260408101516040830152606081015160608301525050565b9285019290840190600101613f0e565b5050508481036020860152613f678189613af5565b925050508281036040840152613f7d8186613df6565b9050828103606084015261189a8185613df6565b600060808284031215613fa357600080fd5b613fab613bb0565b825181526020830151602082015260408301516040820152606083015160608201528091505092915050565b805160ff8116811461200457600080fd5b600080600060608486031215613ffd57600080fd5b835161400881613ebb565b6020850151909350915061401e60408501613fd7565b90509250925092565b805169ffffffffffffffffffff8116811461200457600080fd5b600080600080600060a0868803121561405957600080fd5b61406286614027565b945060208601519350604086015192506060860151915061408560808701614027565b90509295509295909350565b634e487b7160e01b600052601160045260246000fd5b8181038181111561011657610116614091565b60008060008060008060c087890312156140d357600080fd5b86516140de81613ebb565b60208801519096506140ef81613ebb565b94506140fd60408801613fd7565b935061410b60608801613fd7565b9250608087015161411b81613ebb565b60a088015190925063ffffffff8116811461413557600080fd5b809150509295509295509295565b6020808252825182820181905260009190848201906040850190845b8181101561418157835163ffffffff168352928401929184019160010161415f565b50909695505050505050565b600082601f83011261419e57600080fd5b815160206141ae613c4f83613c0a565b8083825260208201915060208460051b8701019350868411156141d057600080fd5b602086015b84811015613b8f5780516141e881613ebb565b83529183019183016141d5565b6000806040838503121561420857600080fd5b825167ffffffffffffffff8082111561422057600080fd5b818501915085601f83011261423457600080fd5b81516020614244613c4f83613c0a565b82815260059290921b8401810191818101908984111561426357600080fd5b948201945b838610156142915785518060060b81146142825760008081fd5b82529482019490820190614268565b918801519196509093505050808211156142aa57600080fd5b506142b78582860161418d565b9150509250929050565b600682810b9082900b03667fffffffffffff198112667fffffffffffff8213171561011657610116614091565b634e487b7160e01b600052601260045260246000fd5b60008160060b8360060b8061431b5761431b6142ee565b667fffffffffffff1982146000198214161561433957614339614091565b90059392505050565b600082614351576143516142ee565b500790565b60008160020b627fffff19810361436f5761436f614091565b6000190192915050565b600181815b808511156143b457816000190482111561439a5761439a614091565b808516156143a757918102915b93841c939080029061437e565b509250929050565b6000826143cb57506001610116565b816143d857506000610116565b81600181146143ee57600281146143f857614414565b6001915050610116565b60ff84111561440957614409614091565b50506001821b610116565b5060208310610133831016604e8410600b8410161715614437575081810a610116565b6144418383614379565b806000190482111561445557614455614091565b029392505050565b60006102b460ff8416836143bc565b818103600083128015838313168383128216171561448c5761448c614091565b5092915050565b6000600160ff1b82016144a8576144a8614091565b5060000390565b8051801515811461200457600080fd5b6000806000606084860312156144d457600080fd5b83516144df81613ebb565b6020850151909350915061401e604085016144af565b60006080828403121561450757600080fd5b61450f613bb0565b82518060070b811461452057600080fd5b8152602083015167ffffffffffffffff8116811461453d57600080fd5b60208201526040830151600381900b811461455757600080fd5b60408201526060928301519281019290925250919050565b808201828112600083128015821682158216171561458f5761458f614091565b505092915050565b6000806000606084860312156145ac57600080fd5b83516145b781613ebb565b602085015160409095015190969495509392505050565b60ff8416815267ffffffffffffffff831660208201526060604082015260006145fa6060830184613df6565b95945050505050565b6001600160a01b03831681526040602082018190526000906102ce90830184613af5565b60006020828403121561463957600080fd5b5051919050565b60008261464f5761464f6142ee565b600160ff1b82146000198414161561466957614669614091565b500590565b6146788185613abd565b60606020820152600061468e6060830185613af5565b8281036040840152610f4f8185613df6565b601f8211156146e8576000816000526020600020601f850160051c810160208610156146c95750805b601f850160051c820191505b8181101561325c578281556001016146d5565b505050565b815167ffffffffffffffff81111561470757614707613b9a565b61471b816147158454613dc2565b846146a0565b602080601f83116001811461475057600084156147385750858301515b600019600386901b1c1916600185901b17855561325c565b600085815260208120601f198616915b8281101561477f57888601518255948401946001909101908401614760565b508582101561479d5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b8082018082111561011657610116614091565b6000826147cf576147cf6142ee565b500490565b6000826147e3576147e36142ee565b500690565b80820260008212600160ff1b8414161561480457614804614091565b818105831482151761011657610116614091565b600069ffffffffffffffffffff82168061436f5761436f614091565b60006001820161484657614846614091565b5060010190565b60006102b483836143bc565b808202811582820484141761011657610116614091565b60006020828403121561488257600080fd5b6102b4826144af565b60006020828403121561489d57600080fd5b6102b482613fd7565b60006001600160ff1b01820161484657614846614091565b6000600160ff1b82016148d3576148d3614091565b506000190190565b60008160020b627fffff1981036148f4576148f4614091565b60000392915050565b6000825161490f818460208701613ad1565b919091019291505056fea264697066735822122074f32fef384fdc296b0859f1c1f941c8e736c6cb972aa9e2b894956ebd6a80b364736f6c63430008160033","r":"0x1","s":"0x1","yParity":"0x0","hash":"0xbc73db80bf4b8784ba10a8910a0b7ef85f6846d102b41dd990969ea205335354"}}],"ommers":[]},{"header":{"parentHash":"0x026ae0c6ae91f186a9befa1ac8be30eea35e30e77de51a731085221e5cd39209","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0xb6003e7ba07a15a9e35f63daa484728ec4ceeded0c4d10ac1b04e9552d412b3c","transactionsRoot":"0x6e4969a136061ca7a390d12830d47a151585325a8d396819fb2b958ff85e9f8f","receiptsRoot":"0xc3e81df67d3e2a6c8345a954ef250cfcc41abcc2292a5aa263071124533fc9ad","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x3","gasLimit":"0x1c9c380","gasUsed":"0x3c0f6","timestamp":"0x66b200ce","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x18993a68","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"EIP1559":{"chainId":"0x343a","nonce":"0x0","gasLimit":"0x3c0f6","maxFeePerGas":"0x5d4285cd","maxPriorityFeePerGas":"0x3b9aca00","value":"0x0","accessList":[],"input":"0x608060405234801561001057600080fd5b50610380806100206000396000f3fe6080604052600080357fffffffff0000000000000000000000000000000000000000000000000000000016905060008160e01c610251565b60006379ba509782101561015e5781631627540c811461009857632a952b2d81146100b457633659cfe681146100d0576350c946fe81146100ec576353a47bb781146101085763625ca21c81146101245763718fe928811461014057610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc9150610158565b738138ef7cf908021d117e542120b7a390650161079150610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc9150610158565b738138ef7cf908021d117e542120b7a390650161079150610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc9150610158565b738138ef7cf908021d117e542120b7a390650161079150610158565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc91505b5061024c565b816379ba509781146101a657638da5cb5b81146101c25763aaf10f4281146101de5763c7f62cda81146101fa5763daa250be81146102165763deba1b9881146102325761024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b7347d08dad17ccb558b3ea74b1a0e73a9cc804a9dc915061024a565b738138ef7cf908021d117e542120b7a39065016107915061024a565b738138ef7cf908021d117e542120b7a3906501610791505b505b919050565b61025a81610037565b915050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036102ce57816040517fc2a825f50000000000000000000000000000000000000000000000000000000081526004016102c5919061032f565b60405180910390fd5b3660008037600080366000845af43d6000803e80600081146102ef573d6000f35b3d6000fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b610329816102f4565b82525050565b60006020820190506103446000830184610320565b9291505056fea264697066735822122017a4b7fdaaab3897a7b47abaed8d2ee92d558883d3bb2a8454f9601b2ab2c3db64736f6c63430008150033","r":"0x1","s":"0x1","yParity":"0x0","hash":"0x2476e039803622aeb040f924f04c493f559aed3d6c9372ab405cb33c8c695328"}}],"ommers":[]},{"header":{"parentHash":"0x3d22100ac0ee8d5cde334f7f926191a861b0648971ebc179547df28a0224c6d0","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x9511d4711e5c30a72b0bff38a261daa75dcc5ba8b772d970a5c742244b4c861b","transactionsRoot":"0xba5fff578d3d6c2cd63acbe9bca353eaa6fe22a5c408956eff49106e0a96c507","receiptsRoot":"0xbae111f01cb07677e3a8c5031546138407c01bc964d3493d732dc4edf47d36d3","logsBloom":"0x00000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000020000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000001000000000000000000000400000001000010000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x5","gasLimit":"0x1c9c380","gasUsed":"0xcae7","timestamp":"0x66b200cb","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x12e09c7a","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"EIP1559":{"chainId":"0x343a","nonce":"0x0","gasLimit":"0xcc4d","maxFeePerGas":"0x557e5ec4","maxPriorityFeePerGas":"0x3b9aca00","to":"0x83a0444b93927c3afcbe46e522280390f748e171","value":"0x0","accessList":[],"input":"0x3659cfe6000000000000000000000000108f53faf774d7c4c56f5bce9ca6e605ce8aeadd","r":"0x1","s":"0x1","yParity":"0x0","hash":"0xf88e7b19ee347145c257e0cf7ac4ecc2bae83ca79d7edaa231e71d3213aeb151"}}],"ommers":[]},{"header":{"parentHash":"0x08abe6e453727534d8dd708843a7522b7d500338bdfe2402ca105dcdb05eebe9","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x9c8eaf493f8b4edce2ba1647343eadcc0989cf461e712c0a6253ff2ca1842bb7","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x1","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66b200ca","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x3b9aca00","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0xdd07c07470e1deff3749831f0f1ad8d4b6e35505e83b3c6ea14181716197cd8a","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x29aa352e71b139e83b397bdd3dcf9b65d74770edaf3a9624d0dbc4f96f868680","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x2","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66b200cb","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x24a1ab52","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x0","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66b200c9","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x3b9aca00","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0xf6930be4847cac5017bbcbec2756eed19f36b4196526a98a88e311c296e3a9be","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x29aa352e71b139e83b397bdd3dcf9b65d74770edaf3a9624d0dbc4f96f868680","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x1","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66b200cc","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x200d75e8","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0x08abe6e453727534d8dd708843a7522b7d500338bdfe2402ca105dcdb05eebe9","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0xb6003e7ba07a15a9e35f63daa484728ec4ceeded0c4d10ac1b04e9552d412b3c","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x4","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66b200ca","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x1592fbf9","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0x149d41e3b89d8324cef3feff98ef308e97bafe8745cc8461c60172bc7d4c44ba","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x510f2275449c013534a25ad0b13c867caf720947b68bcbcd4863f7b172a5d023","transactionsRoot":"0x0b44110186e52ff0ceb6b0776ca2992c94144a4ed712eef65ea038260ef0fcc7","receiptsRoot":"0xc2823b8eb4730d9f2657137cc2ddc2c4f22ab68e0ab826236cf6a1551ca2b3a5","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x2","gasLimit":"0x1c9c380","gasUsed":"0xe61f9","timestamp":"0x66b200cb","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x342770c0","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"EIP1559":{"chainId":"0x343a","nonce":"0x0","gasLimit":"0xe94d1","maxFeePerGas":"0x83215600","maxPriorityFeePerGas":"0x3b9aca00","to":"0x4e59b44847b379578588920ca78fbf26c0b4956c","value":"0x0","accessList":[],"input":"0x4786e4342646b3ba97c1790b6cf5a55087a36240b22570f5d3a5d6bcc929d93b608060405234801561001057600080fd5b5060008061002661006d60201b61081b1760201c565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050610141565b60008060405160200161007f90610121565b6040516020818303038152906040528051906020012090508091505090565b600082825260208201905092915050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e4f776e6160008201527f626c650000000000000000000000000000000000000000000000000000000000602082015250565b600061010b60238361009e565b9150610116826100af565b604082019050919050565b6000602082019050818103600083015261013a816100fe565b9050919050565b611000806101506000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806379ba50971161005b57806379ba5097146100ed5780638da5cb5b146100f7578063aaf10f4214610115578063c7f62cda1461013357610088565b80631627540c1461008d5780633659cfe6146100a957806353a47bb7146100c5578063718fe928146100e3575b600080fd5b6100a760048036038101906100a29190610d25565b61014f565b005b6100c360048036038101906100be9190610d25565b6102d0565b005b6100cd6102e4565b6040516100da9190610d61565b60405180910390f35b6100eb610317565b005b6100f56103fe565b005b6100ff61058b565b60405161010c9190610d61565b60405180910390f35b61011d6105be565b60405161012a9190610d61565b60405180910390f35b61014d60048036038101906101489190610d25565b6105f1565b005b61015761084c565b600061016161081b565b9050600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036101c9576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610252576040517fa88ee57700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22826040516102c49190610d61565b60405180910390a15050565b6102d861084c565b6102e1816108c5565b50565b60006102ee61081b565b60010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600061032161081b565b90503373ffffffffffffffffffffffffffffffffffffffff168160010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146103b757336040517fa0e5a0d70000000000000000000000000000000000000000000000000000000081526004016103ae9190610d61565b60405180910390fd5b60008160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600061040861081b565b905060008160010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104a357336040517fa0e5a0d700000000000000000000000000000000000000000000000000000000815260040161049a9190610d61565b60405180910390fd5b7fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c8260000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16826040516104f8929190610d7c565b60405180910390a1808260000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008260010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b600061059561081b565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60006105c8610b05565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60006105fb610b05565b905060018160000160146101000a81548160ff02191690831515021790555060008160000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050828260000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008373ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff16633659cfe6846040516024016106cc9190610d61565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161071b9190610e16565b600060405180830381855af49150503d8060008114610756576040519150601f19603f3d011682016040523d82523d6000602084013e61075b565b606091505b505090508015806107c357508173ffffffffffffffffffffffffffffffffffffffff16610786610b05565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b156107fa576040517fa1cfa5a800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008360000160146101000a81548160ff0219169083151502179055600080fd5b60008060405160200161082d90610eb0565b6040516020818303038152906040528051906020012090508091505090565b610854610b36565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146108c357336040517f8e4a23d60000000000000000000000000000000000000000000000000000000081526004016108ba9190610d61565b60405180910390fd5b565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361092b576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61093481610b69565b61097557806040517f8a8b41ec00000000000000000000000000000000000000000000000000000000815260040161096c9190610d61565b60405180910390fd5b600061097f610b05565b90508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610a0a576040517fa88ee57700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060000160149054906101000a900460ff16158015610a2e5750610a2d82610b7c565b5b15610a7057816040517f15504301000000000000000000000000000000000000000000000000000000008152600401610a679190610d61565b60405180910390fd5b818160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503073ffffffffffffffffffffffffffffffffffffffff167f5d611f318680d00598bb735d61bacf0c514c6b50e1e5ad30040a4df2b12791c783604051610af99190610d61565b60405180910390a25050565b600080604051602001610b1790610f42565b6040516020818303038152906040528051906020012090508091505090565b6000610b4061081b565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600080823b905060008111915050919050565b60008060003073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff1663c7f62cda86604051602401610bc59190610d61565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610c149190610e16565b600060405180830381855af49150503d8060008114610c4f576040519150601f19603f3d011682016040523d82523d6000602084013e610c54565b606091505b509150915081158015610cb9575063a1cfa5a860e01b604051602001610c7a9190610faf565b6040516020818303038152906040528051906020012081604051602001610ca19190610e16565b60405160208183030381529060405280519060200120145b92505050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610cf282610cc7565b9050919050565b610d0281610ce7565b8114610d0d57600080fd5b50565b600081359050610d1f81610cf9565b92915050565b600060208284031215610d3b57610d3a610cc2565b5b6000610d4984828501610d10565b91505092915050565b610d5b81610ce7565b82525050565b6000602082019050610d766000830184610d52565b92915050565b6000604082019050610d916000830185610d52565b610d9e6020830184610d52565b9392505050565b600081519050919050565b600081905092915050565b60005b83811015610dd9578082015181840152602081019050610dbe565b60008484015250505050565b6000610df082610da5565b610dfa8185610db0565b9350610e0a818560208601610dbb565b80840191505092915050565b6000610e228284610de5565b915081905092915050565b600082825260208201905092915050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e4f776e6160008201527f626c650000000000000000000000000000000000000000000000000000000000602082015250565b6000610e9a602383610e2d565b9150610ea582610e3e565b604082019050919050565b60006020820190508181036000830152610ec981610e8d565b9050919050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e50726f7860008201527f7900000000000000000000000000000000000000000000000000000000000000602082015250565b6000610f2c602183610e2d565b9150610f3782610ed0565b604082019050919050565b60006020820190508181036000830152610f5b81610f1f565b9050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6000819050919050565b610fa9610fa482610f62565b610f8e565b82525050565b6000610fbb8284610f98565b6004820191508190509291505056fea264697066735822122023a7c33d7b91dce35ffbcf8837693364ab22a3905d0fc00016833e5fac45ca2f64736f6c63430008110033","r":"0x1","s":"0x1","yParity":"0x0","hash":"0x4feae6769d748b4f0f7c9bf21d782236c88f13906789a3ec602961296e4c3e43"}}],"ommers":[]},{"header":{"parentHash":"0xb3535af5103fd1c2bbd6dc7ff23f0799037a6542c231ebcb85abd776560fa512","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x23d74fb99ff6e42cbb5c33f92b078e37be6af2b6092459b103ff7059a6517ebc","transactionsRoot":"0x9eab45eca206fe11c107ea985c7d02fcfa442836aea3e04ba11dc4df587d5aa6","receiptsRoot":"0xe25abcfa973db8c55f73292137c626430de130a382ad4466337fefb0f7c8fde0","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x2","gasLimit":"0x1c9c380","gasUsed":"0x3ce3f","timestamp":"0x66b200cd","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x1c0bc72b","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"EIP1559":{"chainId":"0x343a","nonce":"0x0","gasLimit":"0x3d8a8","maxFeePerGas":"0x6211577c","maxPriorityFeePerGas":"0x3b9aca00","to":"0x4e59b44847b379578588920ca78fbf26c0b4956c","value":"0x0","accessList":[],"input":"0x4786e4342646b3ba97c1790b6cf5a55087a36240b22570f5d3a5d6bcc929d93b608060405234801561001057600080fd5b5060405161068538038061068583398181016040528101906100329190610275565b818181600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361009b576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6100ae8161019d60201b61004f1760201c565b6100ef57806040517f8a8b41ec0000000000000000000000000000000000000000000000000000000081526004016100e691906102c4565b60405180910390fd5b806100fe6101b060201b60201c565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050806101536101e160201b6100621760201c565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050505050610414565b600080823b905060008111915050919050565b6000806040516020016101c290610362565b6040516020818303038152906040528051906020012090508091505090565b6000806040516020016101f3906103f4565b6040516020818303038152906040528051906020012090508091505090565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061024282610217565b9050919050565b61025281610237565b811461025d57600080fd5b50565b60008151905061026f81610249565b92915050565b6000806040838503121561028c5761028b610212565b5b600061029a85828601610260565b92505060206102ab85828601610260565b9150509250929050565b6102be81610237565b82525050565b60006020820190506102d960008301846102b5565b92915050565b600082825260208201905092915050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e50726f7860008201527f7900000000000000000000000000000000000000000000000000000000000000602082015250565b600061034c6021836102df565b9150610357826102f0565b604082019050919050565b6000602082019050818103600083015261037b8161033f565b9050919050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e4f776e6160008201527f626c650000000000000000000000000000000000000000000000000000000000602082015250565b60006103de6023836102df565b91506103e982610382565b604082019050919050565b6000602082019050818103600083015261040d816103d1565b9050919050565b610262806104236000396000f3fe6080604052366100135761001161001d565b005b61001b61001d565b005b6000610027610093565b90503660008037600080366000845af43d6000803e806000811461004a573d6000f35b3d6000fd5b600080823b905060008111915050919050565b6000806040516020016100749061017a565b6040516020818303038152906040528051906020012090508091505090565b600061009d6100c6565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000806040516020016100d89061020c565b6040516020818303038152906040528051906020012090508091505090565b600082825260208201905092915050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e4f776e6160008201527f626c650000000000000000000000000000000000000000000000000000000000602082015250565b60006101646023836100f7565b915061016f82610108565b604082019050919050565b6000602082019050818103600083015261019381610157565b9050919050565b7f696f2e73796e7468657469782e636f72652d636f6e7472616374732e50726f7860008201527f7900000000000000000000000000000000000000000000000000000000000000602082015250565b60006101f66021836100f7565b91506102018261019a565b604082019050919050565b60006020820190508181036000830152610225816101e9565b905091905056fea2646970667358221220800da1f73cebd5e4afa07496d9bca6b6c4f526bdd3f4014ec15c70fe3a1c441364736f6c6343000811003300000000000000000000000047d08dad17ccb558b3ea74b1a0e73a9cc804a9dc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266","r":"0x1","s":"0x1","yParity":"0x0","hash":"0xb6794d5c7abed6f91d447e8efb72ef2580595a6d7c8dee57ba1dbb330970146a"}}],"ommers":[]},{"header":{"parentHash":"0x08abe6e453727534d8dd708843a7522b7d500338bdfe2402ca105dcdb05eebe9","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x510f2275449c013534a25ad0b13c867caf720947b68bcbcd4863f7b172a5d023","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x3","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66b200ca","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x29dd5614","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]}]} \ No newline at end of file diff --git a/crates/anvil/tests/it/state.rs b/crates/anvil/tests/it/state.rs index 7ed11ca46..8227a89f1 100644 --- a/crates/anvil/tests/it/state.rs +++ b/crates/anvil/tests/it/state.rs @@ -36,6 +36,16 @@ async fn can_load_existing_state_legacy() { assert_eq!(block_number, Uint::from(2)); } +#[tokio::test(flavor = "multi_thread")] +async fn can_load_existing_state_legacy_stress() { + let state_file = "test-data/state-dump-legacy-stress.json"; + + let (api, _handle) = spawn(NodeConfig::test().with_init_state_path(state_file)).await; + + let block_number = api.block_number().unwrap(); + assert_eq!(block_number, Uint::from(5)); +} + #[tokio::test(flavor = "multi_thread")] async fn can_load_existing_state() { let state_file = "test-data/state-dump.json"; From 08f1a0768c657244100d57e37454dd957fec5c30 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 18 Sep 2024 16:10:36 +0200 Subject: [PATCH 178/184] ci(release): fix hardcoded profile (#8888) --- .github/workflows/release.yml | 44 ++++++++++++++++------------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4780a5f2d..b398aae8e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -103,6 +103,13 @@ jobs: svm_target_platform: windows-amd64 platform: win32 arch: amd64 + env: + SVM_TARGET_PLATFORM: ${{ matrix.svm_target_platform }} + PLATFORM_NAME: ${{ matrix.platform }} + TARGET: ${{ matrix.target }} + PROFILE: maxperf + OUT_DIR: target/${{ env.TARGET }}/${{ env.PROFILE }} + VERSION_NAME: ${{ (env.IS_NIGHTLY && 'nightly') || needs.prepare.outputs.tag_name }} steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable @@ -127,26 +134,24 @@ jobs: echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV - name: Build binaries - env: - SVM_TARGET_PLATFORM: ${{ matrix.svm_target_platform }} shell: bash run: | set -eo pipefail - target="${{ matrix.target }}" - flags=(--profile maxperf --bins --no-default-features --features rustls,aws-kms,cli,asm-keccak) + flags=(--target $TARGET --profile $PROFILE --bins + --no-default-features --features rustls,aws-kms,cli,asm-keccak) # `jemalloc` is not fully supported on MSVC or aarch64 Linux. - if [[ "$target" != *msvc* && "$target" != "aarch64-unknown-linux-gnu" ]]; then + if [[ "$TARGET" != *msvc* && "$TARGET" != "aarch64-unknown-linux-gnu" ]]; then flags+=(--features jemalloc) fi - [[ "$target" == *windows* ]] && exe=".exe" + [[ "$TARGET" == *windows* ]] && ext=".exe" - cargo build --target "$target" "${flags[@]}" + cargo build "${flags[@]}" bins=(anvil cast chisel forge) for name in "${bins[@]}"; do - bin=./target/$target/release/$name$exe + bin=$OUT_DIR/$name$ext echo "" file "$bin" || true du -h "$bin" || true @@ -156,23 +161,18 @@ jobs: - name: Archive binaries id: artifacts - env: - PLATFORM_NAME: ${{ matrix.platform }} - TARGET: ${{ matrix.target }} - ARCH: ${{ matrix.arch }} - VERSION_NAME: ${{ (env.IS_NIGHTLY && 'nightly') || needs.prepare.outputs.tag_name }} shell: bash run: | if [ "$PLATFORM_NAME" == "linux" ]; then - tar -czvf "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" -C ./target/${TARGET}/release forge cast anvil chisel + tar -czvf "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" -C $OUT_DIR forge cast anvil chisel echo "file_name=foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" >> $GITHUB_OUTPUT elif [ "$PLATFORM_NAME" == "darwin" ]; then # We need to use gtar here otherwise the archive is corrupt. # See: https://github.com/actions/virtual-environments/issues/2619 - gtar -czvf "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" -C ./target/${TARGET}/release forge cast anvil chisel + gtar -czvf "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" -C $OUT_DIR forge cast anvil chisel echo "file_name=foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" >> $GITHUB_OUTPUT else - cd ./target/${TARGET}/release + cd $OUT_DIR 7z a -tzip "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.zip" forge.exe cast.exe anvil.exe chisel.exe mv "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.zip" ../../../ echo "file_name=foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.zip" >> $GITHUB_OUTPUT @@ -181,17 +181,13 @@ jobs: - name: Build man page id: man if: matrix.target == 'x86_64-unknown-linux-gnu' - env: - PLATFORM_NAME: ${{ matrix.platform }} - TARGET: ${{ matrix.target }} - VERSION_NAME: ${{ (env.IS_NIGHTLY && 'nightly') || needs.prepare.outputs.tag_name }} shell: bash run: | sudo apt-get -y install help2man - help2man -N ./target/${TARGET}/release/forge > forge.1 - help2man -N ./target/${TARGET}/release/cast > cast.1 - help2man -N ./target/${TARGET}/release/anvil > anvil.1 - help2man -N ./target/${TARGET}/release/chisel > chisel.1 + help2man -N $OUT_DIR/forge > forge.1 + help2man -N $OUT_DIR/cast > cast.1 + help2man -N $OUT_DIR/anvil > anvil.1 + help2man -N $OUT_DIR/chisel > chisel.1 gzip forge.1 gzip cast.1 gzip anvil.1 From 03ea54c63e33e3175a6f44d8cfe3718bd6c962ba Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Wed, 18 Sep 2024 18:28:52 +0200 Subject: [PATCH 179/184] ci(release): fix workflow (#8893) * ci(release): fix workflow post-8888 * move profile to global env def * add missing $ARCH env * to be sure, add back SVM_TARGET_PLATFORM * cannot use `env.TARGET` directly after setting it --- .github/workflows/release.yml | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b398aae8e..1377e5d6e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,6 +11,7 @@ on: env: CARGO_TERM_COLOR: always IS_NIGHTLY: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }} + PROFILE: maxperf jobs: prepare: @@ -103,13 +104,6 @@ jobs: svm_target_platform: windows-amd64 platform: win32 arch: amd64 - env: - SVM_TARGET_PLATFORM: ${{ matrix.svm_target_platform }} - PLATFORM_NAME: ${{ matrix.platform }} - TARGET: ${{ matrix.target }} - PROFILE: maxperf - OUT_DIR: target/${{ env.TARGET }}/${{ env.PROFILE }} - VERSION_NAME: ${{ (env.IS_NIGHTLY && 'nightly') || needs.prepare.outputs.tag_name }} steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable @@ -134,6 +128,11 @@ jobs: echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV - name: Build binaries + env: + SVM_TARGET_PLATFORM: ${{ matrix.svm_target_platform }} + PLATFORM_NAME: ${{ matrix.platform }} + TARGET: ${{ matrix.target }} + OUT_DIR: target/${{ matrix.target }}/${{ env.PROFILE }} shell: bash run: | set -eo pipefail @@ -161,6 +160,11 @@ jobs: - name: Archive binaries id: artifacts + env: + PLATFORM_NAME: ${{ matrix.platform }} + OUT_DIR: target/${{ matrix.target }}/${{ env.PROFILE }} + VERSION_NAME: ${{ (env.IS_NIGHTLY && 'nightly') || needs.prepare.outputs.tag_name }} + ARCH: ${{ matrix.arch }} shell: bash run: | if [ "$PLATFORM_NAME" == "linux" ]; then @@ -181,6 +185,9 @@ jobs: - name: Build man page id: man if: matrix.target == 'x86_64-unknown-linux-gnu' + env: + OUT_DIR: target/${{ matrix.target }}/${{ env.PROFILE }} + VERSION_NAME: ${{ (env.IS_NIGHTLY && 'nightly') || needs.prepare.outputs.tag_name }} shell: bash run: | sudo apt-get -y install help2man From 78ef20dfb5fe42d2f23eefbc4c773e331d307b75 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 19 Sep 2024 10:30:54 +0300 Subject: [PATCH 180/184] chore(forge): fix isolate ext integration tests (#8901) --- crates/forge/tests/cli/ext_integration.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/forge/tests/cli/ext_integration.rs b/crates/forge/tests/cli/ext_integration.rs index 1cf7b54ad..f0b1c5144 100644 --- a/crates/forge/tests/cli/ext_integration.rs +++ b/crates/forge/tests/cli/ext_integration.rs @@ -71,6 +71,7 @@ fn solady() { #[test] #[cfg_attr(windows, ignore = "weird git fail")] +#[cfg(not(feature = "isolate-by-default"))] fn geb() { ExtTester::new("reflexer-labs", "geb", "1a59f16a377386c49f520006ed0f7fd9d128cb09") .env("FOUNDRY_LEGACY_ASSERTIONS", "true") From 0c7601a243d48b0b55ced0f286c3c41766f24a97 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Thu, 19 Sep 2024 14:14:40 +0530 Subject: [PATCH 181/184] chore: bump alloy 0.3.6 (#8902) * bump 0.3.6 + block-explorers patch * fix * fix test * nit * bump block-explorers * bump --- Cargo.lock | 223 ++++++++++---------- Cargo.toml | 52 ++--- crates/anvil/src/eth/backend/mem/mod.rs | 3 +- crates/anvil/src/eth/backend/mem/storage.rs | 3 +- crates/anvil/tests/it/eip4844.rs | 2 +- crates/anvil/tests/it/otterscan.rs | 8 +- crates/anvil/tests/it/utils.rs | 11 +- crates/cast/bin/tx.rs | 4 +- crates/common/src/compile.rs | 2 +- crates/common/src/provider/mod.rs | 11 +- crates/forge/tests/it/test_helpers.rs | 3 +- 11 files changed, 172 insertions(+), 150 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c11f775fa..71467f3a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,9 +85,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28ddd17ffb7e4d66ef3a84e7b179072a9320cdc4b26c7f6f44cbf1081631b36" +checksum = "629b62e38d471cc15fea534eb7283d2f8a4e8bdb1811bcc5d66dda6cfce6fae1" dependencies = [ "alloy-eips", "alloy-primitives", @@ -99,9 +99,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69257e2ffe1a9f15f20a89cd54d1ca758468c5b3e87979191b8b5fc24d39b37" +checksum = "0eefe64fd344cffa9cf9e3435ec4e93e6e9c3481bc37269af988bf497faf4a6a" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -168,9 +168,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f6c5c0a383f14519531cf58d8440e74f10b938e289f803af870be6f79223110" +checksum = "f923dd5fca5f67a43d81ed3ebad0880bd41f6dd0ada930030353ac356c54cd0f" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -187,9 +187,9 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7db0ddc76399bb1a4010f630767f027cafe65ab406cfee8e6040128cd65e8325" +checksum = "3a7a18afb0b318616b6b2b0e2e7ac5529d32a966c673b48091c9919e284e6aca" dependencies = [ "alloy-primitives", "alloy-serde", @@ -210,9 +210,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7111af869909275cffc5c84d16b6c892d6d512773e40cbe83187d0b9c5235e91" +checksum = "d3c717b5298fad078cd3a418335b266eba91b511383ca9bd497f742d5975d5ab" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -224,9 +224,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "342028392a2d5050b7b93dd32a0715d3b3b9ce30072ecb69a35dd4895c005495" +checksum = "fb3705ce7d8602132bcf5ac7a1dd293a42adc2f183abf5907c30ac535ceca049" dependencies = [ "alloy-consensus", "alloy-eips", @@ -245,9 +245,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6e66d78c049dcadd065a926a9f2d9a9b2b10981a7889449e694fac7bccd2c6f" +checksum = "94ad40869867ed2d9cd3842b1e800889e5b49e6b92da346e93862b4a741bedf3" dependencies = [ "alloy-eips", "alloy-primitives", @@ -283,9 +283,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79f14ccc2a3c575cb17b1b4af8c772cf9b5b93b7ce7047d6640e53954abb558d" +checksum = "927f708dd457ed63420400ee5f06945df9632d5d101851952056840426a10dc5" dependencies = [ "alloy-chains", "alloy-consensus", @@ -322,9 +322,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34b9f5e85120aab30b8da23354592f7bd2b208d33d3204ffa1d44ac2e3dd5691" +checksum = "2d05f63677e210d758cd5d6d1ce10f20c980c3560ccfbe79ba1997791862a04f" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -363,9 +363,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dc79aeca84abb122a2fffbc1c91fdf958dca5c95be3875977bc99672bde0027" +checksum = "7d82952dca71173813d4e5733e2c986d8b04aea9e0f3b0a576664c232ad050a5" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -388,9 +388,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22045187a5ebf5b2af3f8b6831b66735b6556c5750ec5790aeeb45935260c1c2" +checksum = "64333d639f2a0cf73491813c629a405744e16343a4bc5640931be707c345ecc5" dependencies = [ "alloy-rpc-types-anvil", "alloy-rpc-types-engine", @@ -403,9 +403,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578d9ccad4e8510d32cc2810d05e01a232ccd79a4a6df60d257466897b43b013" +checksum = "d25cb45ad7c0930dd62eecf164d2afe4c3d2dd2c82af85680ad1f118e1e5cb83" dependencies = [ "alloy-primitives", "alloy-serde", @@ -414,9 +414,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c031a91e94a39f928244bc837c953817be5b8cc61759e1a9123b3abd17560dd" +checksum = "1464c4dd646e1bdfde86ae65ce5ba168dbb29180b478011fe87117ae46b1629b" dependencies = [ "alloy-consensus", "alloy-eips", @@ -431,9 +431,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "238f494727ff861a803bd73b1274ef788a615bf8f8c4bfada4e6df42afa275d2" +checksum = "83aa984386deda02482660aa31cb8ca1e63d533f1c31a52d7d181ac5ec68e9b8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -452,9 +452,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca08b0ccc0861055ceb83a1db009c4c8a7f52a259e7cda7ca6ca36ec2b5ce8" +checksum = "98db35cd42c90b484377e6bc44d95377a7a38a5ebee996e67754ac0446d542ab" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -466,9 +466,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "192ad94fe34c12be8ac4413ea00b1170202faa6fdebaa756b6a33555bf86d902" +checksum = "6bac37082c3b21283b3faf5cc0e08974272aee2f756ce1adeb26db56a5fce0d5" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -478,9 +478,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b95b6f024a558593dd3b8628af03f7df2ca50e4c56839293ad0a7546e471db0" +checksum = "731f75ec5d383107fd745d781619bd9cedf145836c51ecb991623d41278e71fa" dependencies = [ "alloy-primitives", "arbitrary", @@ -490,9 +490,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da64740ff0518606c514eb0e03dd0a1daa8ff94d6d491a626fd8e50efd6c4f18" +checksum = "307324cca94354cd654d6713629f0383ec037e1ff9e3e3d547212471209860c0" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -506,9 +506,9 @@ dependencies = [ [[package]] name = "alloy-signer-aws" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f187534919dcccebaf92774ad6b17645087b49a8716d49e2db29a2e3229b03f2" +checksum = "076be69aa26a4c500919f1ad3847662aa6d1e9bc2995e263ed826b1546d1b990" dependencies = [ "alloy-consensus", "alloy-network", @@ -524,9 +524,9 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907e56e2153e785c36e4197f4e254b5087e073a12b686f486d3bd6245c811313" +checksum = "cabd79d4eb954a8c2ae7889a18e2466af186ae68376251cf58525239c156ec54" dependencies = [ "alloy-consensus", "alloy-network", @@ -542,9 +542,9 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "221d828bdf1df1e7d490c304eb1425c7adacd6897a4baa2316e3f3054fc8863f" +checksum = "f3df66f5ddcc32d2070485dc702f5f5fb97cfbfa817f6e2e6bac16a4e32ed44c" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -562,9 +562,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e250010dce0e3caf6a6033e809718e5921391d937d1cbbcffe52653b37cc63" +checksum = "9fabe917ab1778e760b4701628d1cae8e028ee9d52ac6307de4e1e9286ab6b5f" dependencies = [ "alloy-consensus", "alloy-network", @@ -582,9 +582,9 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "586b4c42aa61b47591813fca23098e21530470986a00e3dc588baa2d5b8dd111" +checksum = "1068949eda889b2c052b29a6e8c7ea2ba16be6d1af83ad165fff2a4e4ad19fcd" dependencies = [ "alloy-consensus", "alloy-network", @@ -672,9 +672,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c7a669caa427abe8802184c8776f5103302f9337bb30a5b36bdebc332946c14" +checksum = "33616b2edf7454302a1d48084db185e52c309f73f6c10be99b0fe39354b3f1e9" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -691,9 +691,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4433ffa97aab6ae643de81c7bde9a2f043496f27368a607405a5c78a610caf74" +checksum = "a944f5310c690b62bbb3e7e5ce34527cbd36b2d18532a797af123271ce595a49" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -706,9 +706,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aa02db8751f9c0c37caf8c38ad3eb7aa1cfb09cfea3451a13aacaf06846c7a5" +checksum = "09fd8491249f74d16ec979b1f5672377b12ebb818e6056478ffa386954dbd350" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -727,9 +727,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0a5c4a0929479bcb85a2df628c01173618a71c807b2f499939a236dbde5d008" +checksum = "a9704761f6297fe482276bee7f77a93cb42bd541c2bd6c1c560b6f3a9ece672e" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -745,9 +745,9 @@ dependencies = [ [[package]] name = "alloy-trie" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "398a977d774db13446b8cead8cfa9517aebf9e03fc8a1512892dc1e03e70bb04" +checksum = "0a46c9c4fdccda7982e7928904bd85fe235a0404ee3d7e197fff13d61eac8b4f" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -964,9 +964,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.88" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e1496f8fb1fbf272686b8d37f523dab3e4a7443300055e74cdaa449f3114356" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "arbitrary" @@ -1320,9 +1320,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.43.0" +version = "1.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f7cb482caa5444d445c94417b9c74e49a849beb09ede4f2f4c3c15f8157387" +checksum = "c6550445e0913c9383375f4a5a2f550817567a19a178107fce1e1afd767f802a" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1342,9 +1342,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.42.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27bf24cd0d389daa923e974b0e7c38daf308fc21e963c049f57980235017175e" +checksum = "70a9d27ed1c12b1140c47daf1bc541606c43fdafd918c4797d520db0043ceef2" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1364,9 +1364,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.43.0" +version = "1.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43b3220f1c46ac0e9dcc0a97d94b93305dacb36d1dd393996300c6b9b74364" +checksum = "44514a6ca967686cde1e2a1b81df6ef1883d0e3e570da8d8bc5c491dcb6fc29b" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1386,9 +1386,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.42.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1c46924fb1add65bba55636e12812cae2febf68c0f37361766f627ddcca91ce" +checksum = "cd7a4d279762a35b9df97209f6808b95d4fe78547fe2316b4d200a0283960c5a" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1807,9 +1807,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" dependencies = [ "serde", ] @@ -1915,9 +1915,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.18" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" dependencies = [ "shlex", ] @@ -2055,9 +2055,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.26" +version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "205d5ef6d485fa47606b98b0ddc4ead26eb850aaa86abfb562a94fb3280ecba0" +checksum = "9b378c786d3bde9442d2c6dd7e6080b2a818db2b96e30d6e7f1b6d224eb617d3" dependencies = [ "clap", ] @@ -3477,9 +3477,9 @@ dependencies = [ [[package]] name = "foundry-block-explorers" -version = "0.7.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "461772f04e5457d6b6501f6aff3a615a032d01368c1cc91c13a06eff172962b6" +checksum = "ff37530e7c5deead0f9d7dc2a27b070e683bef79735ab453849ebdee74fa848f" dependencies = [ "alloy-chains", "alloy-json-abi", @@ -3711,9 +3711,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3372aaa89b1653b61fb297dbc24e74ad727ff76cc4415f1a0ec5f802d24e0797" +checksum = "6d91e510bd537970f68f8462dea0e8df0a2302d4749fb57bc8e10bbd32a283e2" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3749,9 +3749,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c4f9ac0ed5e695bbeb48ff0758ba31ce3d0d52b752aaa466f311f48ed772c0" +checksum = "f9971eefe4eae1cf2ac707beb4d40f63304b34c81c0961d299e461c14a23b1e7" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3759,9 +3759,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad6beeb057a8a58993d13841cffcb99e8aefdeb52ed9b368c85518747b467f9" +checksum = "0cde3d12776c295ad85bcdbbae18f4601e384f40a62b0e3371d880bbcd91c65c" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3783,9 +3783,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "442e5eb231aad523f0f3e26f9475ad9eab1aa2173a5991df1b7fa51f589f309f" +checksum = "569a769f6105248816c253715ec39977d61d369e9c67e4774d6870da8f64dffc" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3798,9 +3798,9 @@ dependencies = [ [[package]] name = "foundry-compilers-core" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92049644ce2745c36be16f6406592155d4be2314306af2543c7d30a32b00d6ed" +checksum = "5f10ade77fa0eab75e142a76711c42a258781bad0c4516ad64aa413297ebb72e" dependencies = [ "alloy-primitives", "cfg-if", @@ -4956,9 +4956,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -5995,9 +5995,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ea5043e58958ee56f3e15a90aee535795cd7dfd319846288d93c5b57d85cbe" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" @@ -6007,9 +6007,9 @@ checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" [[package]] name = "op-alloy-consensus" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef361231f72bea90365e441bd97d9c8fd3875603f18bbcad0e04874c6158e53" +checksum = "21aad1fbf80d2bcd7406880efc7ba109365f44bbb72896758ddcbfa46bf1592c" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6023,15 +6023,17 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a65da5f7591acba3d2304b25145ca9942ea90481213269aed9cb28add8e3ff" +checksum = "e281fbfc2198b7c0c16457d6524f83d192662bc9f3df70f24c3038d4521616df" dependencies = [ "alloy-eips", - "alloy-network", + "alloy-network-primitives", "alloy-primitives", "alloy-rpc-types-eth", "alloy-serde", + "cfg-if", + "hashbrown 0.14.5", "op-alloy-consensus", "serde", "serde_json", @@ -7146,9 +7148,9 @@ dependencies = [ [[package]] name = "revm" -version = "14.0.1" +version = "14.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f719e28cc6fdd086f8bc481429e587740d20ad89729cec3f5f5dd7b655474df" +checksum = "a9f3f55d0414c3d73902d876ba3d55a654f05fe937089fbf5f34b1ced26d78d5" dependencies = [ "auto_impl", "cfg-if", @@ -7161,12 +7163,13 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.6.0" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48184032103bb23788e42e42c7c85207f5b0b8a248b09ea8f5233077f35ab56e" +checksum = "48294aab02ed5d1940ad9b06f2a3230c3f0d98db6eacd618878cf143e204f6b0" dependencies = [ "alloy-primitives", - "alloy-rpc-types", + "alloy-rpc-types-eth", + "alloy-rpc-types-trace", "alloy-sol-types", "anstyle", "colorchoice", @@ -7178,9 +7181,9 @@ dependencies = [ [[package]] name = "revm-interpreter" -version = "10.0.1" +version = "10.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "959ecbc36802de6126852479844737f20194cf8e6718e0c30697d306a2cca916" +checksum = "713dbb271acd13afb06dcd460c1dc43da211e7ac9bc73cdf13528f615f55f96b" dependencies = [ "revm-primitives", "serde", @@ -7188,9 +7191,9 @@ dependencies = [ [[package]] name = "revm-precompile" -version = "11.0.1" +version = "11.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e25f604cb9db593ca3013be8c00f310d6790ccb1b7d8fbbdd4660ec8888043a" +checksum = "f73010c271d53fa7904e9845338e95f3955eb1200a0355e0abfdb89c41aaa9cd" dependencies = [ "aurora-engine-modexp", "blst", @@ -7208,9 +7211,9 @@ dependencies = [ [[package]] name = "revm-primitives" -version = "9.0.1" +version = "9.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ccb981ede47ccf87c68cebf1ba30cdbb7ec935233ea305f3dfff4c1e10ae541" +checksum = "e7a6bff9dbde3370a5ac9555104117f7e6039b3cc76e8d5d9d01899088beca2a" dependencies = [ "alloy-eips", "alloy-primitives", @@ -8713,9 +8716,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.20" +version = "0.22.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" dependencies = [ "indexmap 2.5.0", "serde", @@ -9076,9 +9079,9 @@ checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] @@ -9412,9 +9415,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.5" +version = "0.26.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a" +checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" dependencies = [ "rustls-pki-types", ] diff --git a/Cargo.toml b/Cargo.toml index fbf9be9b8..ade7824a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -163,42 +163,42 @@ foundry-wallets = { path = "crates/wallets" } foundry-linking = { path = "crates/linking" } # solc & compilation utilities -foundry-block-explorers = { version = "0.7.1", default-features = false } -foundry-compilers = { version = "0.11.0", default-features = false } +foundry-block-explorers = { version = "0.7.3", default-features = false } +foundry-compilers = { version = "0.11.1", default-features = false } foundry-fork-db = "0.3" solang-parser = "=0.3.3" ## revm # no default features to avoid c-kzg -revm = { version = "14.0.1", default-features = false } -revm-primitives = { version = "9.0.1", default-features = false } -revm-inspectors = { version = "0.6", features = ["serde"] } +revm = { version = "14.0.2", default-features = false } +revm-primitives = { version = "9.0.2", default-features = false } +revm-inspectors = { version = "0.7", features = ["serde"] } ## ethers ethers-contract-abigen = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { version = "0.3.3", default-features = false } -alloy-contract = { version = "0.3.3", default-features = false } -alloy-eips = { version = "0.3.3", default-features = false } -alloy-genesis = { version = "0.3.3", default-features = false } -alloy-json-rpc = { version = "0.3.3", default-features = false } -alloy-network = { version = "0.3.3", default-features = false } -alloy-provider = { version = "0.3.3", default-features = false } -alloy-pubsub = { version = "0.3.3", default-features = false } -alloy-rpc-client = { version = "0.3.3", default-features = false } -alloy-rpc-types = { version = "0.3.3", default-features = true } -alloy-serde = { version = "0.3.3", default-features = false } -alloy-signer = { version = "0.3.3", default-features = false } -alloy-signer-aws = { version = "0.3.3", default-features = false } -alloy-signer-gcp = { version = "0.3.3", default-features = false } -alloy-signer-ledger = { version = "0.3.3", default-features = false } -alloy-signer-local = { version = "0.3.3", default-features = false } -alloy-signer-trezor = { version = "0.3.3", default-features = false } -alloy-transport = { version = "0.3.3", default-features = false } -alloy-transport-http = { version = "0.3.3", default-features = false } -alloy-transport-ipc = { version = "0.3.3", default-features = false } -alloy-transport-ws = { version = "0.3.3", default-features = false } +alloy-consensus = { version = "0.3.6", default-features = false } +alloy-contract = { version = "0.3.6", default-features = false } +alloy-eips = { version = "0.3.6", default-features = false } +alloy-genesis = { version = "0.3.6", default-features = false } +alloy-json-rpc = { version = "0.3.6", default-features = false } +alloy-network = { version = "0.3.6", default-features = false } +alloy-provider = { version = "0.3.6", default-features = false } +alloy-pubsub = { version = "0.3.6", default-features = false } +alloy-rpc-client = { version = "0.3.6", default-features = false } +alloy-rpc-types = { version = "0.3.6", default-features = true } +alloy-serde = { version = "0.3.6", default-features = false } +alloy-signer = { version = "0.3.6", default-features = false } +alloy-signer-aws = { version = "0.3.6", default-features = false } +alloy-signer-gcp = { version = "0.3.6", default-features = false } +alloy-signer-ledger = { version = "0.3.6", default-features = false } +alloy-signer-local = { version = "0.3.6", default-features = false } +alloy-signer-trezor = { version = "0.3.6", default-features = false } +alloy-transport = { version = "0.3.6", default-features = false } +alloy-transport-http = { version = "0.3.6", default-features = false } +alloy-transport-ipc = { version = "0.3.6", default-features = false } +alloy-transport-ws = { version = "0.3.6", default-features = false } ## alloy-core alloy-dyn-abi = "0.8.1" diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 5e71892a3..0f28e28a5 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -1334,7 +1334,8 @@ impl Backend { GethDebugBuiltInTracerType::NoopTracer => Ok(NoopFrame::default().into()), GethDebugBuiltInTracerType::FourByteTracer | GethDebugBuiltInTracerType::PreStateTracer | - GethDebugBuiltInTracerType::MuxTracer => { + GethDebugBuiltInTracerType::MuxTracer | + GethDebugBuiltInTracerType::FlatCallTracer => { Err(RpcError::invalid_params("unsupported tracer type").into()) } }, diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index f999a512a..441bd1cbb 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -541,7 +541,8 @@ impl MinedTransaction { } GethDebugBuiltInTracerType::PreStateTracer | GethDebugBuiltInTracerType::NoopTracer | - GethDebugBuiltInTracerType::MuxTracer => {} + GethDebugBuiltInTracerType::MuxTracer | + GethDebugBuiltInTracerType::FlatCallTracer => {} }, GethDebugTracerType::JsTracer(_code) => {} } diff --git a/crates/anvil/tests/it/eip4844.rs b/crates/anvil/tests/it/eip4844.rs index 2da74da65..353083409 100644 --- a/crates/anvil/tests/it/eip4844.rs +++ b/crates/anvil/tests/it/eip4844.rs @@ -1,7 +1,7 @@ use crate::utils::http_provider; use alloy_consensus::{SidecarBuilder, SimpleCoder}; use alloy_eips::eip4844::{DATA_GAS_PER_BLOB, MAX_DATA_GAS_PER_BLOCK}; -use alloy_network::TransactionBuilder; +use alloy_network::{TransactionBuilder, TransactionBuilder4844}; use alloy_primitives::U256; use alloy_provider::Provider; use alloy_rpc_types::{BlockId, TransactionRequest}; diff --git a/crates/anvil/tests/it/otterscan.rs b/crates/anvil/tests/it/otterscan.rs index 55474079f..e839c9986 100644 --- a/crates/anvil/tests/it/otterscan.rs +++ b/crates/anvil/tests/it/otterscan.rs @@ -144,12 +144,8 @@ async fn ots_get_internal_operations_contract_selfdestruct(hardfork: EthereumHar let receipt = contract.goodbye().send().await.unwrap().get_receipt().await.unwrap(); - // TODO: This is currently not supported by revm-inspectors - let (expected_to, expected_value) = if hardfork < EthereumHardfork::Cancun { - (address!("DcDD539DA22bfFAa499dBEa4d37d086Dde196E75"), value) - } else { - (Address::ZERO, U256::ZERO) - }; + let expected_to = address!("DcDD539DA22bfFAa499dBEa4d37d086Dde196E75"); + let expected_value = value; let res = api.ots_get_internal_operations(receipt.transaction_hash).await.unwrap(); assert_eq!( diff --git a/crates/anvil/tests/it/utils.rs b/crates/anvil/tests/it/utils.rs index 368377513..3d6ae7009 100644 --- a/crates/anvil/tests/it/utils.rs +++ b/crates/anvil/tests/it/utils.rs @@ -38,7 +38,16 @@ use alloy_transport::BoxTransport; type PubsubSigner = FillProvider< JoinFill< - JoinFill, NonceFiller>, ChainIdFiller>, + JoinFill< + Identity, + JoinFill< + GasFiller, + JoinFill< + alloy_provider::fillers::BlobGasFiller, + JoinFill, + >, + >, + >, WalletFiller, >, RootProvider, diff --git a/crates/cast/bin/tx.rs b/crates/cast/bin/tx.rs index 08ee6d182..1dc9b0cd1 100644 --- a/crates/cast/bin/tx.rs +++ b/crates/cast/bin/tx.rs @@ -1,6 +1,8 @@ use alloy_consensus::{SidecarBuilder, SimpleCoder}; use alloy_json_abi::Function; -use alloy_network::{AnyNetwork, TransactionBuilder}; +use alloy_network::{ + AnyNetwork, TransactionBuilder, TransactionBuilder4844, TransactionBuilder7702, +}; use alloy_primitives::{hex, Address, Bytes, TxKind, U256}; use alloy_provider::Provider; use alloy_rlp::Decodable; diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 0c39dd873..a75ac0819 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -407,7 +407,7 @@ pub fn etherscan_project( Ok(ProjectBuilder::::default() .settings(SolcSettings { - settings: SolcConfig::builder().settings(settings).build().settings, + settings: SolcConfig::builder().settings(settings).build(), ..Default::default() }) .paths(paths) diff --git a/crates/common/src/provider/mod.rs b/crates/common/src/provider/mod.rs index 49cadd95c..fe7fd4ccc 100644 --- a/crates/common/src/provider/mod.rs +++ b/crates/common/src/provider/mod.rs @@ -40,7 +40,16 @@ pub type RetryProvider = RootProvider = FillProvider< JoinFill< - JoinFill, NonceFiller>, ChainIdFiller>, + JoinFill< + Identity, + JoinFill< + GasFiller, + JoinFill< + alloy_provider::fillers::BlobGasFiller, + JoinFill, + >, + >, + >, WalletFiller, >, RootProvider, N>, diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index fdef2f2ec..e5aee722f 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -70,7 +70,8 @@ impl ForgeTestProfile { settings.evm_version = Some(EvmVersion::Cancun); } - SolcConfig::builder().settings(settings).build() + let settings = SolcConfig::builder().settings(settings).build(); + SolcConfig { settings } } pub fn project(&self) -> Project { From 92ccb23144e1cb27724b3ffff11135c1ae08b92c Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 19 Sep 2024 13:37:11 +0200 Subject: [PATCH 182/184] chore(deps): bump mesc to 0.3.0 (#8897) --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 71467f3a4..4726ad042 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5574,9 +5574,9 @@ dependencies = [ [[package]] name = "mesc" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d340f1a6bfcd3ea5075231fb04b74c8bf7cb3be25ee6480976c8500fcd2949f" +checksum = "d04b0347d2799ef17df4623dbcb03531031142105168e0c549e0bf1f980e9e7e" dependencies = [ "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index ade7824a8..a143ef938 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -241,7 +241,7 @@ itertools = "0.13" jsonpath_lib = "0.3" k256 = "0.13" parking_lot = "0.12" -mesc = "0.2.1" +mesc = "0.3" rand = "0.8" rustc-hash = "2.0" semver = "1" From fa3da2e075e766e82274f31602193f248fe3d890 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 19 Sep 2024 14:04:18 +0200 Subject: [PATCH 183/184] chore: reduce size further (#8886) --- Cargo.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a143ef938..c7e38cbc9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,7 +65,7 @@ split-debuginfo = "unpacked" opt-level = 3 lto = "thin" debug = "line-tables-only" -strip = "symbols" +strip = "debuginfo" panic = "abort" codegen-units = 16 @@ -124,6 +124,9 @@ scrypt.opt-level = 3 # Override packages which aren't perf-sensitive for faster compilation speed and smaller binary size. [profile.release.package] alloy-sol-macro-expander.opt-level = "z" +figment.opt-level = "z" +foundry-compilers-artifacts-solc.opt-level = "z" +foundry-config.opt-level = "z" html5ever.opt-level = "z" mdbook.opt-level = "z" prettyplease.opt-level = "z" From da77402c298066b084547c6027f1c9e3104fe871 Mon Sep 17 00:00:00 2001 From: aganisgash Date: Thu, 19 Sep 2024 23:34:48 +0800 Subject: [PATCH 184/184] readme: fix cheatcode path (#8907) fix cheatcode path --- crates/cheatcodes/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cheatcodes/README.md b/crates/cheatcodes/README.md index 790f2553a..7f776392e 100644 --- a/crates/cheatcodes/README.md +++ b/crates/cheatcodes/README.md @@ -12,7 +12,7 @@ Foundry cheatcodes definitions and implementations. All cheatcodes are defined in a single [`sol!`] macro call in [`spec/src/vm.rs`]. -This, combined with the use of an internal [`Cheatcode`](../macros/impl/src/cheatcodes.rs) derive macro, +This, combined with the use of an internal [`Cheatcode`](../../crates/cheatcodes/spec/src/cheatcode.rs) derive macro, allows us to generate both the Rust definitions and the JSON specification of the cheatcodes. Cheatcodes are manually implemented through the `Cheatcode` trait, which is called in the