From 8a40f3466406a148b00073c7893a382aa8db34c9 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 20 Sep 2024 11:58:13 +0200 Subject: [PATCH 001/100] chore(cheatcodes): reduce generated code (#8912) --- crates/cheatcodes/spec/src/vm.rs | 5 +++++ crates/macros/src/cheatcodes.rs | 7 ++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 9c19196c8..9be2e5f6f 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -2365,3 +2365,8 @@ impl PartialEq for ForgeContext { } } } + +#[track_caller] +const fn panic_unknown_safety() -> ! { + panic!("cannot determine safety from the group, add a `#[cheatcode(safety = ...)]` attribute") +} diff --git a/crates/macros/src/cheatcodes.rs b/crates/macros/src/cheatcodes.rs index b38186e57..4fbe6cfa7 100644 --- a/crates/macros/src/cheatcodes.rs +++ b/crates/macros/src/cheatcodes.rs @@ -1,5 +1,5 @@ use proc_macro2::{Ident, Span, TokenStream}; -use quote::{quote, quote_spanned}; +use quote::quote; use syn::{Attribute, Data, DataStruct, DeriveInput, Error, Result}; pub fn derive_cheatcode(input: &DeriveInput) -> Result { @@ -42,13 +42,10 @@ fn derive_call(name: &Ident, data: &DataStruct, attrs: &[Attribute]) -> Result - panic!("cannot determine safety from the group, add a `#[cheatcode(safety = ...)]` attribute") - }; quote! { match Group::#group.safety() { Some(s) => s, - None => #panic, + None => panic_unknown_safety(), } } }; From 5725bcc66899646c640f7feea3fa2bb3dfca753b Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 20 Sep 2024 14:48:00 +0300 Subject: [PATCH 002/100] feat(cheatcodes): display warnings for deprecated cheatcodes (#8883) * feat(cheatcode): disaply message for cheatcodes marked as deprecated * Deprecated cheatcodes as hashset, displayed once per test suite * Add deprecated cheatcode replacement attr * Add support for fuzz and invariant tests * Changes after review: add Deprecated(replacement) * Update crates/cheatcodes/src/inspector.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * chore: touchups * Fix CI --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/cheatcodes/assets/cheatcodes.json | 4 +- .../cheatcodes/assets/cheatcodes.schema.json | 17 ++++- crates/cheatcodes/spec/src/cheatcode.rs | 10 ++- crates/cheatcodes/spec/src/vm.rs | 2 +- crates/cheatcodes/src/inspector.rs | 42 ++++------- crates/cheatcodes/src/lib.rs | 9 +++ crates/evm/evm/src/executors/fuzz/mod.rs | 15 ++-- crates/evm/evm/src/executors/fuzz/types.rs | 19 +++-- .../evm/evm/src/executors/invariant/replay.rs | 11 ++- crates/evm/fuzz/src/lib.rs | 3 + crates/forge/src/result.rs | 32 +++++++- crates/forge/src/runner.rs | 3 + crates/forge/tests/cli/test_cmd.rs | 75 +++++++++++++++++++ crates/macros/src/cheatcodes.rs | 4 +- 14 files changed, 192 insertions(+), 54 deletions(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 88c65137c..4d337ff5e 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -5408,7 +5408,9 @@ ] }, "group": "json", - "status": "deprecated", + "status": { + "deprecated": "replaced by `keyExistsJson`" + }, "safety": "safe" }, { diff --git a/crates/cheatcodes/assets/cheatcodes.schema.json b/crates/cheatcodes/assets/cheatcodes.schema.json index 9301196d9..9ffc13d61 100644 --- a/crates/cheatcodes/assets/cheatcodes.schema.json +++ b/crates/cheatcodes/assets/cheatcodes.schema.json @@ -384,11 +384,20 @@ ] }, { - "description": "The cheatcode has been deprecated, meaning it will be removed in a future release.\n\nUse of deprecated cheatcodes is discouraged and will result in a warning.", - "type": "string", - "enum": [ + "description": "The cheatcode has been deprecated, meaning it will be removed in a future release.\n\nContains the optional reason for deprecation.\n\nUse of deprecated cheatcodes is discouraged and will result in a warning.", + "type": "object", + "required": [ "deprecated" - ] + ], + "properties": { + "deprecated": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false }, { "description": "The cheatcode has been removed and is no longer available for use.\n\nUse of removed cheatcodes will result in a hard error.", diff --git a/crates/cheatcodes/spec/src/cheatcode.rs b/crates/cheatcodes/spec/src/cheatcode.rs index 207cac159..bce501d45 100644 --- a/crates/cheatcodes/spec/src/cheatcode.rs +++ b/crates/cheatcodes/spec/src/cheatcode.rs @@ -23,18 +23,18 @@ pub struct Cheatcode<'a> { /// The group that the cheatcode belongs to. pub group: Group, /// The current status of the cheatcode. E.g. whether it is stable or experimental, etc. - pub status: Status, + pub status: Status<'a>, /// Whether the cheatcode is safe to use inside of scripts. E.g. it does not change state in an /// unexpected way. pub safety: Safety, } /// The status of a cheatcode. -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub enum Status { +pub enum Status<'a> { /// The cheatcode and its API is currently stable. Stable, /// The cheatcode is unstable, meaning it may contain bugs and may break its API on any @@ -44,8 +44,10 @@ pub enum Status { Experimental, /// The cheatcode has been deprecated, meaning it will be removed in a future release. /// + /// Contains the optional reason for deprecation. + /// /// Use of deprecated cheatcodes is discouraged and will result in a warning. - Deprecated, + Deprecated(Option<&'a str>), /// The cheatcode has been removed and is no longer available for use. /// /// Use of removed cheatcodes will result in a hard error. diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 9be2e5f6f..fef210bae 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1865,7 +1865,7 @@ interface Vm { /// Checks if `key` exists in a JSON object /// `keyExists` is being deprecated in favor of `keyExistsJson`. It will be removed in future versions. - #[cheatcode(group = Json, status = Deprecated)] + #[cheatcode(group = Json, status = Deprecated(Some("replaced by `keyExistsJson`")))] function keyExists(string calldata json, string calldata key) external view returns (bool); /// Checks if `key` exists in a JSON object. #[cheatcode(group = Json)] diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index d254a9b66..401966c46 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -447,6 +447,9 @@ pub struct Cheatcodes { /// Addresses with arbitrary storage. pub arbitrary_storage: Option, + + /// Deprecated cheatcodes mapped to the reason. Used to report warnings on test results. + pub deprecated: HashMap<&'static str, Option<&'static str>>, } // This is not derived because calling this in `fn new` with `..Default::default()` creates a second @@ -491,6 +494,7 @@ impl Cheatcodes { rng: Default::default(), ignored_traces: Default::default(), arbitrary_storage: Default::default(), + deprecated: Default::default(), } } @@ -2071,6 +2075,7 @@ fn apply_dispatch( ccx: &mut CheatsCtxt, executor: &mut E, ) -> Result { + // TODO: Replace with `::apply_full` once it's object-safe. macro_rules! dispatch { ($($variant:ident),*) => { match calls { @@ -2079,10 +2084,13 @@ fn apply_dispatch( }; } - let mut dyn_cheat = DynCheatCache::new(calls); - let _guard = trace_span_and_call(&mut dyn_cheat); + let cheat = calls_as_dyn_cheatcode(calls); + if let spec::Status::Deprecated(replacement) = *cheat.status() { + ccx.state.deprecated.insert(cheat.signature(), replacement); + } + let _guard = trace_span_and_call(cheat); let mut result = vm_calls!(dispatch); - fill_and_trace_return(&mut dyn_cheat, &mut result); + fill_and_trace_return(cheat, &mut result); result } @@ -2091,34 +2099,17 @@ 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> { - 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()); +fn trace_span_and_call(cheat: &dyn DynCheatcode) -> tracing::span::EnteredSpan { + let span = debug_span!(target: "cheatcodes", "apply", id = %cheat.id()); let entered = span.entered(); - trace!(target: "cheatcodes", cheat = ?dyn_cheat.get().as_debug(), "applying"); + trace!(target: "cheatcodes", cheat = ?cheat.as_debug(), "applying"); entered } -fn fill_and_trace_return(dyn_cheat: &mut DynCheatCache, result: &mut Result) { +fn fill_and_trace_return(cheat: &dyn DynCheatcode, result: &mut Result) { if let Err(e) = result { if e.is_str() { - let name = dyn_cheat.get().name(); + let name = cheat.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` @@ -2136,7 +2127,6 @@ fn fill_and_trace_return(dyn_cheat: &mut DynCheatCache, result: &mut Result) { ); } -#[cold] fn calls_as_dyn_cheatcode(calls: &Vm::VmCalls) -> &dyn DynCheatcode { macro_rules! as_dyn { ($($variant:ident),*) => { diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index 4a06ff454..50bd54701 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -14,6 +14,7 @@ extern crate tracing; use alloy_primitives::Address; use foundry_evm_core::backend::DatabaseExt; use revm::{ContextPrecompiles, InnerEvmContext}; +use spec::Status; pub use config::CheatsConfig; pub use error::{Error, ErrorKind, Result}; @@ -90,6 +91,8 @@ pub(crate) trait Cheatcode: CheatcodeDef + DynCheatcode { pub(crate) trait DynCheatcode { fn name(&self) -> &'static str; fn id(&self) -> &'static str; + fn signature(&self) -> &'static str; + fn status(&self) -> &Status<'static>; fn as_debug(&self) -> &dyn std::fmt::Debug; } @@ -100,6 +103,12 @@ impl DynCheatcode for T { fn id(&self) -> &'static str { T::CHEATCODE.func.id } + fn signature(&self) -> &'static str { + T::CHEATCODE.func.signature + } + fn status(&self) -> &Status<'static> { + &T::CHEATCODE.status + } fn as_debug(&self) -> &dyn std::fmt::Debug { self } diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index ee5a03dca..54480181f 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -17,7 +17,7 @@ use foundry_evm_fuzz::{ use foundry_evm_traces::SparsedTraceArena; use indicatif::ProgressBar; use proptest::test_runner::{TestCaseError, TestError, TestRunner}; -use std::cell::RefCell; +use std::{cell::RefCell, collections::HashMap}; mod types; pub use types::{CaseOutcome, CounterExampleOutcome, FuzzOutcome}; @@ -39,6 +39,8 @@ pub struct FuzzTestData { pub coverage: Option, // Stores logs for all fuzz cases pub logs: Vec, + // Deprecated cheatcodes mapped to their replacements. + pub deprecated_cheatcodes: HashMap<&'static str, Option<&'static str>>, } /// Wrapper around an [`Executor`] which provides fuzzing support using [`proptest`]. @@ -124,6 +126,7 @@ impl FuzzedExecutor { Some(prev) => prev.merge(case.coverage.unwrap()), opt => *opt = case.coverage, } + data.deprecated_cheatcodes = case.deprecated_cheatcodes; Ok(()) } @@ -168,6 +171,7 @@ impl FuzzedExecutor { breakpoints: last_run_breakpoints, gas_report_traces: traces.into_iter().map(|a| a.arena).collect(), coverage: fuzz_result.coverage, + deprecated_cheatcodes: fuzz_result.deprecated_cheatcodes, }; match run_result { @@ -230,10 +234,10 @@ impl FuzzedExecutor { return Err(TestCaseError::reject(FuzzError::AssumeReject)) } - let breakpoints = call - .cheatcodes - .as_ref() - .map_or_else(Default::default, |cheats| cheats.breakpoints.clone()); + let (breakpoints, deprecated_cheatcodes) = + call.cheatcodes.as_ref().map_or_else(Default::default, |cheats| { + (cheats.breakpoints.clone(), cheats.deprecated.clone()) + }); let success = self.executor.is_raw_call_mut_success(address, &mut call, should_fail); if success { @@ -243,6 +247,7 @@ impl FuzzedExecutor { coverage: call.coverage, breakpoints, logs: call.logs, + deprecated_cheatcodes, })) } else { Ok(FuzzOutcome::CounterExample(CounterExampleOutcome { diff --git a/crates/evm/evm/src/executors/fuzz/types.rs b/crates/evm/evm/src/executors/fuzz/types.rs index ac7c14373..081ff9112 100644 --- a/crates/evm/evm/src/executors/fuzz/types.rs +++ b/crates/evm/evm/src/executors/fuzz/types.rs @@ -5,30 +5,33 @@ use foundry_evm_coverage::HitMaps; use foundry_evm_fuzz::FuzzCase; use foundry_evm_traces::SparsedTraceArena; use revm::interpreter::InstructionResult; +use std::collections::HashMap; /// Returned by a single fuzz in the case of a successful run #[derive(Debug)] pub struct CaseOutcome { - /// Data of a single fuzz test case + /// Data of a single fuzz test case. pub case: FuzzCase, - /// The traces of the call + /// The traces of the call. pub traces: Option, - /// The coverage info collected during the call + /// The coverage info collected during the call. pub coverage: Option, - /// Breakpoints char pc map + /// Breakpoints char pc map. pub breakpoints: Breakpoints, - /// logs of a single fuzz test case + /// logs of a single fuzz test case. pub logs: Vec, + // Deprecated cheatcodes mapped to their replacements. + pub deprecated_cheatcodes: HashMap<&'static str, Option<&'static str>>, } /// Returned by a single fuzz when a counterexample has been discovered #[derive(Debug)] pub struct CounterExampleOutcome { - /// Minimal reproduction test case for failing test + /// Minimal reproduction test case for failing test. pub counterexample: (Bytes, RawCallResult), - /// The status of the call + /// The status of the call. pub exit_reason: InstructionResult, - /// Breakpoints char pc map + /// Breakpoints char pc map. pub breakpoints: Breakpoints, } diff --git a/crates/evm/evm/src/executors/invariant/replay.rs b/crates/evm/evm/src/executors/invariant/replay.rs index c10d31560..177e1b55b 100644 --- a/crates/evm/evm/src/executors/invariant/replay.rs +++ b/crates/evm/evm/src/executors/invariant/replay.rs @@ -17,7 +17,7 @@ use indicatif::ProgressBar; use parking_lot::RwLock; use proptest::test_runner::TestError; use revm::primitives::U256; -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; /// Replays a call sequence for collecting logs and traces. /// Returns counterexample to be used when the call sequence is a failed scenario. @@ -30,6 +30,7 @@ pub fn replay_run( logs: &mut Vec, traces: &mut Traces, coverage: &mut Option, + deprecated_cheatcodes: &mut HashMap<&'static str, Option<&'static str>>, inputs: &[BasicTxDetails], ) -> Result> { // We want traces for a failed case. @@ -84,6 +85,12 @@ pub fn replay_run( )?; traces.push((TraceKind::Execution, invariant_result.traces.clone().unwrap())); logs.extend(invariant_result.logs); + deprecated_cheatcodes.extend( + invariant_result + .cheatcodes + .as_ref() + .map_or_else(Default::default, |cheats| cheats.deprecated.clone()), + ); // Collect after invariant logs and traces. if invariant_contract.call_after_invariant && invariant_success { @@ -107,6 +114,7 @@ pub fn replay_error( logs: &mut Vec, traces: &mut Traces, coverage: &mut Option, + deprecated_cheatcodes: &mut HashMap<&'static str, Option<&'static str>>, progress: Option<&ProgressBar>, ) -> Result> { match failed_case.test_error { @@ -133,6 +141,7 @@ pub fn replay_error( logs, traces, coverage, + deprecated_cheatcodes, &calls, ) } diff --git a/crates/evm/fuzz/src/lib.rs b/crates/evm/fuzz/src/lib.rs index fe1cb38d0..2930a6aa0 100644 --- a/crates/evm/fuzz/src/lib.rs +++ b/crates/evm/fuzz/src/lib.rs @@ -183,6 +183,9 @@ pub struct FuzzTestResult { /// Breakpoints for debugger. Correspond to the same fuzz case as `traces`. pub breakpoints: Option, + + // Deprecated cheatcodes mapped to their replacements. + pub deprecated_cheatcodes: HashMap<&'static str, Option<&'static str>>, } impl FuzzTestResult { diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index be445d491..171a234a5 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -215,8 +215,26 @@ impl SuiteResult { pub fn new( duration: Duration, test_results: BTreeMap, - warnings: Vec, + mut warnings: Vec, ) -> Self { + // Add deprecated cheatcodes warning, if any of them used in current test suite. + let mut deprecated_cheatcodes = HashMap::new(); + for test_result in test_results.values() { + deprecated_cheatcodes.extend(test_result.deprecated_cheatcodes.clone()); + } + if !deprecated_cheatcodes.is_empty() { + let mut warning = + "the following cheatcode(s) are deprecated and will be removed in future versions:" + .to_string(); + for (cheatcode, reason) in deprecated_cheatcodes { + write!(warning, "\n {cheatcode}").unwrap(); + if let Some(reason) = reason { + write!(warning, ": {reason}").unwrap(); + } + } + warnings.push(warning); + } + Self { duration, test_results, warnings } } @@ -390,6 +408,10 @@ pub struct TestResult { /// pc breakpoint char map pub breakpoints: Breakpoints, + + /// Deprecated cheatcodes (mapped to their replacements, if any) used in current test. + #[serde(skip)] + pub deprecated_cheatcodes: HashMap<&'static str, Option<&'static str>>, } impl fmt::Display for TestResult { @@ -501,9 +523,14 @@ impl TestResult { false => TestStatus::Failure, }; self.reason = reason; - self.breakpoints = raw_call_result.cheatcodes.map(|c| c.breakpoints).unwrap_or_default(); self.duration = Duration::default(); self.gas_report_traces = Vec::new(); + + if let Some(cheatcodes) = raw_call_result.cheatcodes { + self.breakpoints = cheatcodes.breakpoints; + self.deprecated_cheatcodes = cheatcodes.deprecated; + } + self } @@ -535,6 +562,7 @@ impl TestResult { 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.deprecated_cheatcodes = result.deprecated_cheatcodes; self } diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 02b8150df..51f2e5cc1 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -530,6 +530,7 @@ impl<'a> ContractRunner<'a> { &mut test_result.logs, &mut test_result.traces, &mut test_result.coverage, + &mut test_result.deprecated_cheatcodes, &txes, ); return test_result.invariant_replay_fail( @@ -572,6 +573,7 @@ impl<'a> ContractRunner<'a> { &mut test_result.logs, &mut test_result.traces, &mut test_result.coverage, + &mut test_result.deprecated_cheatcodes, progress.as_ref(), ) { Ok(call_sequence) => { @@ -607,6 +609,7 @@ impl<'a> ContractRunner<'a> { &mut test_result.logs, &mut test_result.traces, &mut test_result.coverage, + &mut test_result.deprecated_cheatcodes, &invariant_result.last_run_inputs, ) { error!(%err, "Failed to replay last invariant run"); diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 0254f251b..acfec46c6 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -2099,3 +2099,78 @@ contract JunitReportTest is Test { "#]]); }); + +forgetest_init!( + // Enable this if no cheatcodes are deprecated. + // #[ignore = "no cheatcodes are deprecated"] + test_deprecated_cheatcode_warning, + |prj, cmd| { + prj.add_test( + "DeprecatedCheatcodeTest.t.sol", + r#" + import "forge-std/Test.sol"; + contract DeprecatedCheatcodeTest is Test { + function test_deprecated_cheatcode() public view { + vm.keyExists('{"a": 123}', ".a"); + vm.keyExists('{"a": 123}', ".a"); + } + } + + contract DeprecatedCheatcodeFuzzTest is Test { + function test_deprecated_cheatcode(uint256 a) public view { + vm.keyExists('{"a": 123}', ".a"); + } + } + + contract Counter { + uint256 a; + + function count() public { + a++; + } + } + + contract DeprecatedCheatcodeInvariantTest is Test { + function setUp() public { + Counter counter = new Counter(); + } + + /// forge-config: default.invariant.runs = 1 + function invariant_deprecated_cheatcode() public { + vm.keyExists('{"a": 123}', ".a"); + } + } + "#, + ) + .unwrap(); + + // Tests deprecated cheatcode warning for unit tests. + cmd.args(["test", "--mc", "DeprecatedCheatcodeTest"]).assert_success().stderr_eq(str![[ + r#" +Warning: the following cheatcode(s) are deprecated and will be removed in future versions: + keyExists(string,string): replaced by `keyExistsJson` + +"# + ]]); + + // Tests deprecated cheatcode warning for fuzz tests. + cmd.forge_fuse() + .args(["test", "--mc", "DeprecatedCheatcodeFuzzTest"]) + .assert_success() + .stderr_eq(str![[r#" +Warning: the following cheatcode(s) are deprecated and will be removed in future versions: + keyExists(string,string): replaced by `keyExistsJson` + +"#]]); + + // Tests deprecated cheatcode warning for invariant tests. + cmd.forge_fuse() + .args(["test", "--mc", "DeprecatedCheatcodeInvariantTest"]) + .assert_success() + .stderr_eq(str![[r#" +Warning: the following cheatcode(s) are deprecated and will be removed in future versions: + keyExists(string,string): replaced by `keyExistsJson` + +"#]]); + } +); diff --git a/crates/macros/src/cheatcodes.rs b/crates/macros/src/cheatcodes.rs index 4fbe6cfa7..d9c2d2c91 100644 --- a/crates/macros/src/cheatcodes.rs +++ b/crates/macros/src/cheatcodes.rs @@ -20,7 +20,7 @@ pub fn derive_cheatcode(input: &DeriveInput) -> Result { /// Implements `CheatcodeDef` for a function call struct. fn derive_call(name: &Ident, data: &DataStruct, attrs: &[Attribute]) -> Result { let mut group = None::; - let mut status = None::; + let mut status = None::; let mut safety = None::; for attr in attrs.iter().filter(|a| a.path().is_ident("cheatcode")) { attr.meta.require_list()?.parse_nested_meta(|meta| { @@ -38,7 +38,7 @@ fn derive_call(name: &Ident, data: &DataStruct, attrs: &[Attribute]) -> Result Date: Fri, 20 Sep 2024 16:53:06 +0200 Subject: [PATCH 003/100] refactor: rewrite the console.log format string parser (#8913) * refactor: rewrite the console.log format string parser * chore: clippy --- crates/common/fmt/src/console.rs | 238 +++++++++++++++++++++---------- 1 file changed, 163 insertions(+), 75 deletions(-) diff --git a/crates/common/fmt/src/console.rs b/crates/common/fmt/src/console.rs index 7473ccdec..5bc291e03 100644 --- a/crates/common/fmt/src/console.rs +++ b/crates/common/fmt/src/console.rs @@ -1,61 +1,175 @@ use super::UIfmt; use alloy_primitives::{Address, Bytes, FixedBytes, I256, U256}; -use std::iter::Peekable; +use std::fmt::{self, Write}; + +/// A piece is a portion of the format string which represents the next part to emit. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Piece<'a> { + /// A literal string which should directly be emitted. + String(&'a str), + /// A format specifier which should be replaced with the next argument. + NextArgument(FormatSpec), +} /// A format specifier. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] pub enum FormatSpec { - /// %s format spec + /// `%s` #[default] String, - /// %d format spec + /// `%d` Number, - /// %i format spec + /// `%i` Integer, - /// %o format spec + /// `%o` Object, - /// %e format spec with an optional precision + /// `%e`, `%18e` Exponential(Option), - /// %x format spec + /// `%x` Hexadecimal, } -impl FormatSpec { - fn from_chars(iter: &mut Peekable) -> Result - where - I: Iterator, - { - 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() { - if ch.is_ascii_digit() { - num.push(ch); - iter.next(); - } else { - break; - } +impl fmt::Display for FormatSpec { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("%")?; + match *self { + Self::String => f.write_str("s"), + Self::Number => f.write_str("d"), + Self::Integer => f.write_str("i"), + Self::Object => f.write_str("o"), + Self::Exponential(Some(n)) => write!(f, "{n}e"), + Self::Exponential(None) => f.write_str("e"), + Self::Hexadecimal => f.write_str("x"), + } + } +} + +enum ParseArgError { + /// Failed to parse the argument. + Err, + /// Escape `%%`. + Skip, +} + +/// Parses a format string into a sequence of [pieces][Piece]. +#[derive(Debug)] +pub struct Parser<'a> { + input: &'a str, + chars: std::str::CharIndices<'a>, +} + +impl<'a> Parser<'a> { + /// Creates a new parser for the given input. + pub fn new(input: &'a str) -> Self { + Self { input, chars: input.char_indices() } + } + + /// Parses a string until the next format specifier. + /// + /// `skip` is the number of format specifier characters (`%`) to ignore before returning the + /// string. + fn string(&mut self, start: usize, mut skip: usize) -> &'a str { + while let Some((pos, c)) = self.peek() { + if c == '%' { + if skip == 0 { + return &self.input[start..pos]; } - if let Some(&ch) = iter.peek() { - if ch == 'e' { - let num = num.parse().map_err(|_| num)?; - iter.next(); - Ok(Self::Exponential(Some(num))) - } else { - Err(num) - } - } else { - Err(num) + skip -= 1; + } + self.chars.next(); + } + &self.input[start..] + } + + /// Parses a format specifier. + /// + /// If `Err` is returned, the internal iterator may have been advanced and it may be in an + /// invalid state. + fn argument(&mut self) -> Result { + let (start, ch) = self.peek().ok_or(ParseArgError::Err)?; + let simple_spec = match ch { + 's' => Some(FormatSpec::String), + 'd' => Some(FormatSpec::Number), + 'i' => Some(FormatSpec::Integer), + 'o' => Some(FormatSpec::Object), + 'e' => Some(FormatSpec::Exponential(None)), + 'x' => Some(FormatSpec::Hexadecimal), + // "%%" is a literal '%'. + '%' => return Err(ParseArgError::Skip), + _ => None, + }; + if let Some(spec) = simple_spec { + self.chars.next(); + return Ok(spec); + } + + // %e + if ch.is_ascii_digit() { + let n = self.integer(start); + if let Some((_, 'e')) = self.peek() { + self.chars.next(); + return Ok(FormatSpec::Exponential(n)); + } + } + + Err(ParseArgError::Err) + } + + fn integer(&mut self, start: usize) -> Option { + let mut end = start; + while let Some((pos, ch)) = self.peek() { + if !ch.is_ascii_digit() { + end = pos; + break; + } + self.chars.next(); + } + self.input[start..end].parse().ok() + } + + fn current_pos(&mut self) -> usize { + self.peek().map(|(n, _)| n).unwrap_or(self.input.len()) + } + + fn peek(&mut self) -> Option<(usize, char)> { + self.peek_n(0) + } + + fn peek_n(&mut self, n: usize) -> Option<(usize, char)> { + self.chars.clone().nth(n) + } +} + +impl<'a> Iterator for Parser<'a> { + type Item = Piece<'a>; + + fn next(&mut self) -> Option { + let (mut start, ch) = self.peek()?; + let mut skip = 0; + if ch == '%' { + let prev = self.chars.clone(); + self.chars.next(); + match self.argument() { + Ok(arg) => { + debug_assert_eq!(arg.to_string(), self.input[start..self.current_pos()]); + return Some(Piece::NextArgument(arg)); + } + + // Skip the argument if we encountered "%%". + Err(ParseArgError::Skip) => { + start = self.current_pos(); + skip += 1; + } + + // Reset the iterator if we failed to parse the argument, and include any + // parsed and unparsed specifier in `String`. + Err(ParseArgError::Err) => { + self.chars = prev; + skip += 1; } } - ch => Err(String::from(ch)), } + Some(Piece::String(self.string(start, skip))) } } @@ -249,7 +363,7 @@ 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().peekable(); + let mut values = values.iter().copied(); let mut result = String::with_capacity(spec.len()); // for the first space @@ -275,45 +389,19 @@ pub fn console_format(spec: &str, values: &[&dyn ConsoleFmt]) -> String { fn format_spec<'a>( s: &str, - values: &mut Peekable>, + mut values: impl Iterator, result: &mut String, ) { - let mut expect_fmt = false; - let mut chars = s.chars().peekable(); - - while chars.peek().is_some() { - if expect_fmt { - expect_fmt = false; - match FormatSpec::from_chars(&mut chars) { - Ok(spec) => { - let value = values.next().expect("value existence is checked"); - // format and write the value + for piece in Parser::new(s) { + match piece { + Piece::String(s) => result.push_str(s), + Piece::NextArgument(spec) => { + if let Some(value) = values.next() { result.push_str(&value.fmt(spec)); - } - Err(consumed) => { - // on parser failure, write '%' and consumed characters - result.push('%'); - 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 { - result.push(ch); + // Write the format specifier as-is if there are no more values. + write!(result, "{spec}").unwrap(); } - } else { - result.push(ch); } } } From f2c14c176b6f69ede1c067bcfcc0fdf2d6beba5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Vincent?= <28714795+leovct@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:59:41 +0200 Subject: [PATCH 004/100] feat: implement `parseTomlType` cheats (#8911) * feat: implement `parseTomlType` cheats * chore: `forge fmt` * revert: use json naming to indicate to users that they are operating on json data * chore: nit * chore: nit --- crates/cheatcodes/assets/cheatcodes.json | 60 ++++++++ crates/cheatcodes/spec/src/vm.rs | 13 ++ crates/cheatcodes/src/json.rs | 2 +- crates/cheatcodes/src/toml.rs | 25 +++- testdata/cheats/Vm.sol | 3 + testdata/default/cheats/Toml.t.sol | 130 ++++++++++++++++-- .../fixtures/Toml/nested_toml_struct.toml | 23 ++++ testdata/fixtures/Toml/test.toml | 4 +- 8 files changed, 244 insertions(+), 16 deletions(-) create mode 100644 testdata/fixtures/Toml/nested_toml_struct.toml diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 4d337ff5e..4357d82a2 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -6473,6 +6473,66 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "parseTomlTypeArray", + "description": "Parses a string of TOML data at `key` and coerces it to type array corresponding to `typeDescription`.", + "declaration": "function parseTomlTypeArray(string calldata toml, string calldata key, string calldata typeDescription) external pure returns (bytes memory);", + "visibility": "external", + "mutability": "pure", + "signature": "parseTomlTypeArray(string,string,string)", + "selector": "0x49be3743", + "selectorBytes": [ + 73, + 190, + 55, + 67 + ] + }, + "group": "toml", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "parseTomlType_0", + "description": "Parses a string of TOML data and coerces it to type corresponding to `typeDescription`.", + "declaration": "function parseTomlType(string calldata toml, string calldata typeDescription) external pure returns (bytes memory);", + "visibility": "external", + "mutability": "pure", + "signature": "parseTomlType(string,string)", + "selector": "0x47fa5e11", + "selectorBytes": [ + 71, + 250, + 94, + 17 + ] + }, + "group": "toml", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "parseTomlType_1", + "description": "Parses a string of TOML data at `key` and coerces it to type corresponding to `typeDescription`.", + "declaration": "function parseTomlType(string calldata toml, string calldata key, string calldata typeDescription) external pure returns (bytes memory);", + "visibility": "external", + "mutability": "pure", + "signature": "parseTomlType(string,string,string)", + "selector": "0xf9fa5cdb", + "selectorBytes": [ + 249, + 250, + 92, + 219 + ] + }, + "group": "toml", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "parseTomlUint", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index fef210bae..a29b0cf80 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -2134,6 +2134,19 @@ interface Vm { pure returns (bytes32[] memory); + /// Parses a string of TOML data and coerces it to type corresponding to `typeDescription`. + #[cheatcode(group = Toml)] + function parseTomlType(string calldata toml, string calldata typeDescription) external pure returns (bytes memory); + /// Parses a string of TOML data at `key` and coerces it to type corresponding to `typeDescription`. + #[cheatcode(group = Toml)] + function parseTomlType(string calldata toml, string calldata key, string calldata typeDescription) external pure returns (bytes memory); + /// Parses a string of TOML data at `key` and coerces it to type array corresponding to `typeDescription`. + #[cheatcode(group = Toml)] + function parseTomlTypeArray(string calldata toml, string calldata key, string calldata typeDescription) + external + pure + returns (bytes memory); + /// Returns an array of all the keys in a TOML table. #[cheatcode(group = Toml)] function parseTomlKeys(string calldata toml, string calldata key) external pure returns (string[] memory keys); diff --git a/crates/cheatcodes/src/json.rs b/crates/cheatcodes/src/json.rs index dad879e83..ba32b4ebe 100644 --- a/crates/cheatcodes/src/json.rs +++ b/crates/cheatcodes/src/json.rs @@ -654,7 +654,7 @@ fn serialize_json( } /// Resolves a [DynSolType] from user input. -fn resolve_type(type_description: &str) -> Result { +pub(super) fn resolve_type(type_description: &str) -> Result { if let Ok(ty) = DynSolType::parse(type_description) { return Ok(ty); }; diff --git a/crates/cheatcodes/src/toml.rs b/crates/cheatcodes/src/toml.rs index e83a18390..b55ef2d16 100644 --- a/crates/cheatcodes/src/toml.rs +++ b/crates/cheatcodes/src/toml.rs @@ -3,12 +3,13 @@ use crate::{ json::{ canonicalize_json_path, check_json_key_exists, parse_json, parse_json_coerce, - parse_json_keys, + parse_json_keys, resolve_type, }, Cheatcode, Cheatcodes, Result, Vm::*, }; use alloy_dyn_abi::DynSolType; +use alloy_sol_types::SolValue; use foundry_common::fs; use foundry_config::fs_permissions::FsAccessKind; use serde_json::Value as JsonValue; @@ -133,6 +134,28 @@ impl Cheatcode for parseTomlBytes32ArrayCall { } } +impl Cheatcode for parseTomlType_0Call { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { toml, typeDescription } = self; + parse_toml_coerce(toml, "$", &resolve_type(typeDescription)?).map(|v| v.abi_encode()) + } +} + +impl Cheatcode for parseTomlType_1Call { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { toml, key, typeDescription } = self; + parse_toml_coerce(toml, key, &resolve_type(typeDescription)?).map(|v| v.abi_encode()) + } +} + +impl Cheatcode for parseTomlTypeArrayCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { toml, key, typeDescription } = self; + let ty = resolve_type(typeDescription)?; + parse_toml_coerce(toml, key, &DynSolType::Array(Box::new(ty))).map(|v| v.abi_encode()) + } +} + impl Cheatcode for parseTomlKeysCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { toml, key } = self; diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 4b7f3fe42..7316521ec 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -319,6 +319,9 @@ interface Vm { function parseTomlKeys(string calldata toml, string calldata key) external pure returns (string[] memory keys); function parseTomlString(string calldata toml, string calldata key) external pure returns (string memory); function parseTomlStringArray(string calldata toml, string calldata key) external pure returns (string[] memory); + function parseTomlTypeArray(string calldata toml, string calldata key, string calldata typeDescription) external pure returns (bytes memory); + function parseTomlType(string calldata toml, string calldata typeDescription) external pure returns (bytes memory); + function parseTomlType(string calldata toml, string calldata key, string calldata typeDescription) external pure returns (bytes memory); function parseTomlUint(string calldata toml, string calldata key) external pure returns (uint256); function parseTomlUintArray(string calldata toml, string calldata key) external pure returns (uint256[] memory); function parseToml(string calldata toml) external pure returns (bytes memory abiEncodedData); diff --git a/testdata/default/cheats/Toml.t.sol b/testdata/default/cheats/Toml.t.sol index a01b29af6..4b1666d4f 100644 --- a/testdata/default/cheats/Toml.t.sol +++ b/testdata/default/cheats/Toml.t.sol @@ -5,7 +5,81 @@ import "ds-test/test.sol"; import "cheats/Vm.sol"; import "../logs/console.sol"; +library TomlStructs { + address constant HEVM_ADDRESS = address(bytes20(uint160(uint256(keccak256("hevm cheat code"))))); + Vm constant vm = Vm(HEVM_ADDRESS); + + // forge eip712 testdata/default/cheats/Toml.t.sol -R 'cheats=testdata/cheats' -R 'ds-test=testdata/lib/ds-test/src' | grep ^FlatToml + string constant schema_FlatToml = + "FlatToml(uint256 a,int24[][] arr,string str,bytes b,address addr,bytes32 fixedBytes)"; + + // forge eip712 testdata/default/cheats/Toml.t.sol -R 'cheats=testdata/cheats' -R 'ds-test=testdata/lib/ds-test/src' | grep ^NestedToml + string constant schema_NestedToml = + "NestedToml(FlatToml[] members,AnotherFlatToml inner,string name)AnotherFlatToml(bytes4 fixedBytes)FlatToml(uint256 a,int24[][] arr,string str,bytes b,address addr,bytes32 fixedBytes)"; + + function deserializeFlatToml(string memory toml) internal pure returns (ParseTomlTest.FlatToml memory) { + return abi.decode(vm.parseTomlType(toml, schema_FlatToml), (ParseTomlTest.FlatToml)); + } + + function deserializeFlatToml(string memory toml, string memory path) + internal + pure + returns (ParseTomlTest.FlatToml memory) + { + return abi.decode(vm.parseTomlType(toml, path, schema_FlatToml), (ParseTomlTest.FlatToml)); + } + + function deserializeFlatTomlArray(string memory toml, string memory path) + internal + pure + returns (ParseTomlTest.FlatToml[] memory) + { + return abi.decode(vm.parseTomlTypeArray(toml, path, schema_FlatToml), (ParseTomlTest.FlatToml[])); + } + + function deserializeNestedToml(string memory toml) internal pure returns (ParseTomlTest.NestedToml memory) { + return abi.decode(vm.parseTomlType(toml, schema_NestedToml), (ParseTomlTest.NestedToml)); + } + + function deserializeNestedToml(string memory toml, string memory path) + internal + pure + returns (ParseTomlTest.NestedToml memory) + { + return abi.decode(vm.parseTomlType(toml, path, schema_NestedToml), (ParseTomlTest.NestedToml)); + } + + function deserializeNestedTomlArray(string memory toml, string memory path) + internal + pure + returns (ParseTomlTest.NestedToml[] memory) + { + return abi.decode(vm.parseTomlType(toml, path, schema_NestedToml), (ParseTomlTest.NestedToml[])); + } +} + contract ParseTomlTest is DSTest { + using TomlStructs for *; + + struct FlatToml { + uint256 a; + int24[][] arr; + string str; + bytes b; + address addr; + bytes32 fixedBytes; + } + + struct AnotherFlatToml { + bytes4 fixedBytes; + } + + struct NestedToml { + FlatToml[] members; + AnotherFlatToml inner; + string name; + } + Vm constant vm = Vm(HEVM_ADDRESS); string toml; @@ -169,20 +243,20 @@ contract ParseTomlTest is DSTest { assertEq(bytesArray[1], hex"02"); } - struct Nested { + struct NestedStruct { uint256 number; string str; } function test_nestedObject() public { bytes memory data = vm.parseToml(toml, ".nestedObject"); - Nested memory nested = abi.decode(data, (Nested)); + NestedStruct memory nested = abi.decode(data, (NestedStruct)); assertEq(nested.number, 9223372036854775807); // TOML is limited to 64-bit integers assertEq(nested.str, "NEST"); } - function test_advancedJsonPath() public { - bytes memory data = vm.parseToml(toml, ".advancedJsonPath[*].id"); + function test_advancedTomlPath() public { + bytes memory data = vm.parseToml(toml, ".advancedTomlPath[*].id"); uint256[] memory numbers = abi.decode(data, (uint256[])); assertEq(numbers[0], 1); assertEq(numbers[1], 2); @@ -225,6 +299,36 @@ contract ParseTomlTest is DSTest { vm._expectCheatcodeRevert("key \".*\" must return exactly one JSON object"); vm.parseTomlKeys(tomlString, ".*"); } + + // forge eip712 testdata/default/cheats/Toml.t.sol -R 'cheats=testdata/cheats' -R 'ds-test=testdata/lib/ds-test/src' | grep ^FlatToml + string constant schema_FlatToml = + "FlatToml(uint256 a,int24[][] arr,string str,bytes b,address addr,bytes32 fixedBytes)"; + + // forge eip712 testdata/default/cheats/Toml.t.sol -R 'cheats=testdata/cheats' -R 'ds-test=testdata/lib/ds-test/src' | grep ^NestedToml + string constant schema_NestedToml = + "NestedToml(FlatToml[] members,AnotherFlatToml inner,string name)AnotherFlatToml(bytes4 fixedBytes)FlatToml(uint256 a,int24[][] arr,string str,bytes b,address addr,bytes32 fixedBytes)"; + + function test_parseTomlType() public { + string memory readToml = vm.readFile("fixtures/Toml/nested_toml_struct.toml"); + NestedToml memory data = readToml.deserializeNestedToml(); + assertEq(data.members.length, 2); + + FlatToml memory expected = FlatToml({ + a: 200, + arr: new int24[][](0), + str: "some other string", + b: hex"0000000000000000000000000000000000000000", + addr: 0x167D91deaEEE3021161502873d3bcc6291081648, + fixedBytes: 0xed1c7beb1f00feaaaec5636950d6edb25a8d4fedc8deb2711287b64c4d27719d + }); + + assertEq(keccak256(abi.encode(data.members[1])), keccak256(abi.encode(expected))); + assertEq(bytes32(data.inner.fixedBytes), bytes32(bytes4(0x12345678))); + + FlatToml[] memory members = TomlStructs.deserializeFlatTomlArray(readToml, ".members"); + + assertEq(keccak256(abi.encode(members)), keccak256(abi.encode(data.members))); + } } contract WriteTomlTest is DSTest { @@ -238,18 +342,18 @@ contract WriteTomlTest is DSTest { json2 = "example2"; } - struct simpleJson { + struct simpleStruct { uint256 a; string b; } - struct notSimpleJson { + struct nestedStruct { uint256 a; string b; - simpleJson c; + simpleStruct c; } - function test_serializeNotSimpleToml() public { + function test_serializeNestedStructToml() public { string memory json3 = "json3"; string memory path = "fixtures/Toml/write_complex_test.toml"; vm.serializeUint(json3, "a", uint256(123)); @@ -259,14 +363,16 @@ contract WriteTomlTest is DSTest { vm.writeToml(finalJson, path); string memory toml = vm.readFile(path); bytes memory data = vm.parseToml(toml); - notSimpleJson memory decodedData = abi.decode(data, (notSimpleJson)); + nestedStruct memory decodedData = abi.decode(data, (nestedStruct)); + console.log(decodedData.a); + assertEq(decodedData.a, 123); } function test_retrieveEntireToml() public { string memory path = "fixtures/Toml/write_complex_test.toml"; string memory toml = vm.readFile(path); bytes memory data = vm.parseToml(toml, "."); - notSimpleJson memory decodedData = abi.decode(data, (notSimpleJson)); + nestedStruct memory decodedData = abi.decode(data, (nestedStruct)); console.log(decodedData.a); assertEq(decodedData.a, 123); } @@ -294,7 +400,7 @@ contract WriteTomlTest is DSTest { string memory toml = vm.readFile(path); bytes memory data = vm.parseToml(toml); - simpleJson memory decodedData = abi.decode(data, (simpleJson)); + simpleStruct memory decodedData = abi.decode(data, (simpleStruct)); assertEq(decodedData.a, 123); assertEq(decodedData.b, "test"); @@ -303,7 +409,7 @@ contract WriteTomlTest is DSTest { // read again toml = vm.readFile(path); data = vm.parseToml(toml, ".b"); - decodedData = abi.decode(data, (simpleJson)); + decodedData = abi.decode(data, (simpleStruct)); assertEq(decodedData.a, 123); assertEq(decodedData.b, "test"); diff --git a/testdata/fixtures/Toml/nested_toml_struct.toml b/testdata/fixtures/Toml/nested_toml_struct.toml new file mode 100644 index 000000000..3cef0b7ba --- /dev/null +++ b/testdata/fixtures/Toml/nested_toml_struct.toml @@ -0,0 +1,23 @@ +name = "test" + +[[members]] +a = 100 +arr = [ + [1, -2, -5], + [1000, 2000, 0] +] +str = "some string" +b = "0x" +addr = "0x0000000000000000000000000000000000000000" +fixedBytes = "0x8ae3fc6bd1b150a73ec4afe3ef136fa2f88e9c96131c883c5e4a4714811c1598" + +[[members]] +a = 200 +arr = [] +str = "some other string" +b = "0x0000000000000000000000000000000000000000" +addr = "0x167D91deaEEE3021161502873d3bcc6291081648" +fixedBytes = "0xed1c7beb1f00feaaaec5636950d6edb25a8d4fedc8deb2711287b64c4d27719d" + +[inner] +fixedBytes = "0x12345678" diff --git a/testdata/fixtures/Toml/test.toml b/testdata/fixtures/Toml/test.toml index ce735b8f1..806dc2224 100644 --- a/testdata/fixtures/Toml/test.toml +++ b/testdata/fixtures/Toml/test.toml @@ -43,8 +43,8 @@ bytesStringArray = ["0x01", "0x02"] number = 9223372036854775807 # TOML is limited to 64-bit integers str = "NEST" -[[advancedJsonPath]] +[[advancedTomlPath]] id = 1 -[[advancedJsonPath]] +[[advancedTomlPath]] id = 2 From a33fc1d698182969df4fe77f57967db3b2ed2e75 Mon Sep 17 00:00:00 2001 From: Jennifer Date: Fri, 20 Sep 2024 21:32:28 +0200 Subject: [PATCH 005/100] Log address in checksum format (#8915) Co-authored-by: jenpaff --- crates/anvil/src/eth/backend/mem/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 0f28e28a5..53dc9898f 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -1068,7 +1068,7 @@ impl Backend { // log some tx info node_info!(" Transaction: {:?}", info.transaction_hash); if let Some(contract) = &info.contract_address { - node_info!(" Contract created: {contract:?}"); + node_info!(" Contract created: {contract}"); } node_info!(" Gas used: {}", receipt.cumulative_gas_used()); if !info.exit.is_ok() { From 09c8e753e1cc5ce6c68a4d04f33acfcdd42f673c Mon Sep 17 00:00:00 2001 From: Qiwei Yang Date: Sat, 21 Sep 2024 15:46:13 +0800 Subject: [PATCH 006/100] improve description of `--flamechart` and `--flamegraph` (#8917) improve description of --flamechart and --flamegraph, explaining the difference --- crates/forge/bin/cmd/test/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 3bc659ea0..0b8a0995b 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -86,10 +86,16 @@ pub struct TestArgs { debug: Option, /// Generate a flamegraph for a single test. Implies `--decode-internal`. + /// + /// A flame graph is used to visualize which functions or operations within the smart contract + /// are consuming the most gas overall in a sorted manner. #[arg(long, conflicts_with = "flamechart")] flamegraph: bool, /// Generate a flamechart for a single test. Implies `--decode-internal`. + /// + /// A flame chart shows the gas usage over time, illustrating when each function is + /// called (execution order) and how much gas it consumes at each point in the timeline. #[arg(long, conflicts_with = "flamegraph")] flamechart: bool, From 90541f054f1666547a4869eed74751a7463b8571 Mon Sep 17 00:00:00 2001 From: Qiwei Yang Date: Sat, 21 Sep 2024 19:17:17 +0800 Subject: [PATCH 007/100] fix(forge): improve `test --debug` doc (#8918) improve doc --- crates/forge/bin/cmd/test/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 0b8a0995b..ac0d70691 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -69,8 +69,8 @@ pub struct TestArgs { /// 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 - /// works the same as --match-test. + /// The argument passed to this flag is the **regex** of the test function signature you want + /// to run, and it works the same as --match-test. /// /// If more than one test matches your specified criteria, you must add additional filters /// until only one test is found (see --match-contract and --match-path). From 1f9c77a366b913e762c12a2d6b6e21623f637b0f Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 21 Sep 2024 16:48:50 +0200 Subject: [PATCH 008/100] chore: use dyn InspectorExt in Backend (#8919) --- crates/evm/core/src/backend/mod.rs | 24 ++++++++++++++---------- crates/evm/core/src/lib.rs | 3 ++- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index e98ac00c5..43e7bd73e 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -868,7 +868,7 @@ 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::())?; - for tx in full_block.transactions.clone().into_transactions() { + for tx in full_block.inner.transactions.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) || @@ -885,7 +885,7 @@ impl Backend { trace!(tx=?tx.hash, "committing transaction"); commit_transaction( - tx, + &tx.inner, env.clone(), journaled_state, fork, @@ -1235,8 +1235,12 @@ impl DatabaseExt for Backend { fork.db.db.get_transaction(transaction)? }; - // 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 + // 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) = self.get_block_number_and_block_for_transaction(id, transaction)?; let mut env = env.clone(); @@ -1245,7 +1249,7 @@ impl DatabaseExt for Backend { let env = self.env_with_handler_cfg(env); let fork = self.inner.get_fork_by_id_mut(id)?; commit_transaction( - tx, + &tx, env, journaled_state, fork, @@ -1903,17 +1907,17 @@ fn update_env_block(env: &mut Env, block: &Block) { } /// Executes the given transaction and commits state changes to the database _and_ the journaled -/// state, with an optional inspector -fn commit_transaction>( - tx: WithOtherFields, +/// state, with an inspector. +fn commit_transaction( + tx: &Transaction, mut env: EnvWithHandlerCfg, journaled_state: &mut JournaledState, fork: &mut Fork, fork_id: &ForkId, persistent_accounts: &HashSet
, - inspector: I, + inspector: &mut dyn InspectorExt, ) -> eyre::Result<()> { - configure_tx_env(&mut env.env, &tx.inner); + configure_tx_env(&mut env.env, tx); let now = Instant::now(); let res = { diff --git a/crates/evm/core/src/lib.rs b/crates/evm/core/src/lib.rs index 26b392c43..48c647865 100644 --- a/crates/evm/core/src/lib.rs +++ b/crates/evm/core/src/lib.rs @@ -45,9 +45,10 @@ pub trait InspectorExt: Inspector { false } - // Simulates `console.log` invocation. + /// Simulates `console.log` invocation. fn console_log(&mut self, _input: String) {} + /// Returns `true` if the current network is Alphanet. fn is_alphanet(&self) -> bool { false } From a301f261d9986bae659546d294468f5cacca265f Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 21 Sep 2024 17:16:51 +0200 Subject: [PATCH 009/100] chore(evm): use dyn DatabaseExt in inspect (#8921) chore(evm): use dyn DatabaseExt in inspect --- crates/evm/core/src/backend/cow.rs | 8 ++++++-- crates/evm/core/src/backend/mod.rs | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/crates/evm/core/src/backend/cow.rs b/crates/evm/core/src/backend/cow.rs index 2dcd985ae..cba792b32 100644 --- a/crates/evm/core/src/backend/cow.rs +++ b/crates/evm/core/src/backend/cow.rs @@ -62,7 +62,7 @@ impl<'a> CowBackend<'a> { /// Note: in case there are any cheatcodes executed that modify the environment, this will /// update the given `env` with the new values. #[instrument(name = "inspect", level = "debug", skip_all)] - pub fn inspect<'b, I: InspectorExt<&'b mut Self>>( + pub fn inspect<'b, I: InspectorExt<&'b mut dyn DatabaseExt>>( &'b mut self, env: &mut EnvWithHandlerCfg, inspector: I, @@ -71,7 +71,11 @@ impl<'a> CowBackend<'a> { // already, we reset the initialized state self.is_initialized = false; self.spec_id = env.handler_cfg.spec_id; - let mut evm = crate::utils::new_evm_with_inspector(self, env.clone(), inspector); + let mut evm = crate::utils::new_evm_with_inspector( + self as &mut dyn DatabaseExt, + env.clone(), + inspector, + ); let res = evm.transact().wrap_err("backend: failed while inspecting")?; diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 43e7bd73e..4d1d7d9ef 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -749,13 +749,17 @@ impl Backend { /// Note: in case there are any cheatcodes executed that modify the environment, this will /// update the given `env` with the new values. #[instrument(name = "inspect", level = "debug", skip_all)] - pub fn inspect<'a, I: InspectorExt<&'a mut Self>>( + pub fn inspect<'a, I: InspectorExt<&'a mut dyn DatabaseExt>>( &'a mut self, env: &mut EnvWithHandlerCfg, inspector: I, ) -> eyre::Result { self.initialize(env); - let mut evm = crate::utils::new_evm_with_inspector(self, env.clone(), inspector); + let mut evm = crate::utils::new_evm_with_inspector( + self as &mut dyn DatabaseExt, + env.clone(), + inspector, + ); let res = evm.transact().wrap_err("backend: failed while inspecting")?; From ed3ed155b06d05060b213e8a7fe86d4ffae42b46 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 21 Sep 2024 17:17:08 +0200 Subject: [PATCH 010/100] chore(anvil): use dyn DatabaseRef instead of generics (#8920) --- Cargo.lock | 1 - crates/anvil/Cargo.toml | 1 - crates/anvil/src/eth/api.rs | 13 ++-- crates/anvil/src/eth/backend/db.rs | 26 ++++++- crates/anvil/src/eth/backend/executor.rs | 10 +-- crates/anvil/src/eth/backend/mem/fork_db.rs | 15 +++-- .../anvil/src/eth/backend/mem/in_memory_db.rs | 4 ++ crates/anvil/src/eth/backend/mem/mod.rs | 67 +++++++++---------- crates/anvil/src/eth/backend/validate.rs | 1 - 9 files changed, 80 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4726ad042..e8dc2fee5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -873,7 +873,6 @@ dependencies = [ "anvil-rpc", "anvil-server", "async-trait", - "auto_impl", "axum", "bytes", "chrono", diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 77faec1f1..0723fdb47 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -103,7 +103,6 @@ clap = { version = "4", features = [ ], optional = true } clap_complete = { version = "4", optional = true } chrono.workspace = true -auto_impl.workspace = true ctrlc = { version = "3", optional = true } fdlimit = { version = "0.3", optional = true } clap_complete_fig = "4" diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index f856fef42..ec2d0dd6a 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -2445,7 +2445,7 @@ impl EthApi { state, )?); } - self.do_estimate_gas_with_state(request, state, block) + self.do_estimate_gas_with_state(request, &state, block) }) .await? } @@ -2453,15 +2453,12 @@ impl EthApi { /// Estimates the gas usage of the `request` with the state. /// /// This will execute the transaction request and find the best gas limit via binary search. - fn do_estimate_gas_with_state( + fn do_estimate_gas_with_state( &self, mut request: WithOtherFields, - state: D, + state: &dyn DatabaseRef, block_env: BlockEnv, - ) -> Result - where - D: DatabaseRef, - { + ) -> Result { // If the request is a simple native token transfer we can optimize // We assume it's a transfer if we have no input data. let to = request.to.as_ref().and_then(TxKind::to); @@ -2497,7 +2494,7 @@ impl EthApi { // If we have non-zero gas price, cap gas limit by sender balance if gas_price > 0 { if let Some(from) = request.from { - let mut available_funds = self.backend.get_balance_with_state(&state, from)?; + let mut available_funds = self.backend.get_balance_with_state(state, from)?; if let Some(value) = request.value { if value > available_funds { return Err(InvalidTransactionError::InsufficientFunds.into()); diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index 60565c4d5..a02f9d0e3 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -26,8 +26,11 @@ use serde::{ use std::{collections::BTreeMap, fmt, path::Path}; /// Helper trait get access to the full state data of the database -#[auto_impl::auto_impl(Box)] pub trait MaybeFullDatabase: DatabaseRef { + /// Returns a reference to the database as a `dyn DatabaseRef`. + // TODO: Required until trait upcasting is stabilized: + fn as_dyn(&self) -> &dyn DatabaseRef; + fn maybe_as_full_db(&self) -> Option<&HashMap> { None } @@ -51,6 +54,10 @@ impl<'a, T: 'a + MaybeFullDatabase + ?Sized> MaybeFullDatabase for &'a T where &'a T: DatabaseRef, { + fn as_dyn(&self) -> &dyn DatabaseRef { + T::as_dyn(self) + } + fn maybe_as_full_db(&self) -> Option<&HashMap> { T::maybe_as_full_db(self) } @@ -69,7 +76,6 @@ where } /// Helper trait to reset the DB if it's forked -#[auto_impl::auto_impl(Box)] pub trait MaybeForkedDatabase { fn maybe_reset(&mut self, _url: Option, block_number: BlockId) -> Result<(), String>; @@ -79,7 +85,6 @@ pub trait MaybeForkedDatabase { } /// This bundles all required revm traits -#[auto_impl::auto_impl(Box)] pub trait Db: DatabaseRef + Database @@ -188,6 +193,13 @@ pub trait Db: fn current_state(&self) -> StateDb; } +impl dyn Db { + // TODO: Required until trait upcasting is stabilized: + pub fn as_dbref(&self) -> &dyn DatabaseRef { + self.as_dyn() + } +} + /// Convenience impl only used to use any `Db` on the fly as the db layer for revm's CacheDB /// This is useful to create blocks without actually writing to the `Db`, but rather in the cache of /// the `CacheDB` see also @@ -230,6 +242,10 @@ impl + Send + Sync + Clone + fmt::Debug> D } impl> MaybeFullDatabase for CacheDB { + fn as_dyn(&self) -> &dyn DatabaseRef { + self + } + fn maybe_as_full_db(&self) -> Option<&HashMap> { Some(&self.accounts) } @@ -338,6 +354,10 @@ impl DatabaseRef for StateDb { } impl MaybeFullDatabase for StateDb { + fn as_dyn(&self) -> &dyn DatabaseRef { + self.0.as_dyn() + } + fn maybe_as_full_db(&self) -> Option<&HashMap> { self.0.maybe_as_full_db() } diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index c84ad5200..caa042bf4 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -89,11 +89,11 @@ pub struct ExecutedTransactions { } /// An executor for a series of transactions -pub struct TransactionExecutor<'a, Db: ?Sized, Validator: TransactionValidator> { +pub struct TransactionExecutor<'a, Db: ?Sized, V: TransactionValidator> { /// where to insert the transactions pub db: &'a mut Db, /// type used to validate before inclusion - pub validator: Validator, + pub validator: &'a V, /// all pending transactions pub pending: std::vec::IntoIter>, pub block_env: BlockEnv, @@ -111,7 +111,7 @@ pub struct TransactionExecutor<'a, Db: ?Sized, Validator: TransactionValidator> pub precompile_factory: Option>, } -impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<'a, DB, Validator> { +impl<'a, DB: Db + ?Sized, V: TransactionValidator> TransactionExecutor<'a, DB, V> { /// Executes all transactions and puts them in a new block with the provided `timestamp` pub fn execute(mut self) -> ExecutedTransactions { let mut transactions = Vec::new(); @@ -262,8 +262,8 @@ pub enum TransactionExecutionOutcome { DatabaseError(Arc, DatabaseError), } -impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator - for &'b mut TransactionExecutor<'a, DB, Validator> +impl<'a, 'b, DB: Db + ?Sized, V: TransactionValidator> Iterator + for &'b mut TransactionExecutor<'a, DB, V> { type Item = TransactionExecutionOutcome; diff --git a/crates/anvil/src/eth/backend/mem/fork_db.rs b/crates/anvil/src/eth/backend/mem/fork_db.rs index 1329de724..a5f6cf455 100644 --- a/crates/anvil/src/eth/backend/mem/fork_db.rs +++ b/crates/anvil/src/eth/backend/mem/fork_db.rs @@ -8,15 +8,14 @@ use crate::{ use alloy_primitives::{Address, B256, U256, U64}; use alloy_rpc_types::BlockId; use foundry_evm::{ - backend::{BlockchainDb, DatabaseResult, RevertSnapshotAction, StateSnapshot}, + backend::{BlockchainDb, DatabaseError, DatabaseResult, RevertSnapshotAction, StateSnapshot}, fork::database::ForkDbSnapshot, - revm::Database, + revm::{primitives::BlockEnv, Database}, }; +use revm::DatabaseRef; pub use foundry_evm::fork::database::ForkedDatabase; -use foundry_evm::revm::primitives::BlockEnv; -/// Implement the helper for the fork database impl Db for ForkedDatabase { fn insert_account(&mut self, address: Address, account: AccountInfo) { self.database_mut().insert_account(address, account) @@ -87,6 +86,10 @@ impl Db for ForkedDatabase { } impl MaybeFullDatabase for ForkedDatabase { + fn as_dyn(&self) -> &dyn DatabaseRef { + self + } + fn clear_into_snapshot(&mut self) -> StateSnapshot { let db = self.inner().db(); let accounts = std::mem::take(&mut *db.accounts.write()); @@ -118,6 +121,10 @@ impl MaybeFullDatabase for ForkedDatabase { } impl MaybeFullDatabase for ForkDbSnapshot { + fn as_dyn(&self) -> &dyn DatabaseRef { + self + } + fn clear_into_snapshot(&mut self) -> StateSnapshot { std::mem::take(&mut self.snapshot) } 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 56cd3815c..f984b93bb 100644 --- a/crates/anvil/src/eth/backend/mem/in_memory_db.rs +++ b/crates/anvil/src/eth/backend/mem/in_memory_db.rs @@ -104,6 +104,10 @@ impl Db for MemDb { } impl MaybeFullDatabase for MemDb { + fn as_dyn(&self) -> &dyn DatabaseRef { + self + } + fn maybe_as_full_db(&self) -> Option<&HashMap> { Some(&self.inner.accounts) } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 53dc9898f..d8846b3b8 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -857,16 +857,19 @@ impl Backend { } /// Creates an EVM instance with optionally injected precompiles. - fn new_evm_with_inspector_ref( + #[allow(clippy::type_complexity)] + fn new_evm_with_inspector_ref<'i, 'db>( &self, - db: DB, + db: &'db dyn DatabaseRef, env: EnvWithHandlerCfg, - inspector: I, - ) -> revm::Evm<'_, I, WrapDatabaseRef> - where - DB: revm::DatabaseRef, - I: InspectorExt>, - { + inspector: &'i mut dyn InspectorExt< + WrapDatabaseRef<&'db dyn DatabaseRef>, + >, + ) -> revm::Evm< + '_, + &'i mut dyn InspectorExt>>, + WrapDatabaseRef<&'db dyn DatabaseRef>, + > { let mut evm = new_evm_with_inspector_ref(db, env, inspector); if let Some(factory) = &self.precompile_factory { inject_precompiles(&mut evm, factory.precompiles()); @@ -892,7 +895,7 @@ impl Backend { let db = self.db.read().await; let mut inspector = self.build_inspector(); - let mut evm = self.new_evm_with_inspector_ref(&**db, env, &mut inspector); + let mut evm = self.new_evm_with_inspector_ref(db.as_dyn(), env, &mut inspector); let ResultAndState { result, state } = evm.transact()?; let (exit_reason, gas_used, out, logs) = match result { ExecutionResult::Success { reason, gas_used, logs, output, .. } => { @@ -1011,7 +1014,7 @@ impl Backend { env.block.timestamp = U256::from(self.time.next_timestamp()); let executor = TransactionExecutor { - db: &mut *db, + db: &mut **db, validator: self, pending: pool_transactions.into_iter(), block_env: env.block.clone(), @@ -1151,10 +1154,10 @@ impl Backend { self.with_database_at(block_request, |state, block| { let block_number = block.number.to::(); let (exit, out, gas, state) = match overrides { - None => self.call_with_state(state, request, fee_details, block), + None => self.call_with_state(state.as_dyn(), request, fee_details, block), Some(overrides) => { let state = state::apply_state_override(overrides.into_iter().collect(), state)?; - self.call_with_state(state, request, fee_details, block) + self.call_with_state(state.as_dyn(), request, fee_details, block) }, }?; trace!(target: "backend", "call return {:?} out: {:?} gas {} on block {}", exit, out, gas, block_number); @@ -1263,16 +1266,13 @@ impl Backend { inspector } - pub fn call_with_state( + pub fn call_with_state( &self, - state: D, + state: &dyn DatabaseRef, request: WithOtherFields, fee_details: FeeDetails, block_env: BlockEnv, - ) -> Result<(InstructionResult, Option, u128, State), BlockchainError> - where - D: DatabaseRef, - { + ) -> Result<(InstructionResult, Option, u128, State), BlockchainError> { let mut inspector = self.build_inspector(); let env = self.build_call_env(request, fee_details, block_env); @@ -1319,8 +1319,11 @@ impl Backend { ); let env = self.build_call_env(request, fee_details, block); - let mut evm = - self.new_evm_with_inspector_ref(state, env, &mut inspector); + let mut evm = self.new_evm_with_inspector_ref( + state.as_dyn(), + env, + &mut inspector, + ); let ResultAndState { result, state: _ } = evm.transact()?; drop(evm); @@ -1352,7 +1355,7 @@ impl Backend { .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); + let mut evm = self.new_evm_with_inspector_ref(state.as_dyn(), env, &mut inspector); let ResultAndState { result, state: _ } = evm.transact()?; let (exit_reason, gas_used, out) = match result { @@ -1381,16 +1384,13 @@ impl Backend { .await? } - pub fn build_access_list_with_state( + pub fn build_access_list_with_state( &self, - state: D, + state: &dyn DatabaseRef, request: WithOtherFields, fee_details: FeeDetails, block_env: BlockEnv, - ) -> Result<(InstructionResult, Option, u64, AccessList), BlockchainError> - where - D: DatabaseRef, - { + ) -> Result<(InstructionResult, Option, u64, AccessList), BlockchainError> { let from = request.from.unwrap_or_default(); let to = if let Some(TxKind::Call(to)) = request.to { to @@ -1911,7 +1911,7 @@ impl Backend { let db = self.db.read().await; let block = self.env.read().block.clone(); - Ok(f(Box::new(&*db), block)) + Ok(f(Box::new(&**db), block)) } pub async fn storage_at( @@ -1937,17 +1937,14 @@ impl Backend { address: Address, block_request: Option, ) -> Result { - self.with_database_at(block_request, |db, _| self.get_code_with_state(db, address)).await? + self.with_database_at(block_request, |db, _| self.get_code_with_state(&db, address)).await? } - pub fn get_code_with_state( + pub fn get_code_with_state( &self, - state: D, + state: &dyn DatabaseRef, address: Address, - ) -> Result - where - D: DatabaseRef, - { + ) -> Result { trace!(target: "backend", "get code for {:?}", address); let account = state.basic_ref(address)?.unwrap_or_default(); if account.code_hash == KECCAK_EMPTY { diff --git a/crates/anvil/src/eth/backend/validate.rs b/crates/anvil/src/eth/backend/validate.rs index 650ce24a5..eca3fd9e3 100644 --- a/crates/anvil/src/eth/backend/validate.rs +++ b/crates/anvil/src/eth/backend/validate.rs @@ -6,7 +6,6 @@ use foundry_evm::revm::primitives::{AccountInfo, EnvWithHandlerCfg}; /// A trait for validating transactions #[async_trait::async_trait] -#[auto_impl::auto_impl(&, Box)] pub trait TransactionValidator { /// Validates the transaction's validity when it comes to nonce, payment /// From 0380ca77515c27e7b92bf265b9bbeffe032faef9 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 21 Sep 2024 17:29:37 +0200 Subject: [PATCH 011/100] chore(doc): remove auto_impl (#8922) --- Cargo.lock | 1 - crates/doc/Cargo.toml | 1 - crates/doc/src/writer/as_doc.rs | 5 ++--- crates/doc/src/writer/buf_writer.rs | 2 +- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e8dc2fee5..5b6b19ac5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3337,7 +3337,6 @@ name = "forge-doc" version = "0.2.0" dependencies = [ "alloy-primitives", - "auto_impl", "derive_more 1.0.0", "eyre", "forge-fmt", diff --git a/crates/doc/Cargo.toml b/crates/doc/Cargo.toml index 0dcd40747..3ebc7f8da 100644 --- a/crates/doc/Cargo.toml +++ b/crates/doc/Cargo.toml @@ -20,7 +20,6 @@ foundry-config.workspace = true alloy-primitives.workspace = true -auto_impl.workspace = true derive_more.workspace = true eyre.workspace = true itertools.workspace = true diff --git a/crates/doc/src/writer/as_doc.rs b/crates/doc/src/writer/as_doc.rs index a21a59c11..a14f9d344 100644 --- a/crates/doc/src/writer/as_doc.rs +++ b/crates/doc/src/writer/as_doc.rs @@ -14,7 +14,6 @@ use std::path::{Path, PathBuf}; pub type AsDocResult = Result; /// A trait for formatting a parse unit as documentation. -#[auto_impl::auto_impl(&)] pub trait AsDoc { /// Formats a parse tree item into a doc string. fn as_doc(&self) -> AsDocResult; @@ -224,7 +223,7 @@ impl AsDoc for Document { // TODO: cleanup // Write function docs writer.writeln_doc( - item.comments.exclude_tags(&[CommentTag::Param, CommentTag::Return]), + &item.comments.exclude_tags(&[CommentTag::Param, CommentTag::Return]), )?; // Write function header @@ -295,7 +294,7 @@ impl Document { writer.writeln()?; // Write function docs - writer.writeln_doc(comments.exclude_tags(&[CommentTag::Param, CommentTag::Return]))?; + writer.writeln_doc(&comments.exclude_tags(&[CommentTag::Param, CommentTag::Return]))?; // Write function header writer.write_code(code)?; diff --git a/crates/doc/src/writer/buf_writer.rs b/crates/doc/src/writer/buf_writer.rs index dfec68fe2..e6109c338 100644 --- a/crates/doc/src/writer/buf_writer.rs +++ b/crates/doc/src/writer/buf_writer.rs @@ -43,7 +43,7 @@ impl BufWriter { } /// Write [AsDoc] implementation to the buffer with newline. - pub fn writeln_doc(&mut self, doc: T) -> fmt::Result { + pub fn writeln_doc(&mut self, doc: &T) -> fmt::Result { writeln!(self.buf, "{}", doc.as_doc()?) } From 1d2379a22a78d48a6f1a159868ca5978f31c5462 Mon Sep 17 00:00:00 2001 From: aganisgash Date: Sat, 21 Sep 2024 23:56:21 +0800 Subject: [PATCH 012/100] chore(docs): Update testcode path (#8923) Update testcode path --- testdata/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/testdata/README.md b/testdata/README.md index 566da52d1..5af60d647 100644 --- a/testdata/README.md +++ b/testdata/README.md @@ -4,9 +4,9 @@ A test suite that tests different aspects of Foundry. ### Structure -- [`core`](core): Tests for fundamental aspects of Foundry -- [`logs`](logs): Tests for Foundry logging capabilities -- [`cheats`](cheats): Tests for Foundry cheatcodes -- [`fuzz`](fuzz): Tests for the Foundry fuzzer -- [`trace`](trace): Tests for the Foundry tracer -- [`fork`](fork): Tests for Foundry forking capabilities +- [`core`](default/core): Tests for fundamental aspects of Foundry +- [`logs`](default/logs): Tests for Foundry logging capabilities +- [`cheats`](default/cheats): Tests for Foundry cheatcodes +- [`fuzz`](default/fuzz): Tests for the Foundry fuzzer +- [`trace`](default/trace): Tests for the Foundry tracer +- [`fork`](default/fork): Tests for Foundry forking capabilities From cd1c77aa5db15b643b9e994e4f71c71f9b9a862f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 22 Sep 2024 11:37:37 +0200 Subject: [PATCH 013/100] chore(deps): weekly `cargo update` (#8927) --- Cargo.lock | 130 +++++++++++++++++++++-------------------------------- 1 file changed, 50 insertions(+), 80 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b6b19ac5..a4621e556 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,9 +74,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-chains" -version = "0.1.31" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b68b94c159bcc2ca5f758b8663d7b00fc7c5e40569984595ddf2221b0f7f7f6e" +checksum = "805f7a974de5804f5c053edc6ca43b20883bdd3a733b3691200ae3a4b454a2db" dependencies = [ "num_enum", "serde", @@ -738,7 +738,7 @@ dependencies = [ "rustls 0.23.13", "serde_json", "tokio", - "tokio-tungstenite 0.23.1", + "tokio-tungstenite", "tracing", "ws_stream_wasm", ] @@ -1571,9 +1571,9 @@ dependencies = [ [[package]] name = "axum" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec" dependencies = [ "async-trait", "axum-core", @@ -1599,8 +1599,8 @@ dependencies = [ "sha1", "sync_wrapper 1.0.1", "tokio", - "tokio-tungstenite 0.21.0", - "tower 0.4.13", + "tokio-tungstenite", + "tower 0.5.1", "tower-layer", "tower-service", "tracing", @@ -1608,9 +1608,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +checksum = "5e6b8ba012a258d63c9adfa28b9ddcf66149da6f986c5b5452e629d5ee64bf00" dependencies = [ "async-trait", "bytes", @@ -1621,7 +1621,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.1", "tower-layer", "tower-service", "tracing", @@ -2029,9 +2029,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.17" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" dependencies = [ "clap_builder", "clap_derive", @@ -2039,9 +2039,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.17" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" dependencies = [ "anstream", "anstyle", @@ -2054,9 +2054,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.28" +version = "4.5.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b378c786d3bde9442d2c6dd7e6080b2a818db2b96e30d6e7f1b6d224eb617d3" +checksum = "8937760c3f4c60871870b8c3ee5f9b30771f792a7045c48bcbba999d7d6b3b8e" dependencies = [ "clap", ] @@ -2073,9 +2073,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck", "proc-macro2", @@ -4016,9 +4016,9 @@ dependencies = [ [[package]] name = "foundry-fork-db" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c88cb03fc4bd87856fc4d0ad38fd067f85c7c6306bf794202fc50a897449837b" +checksum = "c3ab0b0379d1838fa1f0da521ffbbac168aaf113498ea8d6c9650195cadfa736" dependencies = [ "alloy-primitives", "alloy-provider", @@ -5282,9 +5282,9 @@ dependencies = [ [[package]] name = "k256" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if", "ecdsa", @@ -6290,9 +6290,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.12" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c73c26c01b8c87956cea613c907c9d6ecffd8d18a2a5908e5de0adfaa185cea" +checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9" dependencies = [ "memchr", "thiserror", @@ -6301,9 +6301,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.12" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "664d22978e2815783adbdd2c588b455b1bd625299ce36b2a99881ac9627e6d8d" +checksum = "4d3a6e3394ec80feb3b6393c725571754c6188490265c61aaf260810d6b95aa0" dependencies = [ "pest", "pest_generator", @@ -6311,9 +6311,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.12" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2d5487022d5d33f4c30d91c22afa240ce2a644e87fe08caad974d4eab6badbe" +checksum = "94429506bde1ca69d1b5601962c73f4172ab4726571a59ea95931218cb0e930e" dependencies = [ "pest", "pest_meta", @@ -6324,9 +6324,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.7.12" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0091754bbd0ea592c4deb3a122ce8ecbb0753b738aa82bc055fcc2eccc8d8174" +checksum = "ac8a071862e93690b6e34e9a5fb8e33ff3734473ac0245b27232222c4906a33f" dependencies = [ "once_cell", "pest", @@ -6522,9 +6522,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" +checksum = "d30538d42559de6b034bc76fd6dd4c38961b1ee5c6c56e3808c50128fdbc22ce" [[package]] name = "powerfmt" @@ -6747,9 +6747,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2ecbe40f08db5c006b5764a2645f7f3f141ce756412ac9e1dd6087e6d32995" +checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" dependencies = [ "bytes", "prost-derive", @@ -6757,9 +6757,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac" +checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" dependencies = [ "anyhow", "itertools 0.13.0", @@ -6770,9 +6770,9 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60caa6738c7369b940c3d49246a8d1749323674c65cb13010134f5c9bad5b519" +checksum = "4759aa0d3a6232fb8dbdb97b61de2c20047c68aca932c7ed76da9d788508d670" dependencies = [ "prost", ] @@ -6830,7 +6830,7 @@ dependencies = [ "chrono", "indexmap 2.5.0", "newtype-uuid", - "quick-xml 0.36.1", + "quick-xml 0.36.2", "strip-ansi-escapes", "thiserror", "uuid 1.10.0", @@ -6856,9 +6856,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.36.1" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a05e2e8efddfa51a84ca47cec303fac86c8541b686d37cac5efc0e094417bc" +checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" dependencies = [ "memchr", ] @@ -7194,7 +7194,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f73010c271d53fa7904e9845338e95f3955eb1200a0355e0abfdb89c41aaa9cd" dependencies = [ "aurora-engine-modexp", - "blst", "c-kzg", "cfg-if", "k256", @@ -7755,9 +7754,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" dependencies = [ "core-foundation-sys", "libc", @@ -8640,18 +8639,6 @@ dependencies = [ "tokio-util", ] -[[package]] -name = "tokio-tungstenite" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite 0.21.0", -] - [[package]] name = "tokio-tungstenite" version = "0.23.1" @@ -8664,7 +8651,7 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", - "tungstenite 0.23.0", + "tungstenite", "webpki-roots", ] @@ -8794,8 +8781,10 @@ dependencies = [ "futures-util", "pin-project-lite", "sync_wrapper 0.1.2", + "tokio", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -8970,25 +8959,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "tungstenite" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http 1.1.0", - "httparse", - "log", - "rand", - "sha1", - "thiserror", - "url", - "utf-8", -] - [[package]] name = "tungstenite" version = "0.23.0" @@ -9103,15 +9073,15 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-xid" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "untrusted" From a592f7a9b93c7cc099341e6e9dfee3f2bb0b8748 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sun, 22 Sep 2024 16:52:20 +0200 Subject: [PATCH 014/100] chore: don't display filter used if empty (#8929) --- crates/forge/bin/cmd/test/mod.rs | 16 +++++++++++++--- crates/forge/tests/cli/test_cmd.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index ac0d70691..47a4bd075 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -494,11 +494,21 @@ impl TestArgs { self.flamechart) && num_filtered != 1 { + let action = if self.flamegraph { + "generate a flamegraph" + } else if self.flamechart { + "generate a flamechart" + } else { + "run the debugger" + }; + let filter = if filter.is_empty() { + String::new() + } else { + format!("\n\nFilter used:\n{filter}") + }; eyre::bail!( "{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}", - action = if self.flamegraph {"generate a flamegraph"} else if self.flamechart {"generate a flamechart"} else {"run the debugger"}, + Use --match-contract and --match-path to further limit the search.{filter}", ); } diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index acfec46c6..c3a5a548a 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -2174,3 +2174,31 @@ Warning: the following cheatcode(s) are deprecated and will be removed in future "#]]); } ); + +forgetest_init!(requires_single_test, |prj, cmd| { + cmd.args(["test", "--debug", "test"]).assert_failure().stderr_eq(str![[r#" +Error: +2 tests matched your criteria, but exactly 1 test must match in order to run the debugger. + +Use --match-contract and --match-path to further limit the search. + +Filter used: + match-test: `test` + + +"#]]); + cmd.forge_fuse().args(["test", "--flamegraph"]).assert_failure().stderr_eq(str![[r#" +Error: +2 tests matched your criteria, but exactly 1 test must match in order to generate a flamegraph. + +Use --match-contract and --match-path to further limit the search. + +"#]]); + cmd.forge_fuse().args(["test", "--flamechart"]).assert_failure().stderr_eq(str![[r#" +Error: +2 tests matched your criteria, but exactly 1 test must match in order to generate a flamechart. + +Use --match-contract and --match-path to further limit the search. + +"#]]); +}); From dab903633e4f01db8c604655bfe3c03a893c0827 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 23 Sep 2024 02:40:38 +0200 Subject: [PATCH 015/100] chore: use serde_json::from_str when possible (#8925) chore: use serde_json::from_str when possible --- crates/chisel/src/dispatcher.rs | 3 +-- crates/common/src/fs.rs | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/crates/chisel/src/dispatcher.rs b/crates/chisel/src/dispatcher.rs index 6afa7ccb9..0f2b7afe3 100644 --- a/crates/chisel/src/dispatcher.rs +++ b/crates/chisel/src/dispatcher.rs @@ -514,8 +514,7 @@ impl ChiselDispatcher { let json = response.json::().await.unwrap(); if json.status == "1" && json.result.is_some() { let abi = json.result.unwrap(); - let abi: serde_json::Result = - serde_json::from_slice(abi.as_bytes()); + let abi: serde_json::Result = serde_json::from_str(&abi); if let Ok(abi) = abi { let mut interface = format!( "// Interface of {}\ninterface {} {{\n", diff --git a/crates/common/src/fs.rs b/crates/common/src/fs.rs index 8ee47d2fd..45c21eba6 100644 --- a/crates/common/src/fs.rs +++ b/crates/common/src/fs.rs @@ -43,9 +43,8 @@ pub fn read_to_string(path: impl AsRef) -> Result { pub fn read_json_file(path: &Path) -> Result { // read the file into a byte array first // https://github.com/serde-rs/json/issues/160 - let bytes = read(path)?; - serde_json::from_slice(&bytes) - .map_err(|source| FsPathError::ReadJson { source, path: path.into() }) + let s = read_to_string(path)?; + serde_json::from_str(&s).map_err(|source| FsPathError::ReadJson { source, path: path.into() }) } /// Writes the object as a JSON object. From cba6e97fcdaedc2aad5c3b25f32be20ec2068e87 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 23 Sep 2024 03:20:29 +0200 Subject: [PATCH 016/100] chore: deprecate --debug regex argument (#8930) * chore: deprecate --debug regex argument * fix: enable full internal decoding if exactly one test matched --- crates/evm/traces/src/folded_stack_trace.rs | 13 +-- crates/forge/bin/cmd/test/mod.rs | 98 ++++++++------------- crates/forge/tests/cli/test_cmd.rs | 13 +-- 3 files changed, 52 insertions(+), 72 deletions(-) diff --git a/crates/evm/traces/src/folded_stack_trace.rs b/crates/evm/traces/src/folded_stack_trace.rs index de76f8e80..ee3a05afa 100644 --- a/crates/evm/traces/src/folded_stack_trace.rs +++ b/crates/evm/traces/src/folded_stack_trace.rs @@ -30,14 +30,13 @@ impl EvmFoldedStackTraceBuilder { 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); + let contract_name = node.trace.decoded.label.as_deref().unwrap_or("Contract"); format!("new {contract_name}") } else { let selector = node .selector() .map(|selector| selector.encode_hex_with_prefix()) - .unwrap_or("fallback".to_string()); + .unwrap_or_else(|| "fallback".to_string()); let signature = node.trace.decoded.call_data.as_ref().map(|dc| &dc.signature).unwrap_or(&selector); @@ -114,9 +113,11 @@ impl EvmFoldedStackTraceBuilder { /// 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 +/// ```solidity +/// function top() { child_a(); child_b() } // consumes 500 gas +/// function child_a() {} // consumes 100 gas +/// function child_b() {} // consumes 200 gas +/// ``` /// /// For execution of the `top` function looks like: /// 1. enter `top` diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 47a4bd075..0eebbb9a0 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -20,7 +20,7 @@ use foundry_cli::{ opts::CoreBuildArgs, utils::{self, LoadConfig}, }; -use foundry_common::{compile::ProjectCompiler, evm::EvmArgs, fs, shell}; +use foundry_common::{cli_warn, compile::ProjectCompiler, evm::EvmArgs, fs, shell}; use foundry_compilers::{ artifacts::output_selection::OutputSelection, compilers::{multi::MultiCompilerLanguage, CompilerSettings, Language}, @@ -67,29 +67,20 @@ pub struct TestArgs { #[arg(value_hint = ValueHint::FilePath)] pub path: Option, - /// Run a test in the debugger. - /// - /// The argument passed to this flag is the **regex** of the test function signature you want - /// to run, and it works the same as --match-test. - /// - /// If more than one test matches your specified criteria, you must add additional filters - /// until only one test is found (see --match-contract and --match-path). + /// Run a single test in the debugger. /// /// The matching test will be opened in the debugger regardless of the outcome of the test. /// /// If the matching test is a fuzz test, then it will open the debugger on the first failure - /// case. - /// If the fuzz test does not fail, it will open the debugger on the last fuzz case. - /// - /// For more fine-grained control of which fuzz case is run, see forge run. - #[arg(long, value_name = "TEST_FUNCTION")] - debug: Option, + /// case. If the fuzz test does not fail, it will open the debugger on the last fuzz case. + #[arg(long, value_name = "DEPRECATED_TEST_FUNCTION_REGEX")] + debug: Option>, /// Generate a flamegraph for a single test. Implies `--decode-internal`. /// /// A flame graph is used to visualize which functions or operations within the smart contract /// are consuming the most gas overall in a sorted manner. - #[arg(long, conflicts_with = "flamechart")] + #[arg(long)] flamegraph: bool, /// Generate a flamechart for a single test. Implies `--decode-internal`. @@ -99,18 +90,13 @@ pub struct TestArgs { #[arg(long, conflicts_with = "flamegraph")] flamechart: bool, - /// Whether to identify internal functions in traces. + /// Identify internal functions in traces. /// - /// If no argument is passed to this flag, it will trace internal functions scope and decode - /// stack parameters, but parameters stored in memory (such as bytes or arrays) will not be - /// decoded. + /// This will trace internal functions and decode stack parameters. /// - /// To decode memory parameters, you should pass an argument with a test function name, - /// similarly to --debug and --match-test. - /// - /// If more than one test matches your specified criteria, you must add additional filters - /// until only one test is found (see --match-contract and --match-path). - #[arg(long, value_name = "TEST_FUNCTION")] + /// Parameters stored in memory (such as bytes or arrays) are currently decoded only when a + /// single function is matched, similarly to `--debug`, for performance reasons. + #[arg(long, value_name = "DEPRECATED_TEST_FUNCTION_REGEX")] decode_internal: Option>, /// Print a gas report. @@ -342,19 +328,15 @@ impl TestArgs { let env = evm_opts.evm_env().await?; // Enable internal tracing for more informative flamegraph. - if should_draw { + if should_draw && self.decode_internal.is_none() { 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() { - // If function filter is provided, we enable full tracing. - InternalTraceMode::Full - } else { - // If no function filter is provided, we enable simple tracing. - InternalTraceMode::Simple - } + let decode_internal = if self.decode_internal.is_some() { + // If more than one function matched, we enable simple tracing. + // If only one function matched, we enable full tracing. This is done in `run_tests`. + InternalTraceMode::Simple } else { InternalTraceMode::None }; @@ -373,13 +355,18 @@ impl TestArgs { .alphanet(evm_opts.alphanet) .build(project_root, &output, env, evm_opts)?; - let mut maybe_override_mt = |flag, maybe_regex: Option<&Regex>| { - if let Some(regex) = maybe_regex { + let mut maybe_override_mt = |flag, maybe_regex: Option<&Option>| { + if let Some(Some(regex)) = maybe_regex { + cli_warn!( + "specifying argument for --{flag} is deprecated and will be removed in the future, \ + use --match-test instead" + ); + let test_pattern = &mut filter.args_mut().test_pattern; if test_pattern.is_some() { eyre::bail!( "Cannot specify both --{flag} and --match-test. \ - Use --match-contract and --match-path to further limit the search instead." + Use --match-contract and --match-path to further limit the search instead." ); } *test_pattern = Some(regex.clone()); @@ -387,12 +374,8 @@ impl TestArgs { Ok(()) }; - maybe_override_mt("debug", self.debug.as_ref())?; - maybe_override_mt( - "decode-internal", - self.decode_internal.as_ref().and_then(|v| v.as_ref()), - )?; + maybe_override_mt("decode-internal", self.decode_internal.as_ref())?; let libraries = runner.libraries.clone(); let mut outcome = self.run_tests(runner, config, verbosity, &filter, &output).await?; @@ -401,18 +384,10 @@ impl TestArgs { let (suite_name, test_name, mut test_result) = outcome.remove_first().ok_or_eyre("no tests were executed")?; - let arena = test_result + let (_, arena) = test_result .traces .iter_mut() - .find_map( - |(kind, arena)| { - if *kind == TraceKind::Execution { - Some(arena) - } else { - None - } - }, - ) + .find(|(kind, _)| *kind == TraceKind::Execution) .unwrap(); // Decode traces. @@ -425,6 +400,7 @@ impl TestArgs { 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 file = std::io::BufWriter::new(file); let mut options = inferno::flamegraph::Options::default(); options.title = format!("{label} {contract}::{test_name}"); @@ -435,13 +411,13 @@ impl TestArgs { } // Generate SVG. - inferno::flamegraph::from_lines(&mut options, fst.iter().map(|s| s.as_str()), file) + inferno::flamegraph::from_lines(&mut options, fst.iter().map(String::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 let Err(e) = opener::open(&file_name) { + eprintln!("\nFailed to open {file_name}; please open it manually: {e}"); } } @@ -488,12 +464,7 @@ 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()) || - self.flamegraph || - self.flamechart) && - num_filtered != 1 - { + if num_filtered != 1 && (self.debug.is_some() || self.flamegraph || self.flamechart) { let action = if self.flamegraph { "generate a flamegraph" } else if self.flamechart { @@ -512,6 +483,11 @@ impl TestArgs { ); } + // If exactly one test matched, we enable full tracing. + if num_filtered == 1 && self.decode_internal.is_some() { + runner.decode_internal = InternalTraceMode::Full; + } + if self.json { let results = runner.test_collect(filter); println!("{}", serde_json::to_string(&results)?); diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index c3a5a548a..63a4a9210 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -2176,16 +2176,12 @@ Warning: the following cheatcode(s) are deprecated and will be removed in future ); forgetest_init!(requires_single_test, |prj, cmd| { - cmd.args(["test", "--debug", "test"]).assert_failure().stderr_eq(str![[r#" + cmd.args(["test", "--debug"]).assert_failure().stderr_eq(str![[r#" Error: 2 tests matched your criteria, but exactly 1 test must match in order to run the debugger. Use --match-contract and --match-path to further limit the search. -Filter used: - match-test: `test` - - "#]]); cmd.forge_fuse().args(["test", "--flamegraph"]).assert_failure().stderr_eq(str![[r#" Error: @@ -2202,3 +2198,10 @@ Use --match-contract and --match-path to further limit the search. "#]]); }); + +forgetest_init!(deprecated_regex_arg, |prj, cmd| { + cmd.args(["test", "--decode-internal", "test_Increment"]).assert_success().stderr_eq(str![[r#" +warning: specifying argument for --decode-internal is deprecated and will be removed in the future, use --match-test instead + +"#]]); +}); From 07a1f67114565968b188d1abf42fe2c5c57bfca5 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 23 Sep 2024 20:29:26 +0300 Subject: [PATCH 017/100] chore: fixes for --all-features tests (#8937) --- crates/cast/tests/cli/main.rs | 6 ++++++ crates/forge/tests/it/inline.rs | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 01fdaef86..a93d30c91 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -702,6 +702,8 @@ to 0x91da5bf3F8Eb72724E6f50Ec6C3D199C6355c59c "#]]); + let rpc = next_http_rpc_endpoint(); + // cmd.cast_fuse() .args([ @@ -879,6 +881,8 @@ casttest!(mktx_requires_to, |_prj, cmd| { "mktx", "--private-key", "0x0000000000000000000000000000000000000000000000000000000000000001", + "--chain", + "1", ]); cmd.assert_failure().stderr_eq(str![[r#" Error: @@ -967,6 +971,8 @@ casttest!(send_requires_to, |_prj, cmd| { "send", "--private-key", "0x0000000000000000000000000000000000000000000000000000000000000001", + "--chain", + "1", ]); cmd.assert_failure().stderr_eq(str![[r#" Error: diff --git a/crates/forge/tests/it/inline.rs b/crates/forge/tests/it/inline.rs index ed7729f7f..4448f982d 100644 --- a/crates/forge/tests/it/inline.rs +++ b/crates/forge/tests/it/inline.rs @@ -1,6 +1,6 @@ //! Inline configuration tests. -use crate::test_helpers::TEST_DATA_DEFAULT; +use crate::test_helpers::{ForgeTestData, ForgeTestProfile, TEST_DATA_DEFAULT}; use forge::{result::TestKind, TestOptionsBuilder}; use foundry_config::{FuzzConfig, InvariantConfig}; use foundry_test_utils::Filter; @@ -8,7 +8,8 @@ use foundry_test_utils::Filter; #[tokio::test(flavor = "multi_thread")] async fn inline_config_run_fuzz() { let filter = Filter::new(".*", ".*", ".*inline/FuzzInlineConf.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner(); + // Fresh runner to make sure there's no persisted failure from previous tests. + let mut runner = ForgeTestData::new(ForgeTestProfile::Default).runner(); let result = runner.test_collect(&filter); let results = result .into_iter() From 232e6c795a0307e6ca4f3fbe95abab66874a429b Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 24 Sep 2024 12:59:23 +0300 Subject: [PATCH 018/100] chore: more fixes for --all-features tests (#8946) --- crates/anvil/tests/it/anvil_api.rs | 6 +++++- crates/anvil/tests/it/gas.rs | 6 +++++- crates/anvil/tests/it/proof.rs | 6 +++++- crates/anvil/tests/it/traces.rs | 6 +++++- crates/cast/tests/cli/main.rs | 1 + crates/forge/tests/cli/config.rs | 3 +++ crates/forge/tests/cli/verify_bytecode.rs | 1 + 7 files changed, 25 insertions(+), 4 deletions(-) diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index 0e8001853..803802313 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -667,7 +667,7 @@ async fn can_remove_pool_transactions() { #[tokio::test(flavor = "multi_thread")] async fn test_reorg() { - let (api, handle) = spawn(NodeConfig::test()).await; + let (api, mut handle) = spawn(NodeConfig::test()).await; let provider = handle.ws_provider(); let accounts = handle.dev_wallets().collect::>(); @@ -792,4 +792,8 @@ async fn test_reorg() { }) .await; assert!(res.is_err()); + + if let Some(signal) = handle.shutdown_signal_mut().take() { + signal.fire().unwrap(); + } } diff --git a/crates/anvil/tests/it/gas.rs b/crates/anvil/tests/it/gas.rs index ae9c9c201..be9f206d7 100644 --- a/crates/anvil/tests/it/gas.rs +++ b/crates/anvil/tests/it/gas.rs @@ -175,7 +175,7 @@ async fn test_tip_above_fee_cap() { #[tokio::test(flavor = "multi_thread")] async fn test_can_use_fee_history() { let base_fee = 50u128; - let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee))).await; + let (_api, mut handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee))).await; let provider = handle.http_provider(); for _ in 0..10 { @@ -200,4 +200,8 @@ async fn test_can_use_fee_history() { assert_eq!(latest_block.header.base_fee_per_gas.unwrap(), *latest_fee_history_fee); assert_eq!(latest_fee_history_fee, next_base_fee); } + + if let Some(signal) = handle.shutdown_signal_mut().take() { + signal.fire().unwrap(); + } } diff --git a/crates/anvil/tests/it/proof.rs b/crates/anvil/tests/it/proof.rs index 757d36082..33c51b2e0 100644 --- a/crates/anvil/tests/it/proof.rs +++ b/crates/anvil/tests/it/proof.rs @@ -122,7 +122,7 @@ async fn test_storage_proof() { #[tokio::test(flavor = "multi_thread")] async fn can_get_random_account_proofs() { - let (api, _handle) = spawn(NodeConfig::test()).await; + let (api, mut handle) = spawn(NodeConfig::test()).await; for acc in std::iter::repeat_with(Address::random).take(10) { let _ = api @@ -130,4 +130,8 @@ async fn can_get_random_account_proofs() { .await .unwrap_or_else(|_| panic!("Failed to get proof for {acc:?}")); } + + if let Some(signal) = handle.shutdown_signal_mut().take() { + signal.fire().unwrap(); + } } diff --git a/crates/anvil/tests/it/traces.rs b/crates/anvil/tests/it/traces.rs index aaa2ca298..9ab304541 100644 --- a/crates/anvil/tests/it/traces.rs +++ b/crates/anvil/tests/it/traces.rs @@ -722,7 +722,7 @@ async fn test_trace_address_fork2() { #[tokio::test(flavor = "multi_thread")] async fn test_trace_filter() { - let (api, handle) = spawn(NodeConfig::test()).await; + let (api, mut handle) = spawn(NodeConfig::test()).await; let provider = handle.ws_provider(); let accounts = handle.dev_wallets().collect::>(); @@ -859,4 +859,8 @@ async fn test_trace_filter() { let traces = api.trace_filter(tracer).await.unwrap(); assert_eq!(traces.len(), 5); + + if let Some(signal) = handle.shutdown_signal_mut().take() { + signal.fire().unwrap(); + } } diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index a93d30c91..8c928b3a8 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1022,6 +1022,7 @@ casttest!(storage, |_prj, cmd| { "#]]); + let rpc = next_http_rpc_endpoint(); cmd.cast_fuse() .args(["storage", usdt, total_supply_slot, "--rpc-url", &rpc, "--block", block_after]) .assert_success() diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 1ddd40272..3d3feba67 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -205,6 +205,7 @@ forgetest_init!(can_override_config, |prj, cmd| { ); // env vars work + std::env::remove_var("DAPP_REMAPPINGS"); 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())); assert_eq!( @@ -517,6 +518,7 @@ forgetest_init!(can_detect_lib_foundry_toml, |prj, cmd| { ); // create a new lib directly in the `lib` folder with a remapping + std::env::remove_var("DAPP_REMAPPINGS"); let mut config = config; config.remappings = vec![Remapping::from_str("nested/=lib/nested").unwrap().into()]; let nested = prj.paths().libraries[0].join("nested-lib"); @@ -524,6 +526,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 config = cmd.config(); let remappings = config.remappings.iter().cloned().map(Remapping::from).collect::>(); similar_asserts::assert_eq!( diff --git a/crates/forge/tests/cli/verify_bytecode.rs b/crates/forge/tests/cli/verify_bytecode.rs index 7e615b565..398ecb52d 100644 --- a/crates/forge/tests/cli/verify_bytecode.rs +++ b/crates/forge/tests/cli/verify_bytecode.rs @@ -33,6 +33,7 @@ fn test_verify_bytecode( prj.add_source(contract_name, &source_code).unwrap(); prj.write_config(config); + let etherscan_key = next_mainnet_etherscan_api_key(); let mut args = vec![ "verify-bytecode", addr, From c9d7b48fb0cdddc33c61db82fc3a94dd7e602c9e Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Tue, 24 Sep 2024 15:32:01 +0530 Subject: [PATCH 019/100] fix(`anvil`): handle OP deposit txs in `TypedTransaction` and `PoolTransaction` conversion (#8942) * fix(`anvil`): handle OP deposit tx in TypeTransaction conversion. * nits * clippy * test Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * nits --------- Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> --- crates/anvil/core/src/eth/transaction/mod.rs | 36 ++++++++++++++++++-- crates/anvil/src/config.rs | 5 +-- crates/anvil/src/eth/pool/transactions.rs | 5 +-- crates/anvil/tests/it/fork.rs | 20 +++++++++++ 4 files changed, 59 insertions(+), 7 deletions(-) diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 128f6e9cd..463c3de13 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -1003,11 +1003,41 @@ impl TypedTransaction { } } -impl TryFrom for TypedTransaction { +impl TryFrom> for TypedTransaction { type Error = ConversionError; - fn try_from(tx: RpcTransaction) -> Result { - // TODO(sergerad): Handle Arbitrum system transactions? + fn try_from(tx: WithOtherFields) -> Result { + if tx.transaction_type.is_some_and(|t| t == 0x7E) { + let mint = tx + .other + .get_deserialized::("mint") + .ok_or(ConversionError::Custom("MissingMint".to_string()))? + .map_err(|_| ConversionError::Custom("Cannot deserialize mint".to_string()))?; + + let source_hash = tx + .other + .get_deserialized::("sourceHash") + .ok_or(ConversionError::Custom("MissingSourceHash".to_string()))? + .map_err(|_| { + ConversionError::Custom("Cannot deserialize source hash".to_string()) + })?; + + let deposit = DepositTransaction { + nonce: tx.nonce, + is_system_tx: true, + from: tx.from, + kind: tx.to.map(TxKind::Call).unwrap_or(TxKind::Create), + value: tx.value, + gas_limit: tx.gas, + input: tx.input.clone(), + mint, + source_hash, + }; + + return Ok(Self::Deposit(deposit)); + } + + let tx = tx.inner; match tx.transaction_type.unwrap_or_default().try_into()? { TxType::Legacy => { let legacy = TxLegacy { diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index cafdf1695..fd26e1ffd 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -1300,8 +1300,9 @@ async fn derive_block_and_transactions( // Convert the transactions to PoolTransactions let force_transactions = filtered_transactions .iter() - .map(|&transaction| PoolTransaction::try_from(transaction.clone().inner)) - .collect::, _>>()?; + .map(|&transaction| PoolTransaction::try_from(transaction.clone())) + .collect::, _>>() + .map_err(|e| eyre::eyre!("Err converting to pool transactions {e}"))?; Ok((transaction_block_number.saturating_sub(1), Some(force_transactions))) } } diff --git a/crates/anvil/src/eth/pool/transactions.rs b/crates/anvil/src/eth/pool/transactions.rs index f0987572b..a1b7beb68 100644 --- a/crates/anvil/src/eth/pool/transactions.rs +++ b/crates/anvil/src/eth/pool/transactions.rs @@ -1,6 +1,7 @@ use crate::eth::{error::PoolError, util::hex_fmt_many}; use alloy_primitives::{Address, TxHash}; use alloy_rpc_types::Transaction as RpcTransaction; +use alloy_serde::WithOtherFields; use anvil_core::eth::transaction::{PendingTransaction, TypedTransaction}; use parking_lot::RwLock; use std::{ @@ -116,9 +117,9 @@ impl fmt::Debug for PoolTransaction { } } -impl TryFrom for PoolTransaction { +impl TryFrom> for PoolTransaction { type Error = eyre::Error; - fn try_from(transaction: RpcTransaction) -> Result { + fn try_from(transaction: WithOtherFields) -> Result { let typed_transaction = TypedTransaction::try_from(transaction)?; let pending_transaction = PendingTransaction::new(typed_transaction)?; Ok(Self { diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index ad2ed1e04..0d1c4f824 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -166,6 +166,26 @@ async fn test_fork_eth_get_nonce() { assert_eq!(api_nonce, provider_nonce); } +#[tokio::test(flavor = "multi_thread")] +async fn test_fork_optimism_with_transaction_hash() { + use std::str::FromStr; + + // Fork to a block with a specific transaction + let fork_tx_hash = + TxHash::from_str("fcb864b5a50f0f0b111dbbf9e9167b2cb6179dfd6270e1ad53aac6049c0ec038") + .unwrap(); + let (api, _handle) = spawn( + NodeConfig::test() + .with_eth_rpc_url(Some(rpc::next_rpc_endpoint(NamedChain::Optimism))) + .with_fork_transaction_hash(Some(fork_tx_hash)), + ) + .await; + + // Make sure the fork starts from previous block + let block_number = api.block_number().unwrap().to::(); + assert_eq!(block_number, 125777954 - 1); +} + #[tokio::test(flavor = "multi_thread")] async fn test_fork_eth_fee_history() { let (api, handle) = spawn(fork_config()).await; From df2203cbb7c7945025c80a46b167b5a4fd118e94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=BBuk?= Date: Tue, 24 Sep 2024 15:09:18 +0200 Subject: [PATCH 020/100] feat(cast): add contract creation bytecodes to traces (#8941) --- Cargo.lock | 8 ++++---- crates/cast/bin/cmd/call.rs | 2 +- crates/cast/bin/cmd/run.rs | 11 ++++++++++- crates/cli/src/utils/cmd.rs | 14 ++++++++++---- crates/evm/traces/src/lib.rs | 10 +++++++++- 5 files changed, 34 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a4621e556..f2c3d9e68 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6762,7 +6762,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools 0.11.0", "proc-macro2", "quote", "syn 2.0.77", @@ -7161,9 +7161,9 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48294aab02ed5d1940ad9b06f2a3230c3f0d98db6eacd618878cf143e204f6b0" +checksum = "b57b33a24b5b8b8efa1da3f60d44f02d6e649f06ef925d7780723ff14ff55321" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -9430,7 +9430,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index 5b1e1ff65..9afc7ff5a 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -200,7 +200,7 @@ impl CallArgs { ), }; - handle_traces(trace, &config, chain, labels, debug, decode_internal).await?; + handle_traces(trace, &config, chain, labels, debug, decode_internal, false).await?; return Ok(()); } diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index b67c0ed11..7d7c922b4 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -242,7 +242,16 @@ impl RunArgs { } }; - handle_traces(result, &config, chain, self.label, self.debug, self.decode_internal).await?; + handle_traces( + result, + &config, + chain, + self.label, + self.debug, + self.decode_internal, + self.verbose, + ) + .await?; Ok(()) } diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index 1e4af95df..8a4bff729 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -17,7 +17,8 @@ use foundry_evm::{ debug::DebugTraceIdentifier, decode_trace_arena, identifier::{EtherscanIdentifier, SignaturesIdentifier}, - render_trace_arena, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces, + render_trace_arena_with_bytecodes, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, + Traces, }, }; use std::{ @@ -361,6 +362,7 @@ pub async fn handle_traces( labels: Vec, debug: bool, decode_internal: bool, + verbose: bool, ) -> Result<()> { let labels = labels.iter().filter_map(|label_str| { let mut iter = label_str.split(':'); @@ -410,19 +412,23 @@ pub async fn handle_traces( .build(); debugger.try_run()?; } else { - print_traces(&mut result, &decoder).await?; + print_traces(&mut result, &decoder, verbose).await?; } Ok(()) } -pub async fn print_traces(result: &mut TraceResult, decoder: &CallTraceDecoder) -> Result<()> { +pub async fn print_traces( + result: &mut TraceResult, + decoder: &CallTraceDecoder, + verbose: bool, +) -> Result<()> { let traces = result.traces.as_mut().expect("No traces found"); println!("Traces:"); for (_, arena) in traces { decode_trace_arena(arena, decoder).await?; - println!("{}", render_trace_arena(arena)); + println!("{}", render_trace_arena_with_bytecodes(arena, verbose)); } println!(); diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index fb1665a5a..382e3d3ec 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -178,7 +178,15 @@ pub async fn decode_trace_arena( /// Render a collection of call traces to a string. pub fn render_trace_arena(arena: &SparsedTraceArena) -> String { - let mut w = TraceWriter::new(Vec::::new()); + render_trace_arena_with_bytecodes(arena, false) +} + +/// Render a collection of call traces to a string optionally including contract creation bytecodes. +pub fn render_trace_arena_with_bytecodes( + arena: &SparsedTraceArena, + with_bytecodes: bool, +) -> String { + let mut w = TraceWriter::new(Vec::::new()).write_bytecodes(with_bytecodes); w.write_arena(&arena.resolve_arena()).expect("Failed to write traces"); String::from_utf8(w.into_writer()).expect("trace writer wrote invalid UTF-8") } From 81fb0f60cc9f65c79eadbf50dd4b9e4907c522f7 Mon Sep 17 00:00:00 2001 From: N Date: Tue, 24 Sep 2024 09:38:37 -0400 Subject: [PATCH 021/100] fix: #8759, default (low) gas limit set even when disabled, use custom gas_limit on forks (#8933) * fix: #8759, do not set low gas price on block if disabled, use custom gas price in forks * test(fix): default block gas limit for large mine test * fix fmt * fix: optional gas_limit in as_json * fix: use option not serde_json::Value::Null * tests: base tests + config tests * fix: nits * fix: comment --- crates/anvil/src/config.rs | 80 ++++++++++++++++++------- crates/anvil/src/eth/backend/mem/mod.rs | 6 +- crates/anvil/src/lib.rs | 4 +- crates/anvil/tests/it/fork.rs | 56 ++++++++++++++++- crates/anvil/tests/it/gas.rs | 17 +++++- crates/anvil/tests/it/transaction.rs | 4 +- crates/test-utils/src/rpc.rs | 4 ++ 7 files changed, 143 insertions(+), 28 deletions(-) diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index fd26e1ffd..61ab278d0 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -19,7 +19,8 @@ use alloy_genesis::Genesis; use alloy_network::AnyNetwork; use alloy_primitives::{hex, utils::Unit, BlockNumber, TxHash, U256}; use alloy_provider::Provider; -use alloy_rpc_types::{BlockNumberOrTag, Transaction}; +use alloy_rpc_types::{Block, BlockNumberOrTag, Transaction}; +use alloy_serde::WithOtherFields; use alloy_signer::Signer; use alloy_signer_local::{ coins_bip39::{English, Mnemonic}, @@ -59,6 +60,8 @@ use yansi::Paint; pub const NODE_PORT: u16 = 8545; /// Default chain id of the node pub const CHAIN_ID: u64 = 31337; +/// The default gas limit for all transactions +pub const DEFAULT_GAS_LIMIT: u128 = 30_000_000; /// Default mnemonic for dev accounts pub const DEFAULT_MNEMONIC: &str = "test test test test test test test test test test test junk"; @@ -90,7 +93,7 @@ pub struct NodeConfig { /// Chain ID of the EVM chain pub chain_id: Option, /// Default gas limit for all txs - pub gas_limit: u128, + pub gas_limit: Option, /// If set to `true`, disables the block gas limit pub disable_block_gas_limit: bool, /// Default gas price for all txs @@ -303,7 +306,20 @@ Gas Limit {} "#, - self.gas_limit.green() + { + if self.disable_block_gas_limit { + "Disabled".to_string() + } else { + self.gas_limit.map(|l| l.to_string()).unwrap_or_else(|| { + if self.fork_choice.is_some() { + "Forked".to_string() + } else { + DEFAULT_GAS_LIMIT.to_string() + } + }) + } + } + .green() ); let _ = write!( @@ -338,6 +354,13 @@ Genesis Timestamp wallet_description.insert("mnemonic".to_string(), phrase); }; + let gas_limit = match self.gas_limit { + // if we have a disabled flag we should max out the limit + Some(_) | None if self.disable_block_gas_limit => Some(u64::MAX.to_string()), + Some(limit) => Some(limit.to_string()), + _ => None, + }; + if let Some(fork) = fork { json!({ "available_accounts": available_accounts, @@ -349,7 +372,7 @@ Genesis Timestamp "wallet": wallet_description, "base_fee": format!("{}", self.get_base_fee()), "gas_price": format!("{}", self.get_gas_price()), - "gas_limit": format!("{}", self.gas_limit), + "gas_limit": gas_limit, }) } else { json!({ @@ -358,7 +381,7 @@ Genesis Timestamp "wallet": wallet_description, "base_fee": format!("{}", self.get_base_fee()), "gas_price": format!("{}", self.get_gas_price()), - "gas_limit": format!("{}", self.gas_limit), + "gas_limit": gas_limit, "genesis_timestamp": format!("{}", self.get_genesis_timestamp()), }) } @@ -390,7 +413,7 @@ impl Default for NodeConfig { let genesis_accounts = AccountGenerator::new(10).phrase(DEFAULT_MNEMONIC).gen(); Self { chain_id: None, - gas_limit: 30_000_000, + gas_limit: None, disable_block_gas_limit: false, gas_price: None, hardfork: None, @@ -546,9 +569,7 @@ impl NodeConfig { /// Sets the gas limit #[must_use] pub fn with_gas_limit(mut self, gas_limit: Option) -> Self { - if let Some(gas_limit) = gas_limit { - self.gas_limit = gas_limit; - } + self.gas_limit = gas_limit; self } @@ -962,7 +983,7 @@ impl NodeConfig { let env = revm::primitives::Env { cfg: cfg.cfg_env, block: BlockEnv { - gas_limit: U256::from(self.gas_limit), + gas_limit: U256::from(self.gas_limit()), basefee: U256::from(self.get_base_fee()), ..Default::default() }, @@ -1140,15 +1161,7 @@ latest block number: {latest_block}" panic!("Failed to get block for block number: {fork_block_number}") }; - // we only use the gas limit value of the block if it is non-zero and the block gas - // limit is enabled, since there are networks where this is not used and is always - // `0x0` which would inevitably result in `OutOfGas` errors as soon as the evm is about to record gas, See also - let gas_limit = if self.disable_block_gas_limit || block.header.gas_limit == 0 { - u64::MAX as u128 - } else { - block.header.gas_limit - }; - + let gas_limit = self.fork_gas_limit(&block); env.block = BlockEnv { number: U256::from(fork_block_number), timestamp: U256::from(block.header.timestamp), @@ -1171,9 +1184,10 @@ latest block number: {latest_block}" // the next block let next_block_base_fee = fees.get_next_block_base_fee_per_gas( block.header.gas_used, - block.header.gas_limit, + gas_limit, block.header.base_fee_per_gas.unwrap_or_default(), ); + // update next base fee fees.set_base_fee(next_block_base_fee); } @@ -1261,6 +1275,32 @@ latest block number: {latest_block}" (db, config) } + + /// we only use the gas limit value of the block if it is non-zero and the block gas + /// limit is enabled, since there are networks where this is not used and is always + /// `0x0` which would inevitably result in `OutOfGas` errors as soon as the evm is about to record gas, See also + pub(crate) fn fork_gas_limit(&self, block: &Block>) -> u128 { + if !self.disable_block_gas_limit { + if let Some(gas_limit) = self.gas_limit { + return gas_limit; + } else if block.header.gas_limit > 0 { + return block.header.gas_limit; + } + } + + u64::MAX as u128 + } + + /// Returns the gas limit for a non forked anvil instance + /// + /// Checks the config for the `disable_block_gas_limit` flag + pub(crate) fn gas_limit(&self) -> u128 { + if self.disable_block_gas_limit { + return u64::MAX as u128; + } + + self.gas_limit.unwrap_or(DEFAULT_GAS_LIMIT) + } } /// If the fork choice is a block number, simply return it with an empty list of transactions. diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index d8846b3b8..0a11fb38c 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -441,12 +441,14 @@ impl Backend { *self.fork.write() = Some(fork); *self.env.write() = env; } else { + let gas_limit = self.node_config.read().await.fork_gas_limit(&fork_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), + gas_limit: U256::from(gas_limit), difficulty: fork_block.header.difficulty, prevrandao: Some(fork_block.header.mix_hash.unwrap_or_default()), // Keep previous `coinbase` and `basefee` value @@ -459,7 +461,7 @@ impl Backend { // 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, + gas_limit, fork_block.header.base_fee_per_gas.unwrap_or_default(), ); diff --git a/crates/anvil/src/lib.rs b/crates/anvil/src/lib.rs index c800131d2..370ba9f23 100644 --- a/crates/anvil/src/lib.rs +++ b/crates/anvil/src/lib.rs @@ -45,7 +45,9 @@ use tokio::{ mod service; mod config; -pub use config::{AccountGenerator, ForkChoice, NodeConfig, CHAIN_ID, VERSION_MESSAGE}; +pub use config::{ + AccountGenerator, ForkChoice, NodeConfig, CHAIN_ID, DEFAULT_GAS_LIMIT, VERSION_MESSAGE, +}; mod hardfork; pub use hardfork::EthereumHardfork; diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 0d1c4f824..8d5b0c588 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -6,12 +6,12 @@ use crate::{ }; use alloy_chains::NamedChain; use alloy_network::{EthereumWallet, ReceiptResponse, TransactionBuilder}; -use alloy_primitives::{address, b256, bytes, Address, Bytes, TxHash, TxKind, U256}; +use alloy_primitives::{address, b256, bytes, uint, Address, Bytes, TxHash, TxKind, U256, U64}; use alloy_provider::Provider; use alloy_rpc_types::{ anvil::Forking, request::{TransactionInput, TransactionRequest}, - BlockId, BlockNumberOrTag, + BlockId, BlockNumberOrTag, BlockTransactionsKind, }; use alloy_serde::WithOtherFields; use alloy_signer_local::PrivateKeySigner; @@ -60,6 +60,37 @@ pub fn fork_config() -> NodeConfig { .silent() } +#[tokio::test(flavor = "multi_thread")] +async fn test_fork_gas_limit_applied_from_config() { + let (api, _handle) = spawn(fork_config().with_gas_limit(Some(10_000_000_u128))).await; + + assert_eq!(api.gas_limit(), uint!(10_000_000_U256)); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_fork_gas_limit_disabled_from_config() { + let (api, handle) = spawn(fork_config().disable_block_gas_limit(true)).await; + + // see https://github.com/foundry-rs/foundry/pull/8933 + assert_eq!(api.gas_limit(), U256::from(U64::MAX)); + + // try to mine a couple blocks + let provider = handle.http_provider(); + let tx = TransactionRequest::default() + .to(Address::random()) + .value(U256::from(1337u64)) + .from(handle.dev_wallets().next().unwrap().address()); + let tx = WithOtherFields::new(tx); + let _ = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); + + let tx = TransactionRequest::default() + .to(Address::random()) + .value(U256::from(1337u64)) + .from(handle.dev_wallets().next().unwrap().address()); + let tx = WithOtherFields::new(tx); + let _ = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); +} + #[tokio::test(flavor = "multi_thread")] async fn test_spawn_fork() { let (api, _handle) = spawn(fork_config()).await; @@ -1241,6 +1272,27 @@ async fn test_arbitrum_fork_block_number() { assert_eq!(block_number, initial_block_number - 2); } +#[tokio::test(flavor = "multi_thread")] +async fn test_base_fork_gas_limit() { + // fork to get initial block for test + let (api, handle) = spawn( + fork_config() + .with_fork_block_number(None::) + .with_eth_rpc_url(Some(next_rpc_endpoint(NamedChain::Base))), + ) + .await; + + let provider = handle.http_provider(); + let block = provider + .get_block(BlockId::Number(BlockNumberOrTag::Latest), BlockTransactionsKind::Hashes) + .await + .unwrap() + .unwrap(); + + assert_eq!(api.gas_limit(), uint!(120_000_000_U256)); + assert_eq!(block.header.gas_limit, 120_000_000_u128); +} + // #[tokio::test(flavor = "multi_thread")] async fn test_fork_execution_reverted() { diff --git a/crates/anvil/tests/it/gas.rs b/crates/anvil/tests/it/gas.rs index be9f206d7..6561e2ada 100644 --- a/crates/anvil/tests/it/gas.rs +++ b/crates/anvil/tests/it/gas.rs @@ -2,7 +2,7 @@ use crate::utils::http_provider_with_signer; use alloy_network::{EthereumWallet, TransactionBuilder}; -use alloy_primitives::{Address, U256}; +use alloy_primitives::{uint, Address, U256, U64}; use alloy_provider::Provider; use alloy_rpc_types::{BlockId, TransactionRequest}; use alloy_serde::WithOtherFields; @@ -10,6 +10,21 @@ use anvil::{eth::fees::INITIAL_BASE_FEE, spawn, NodeConfig}; const GAS_TRANSFER: u128 = 21_000; +#[tokio::test(flavor = "multi_thread")] +async fn test_gas_limit_applied_from_config() { + let (api, _handle) = spawn(NodeConfig::test().with_gas_limit(Some(10_000_000))).await; + + assert_eq!(api.gas_limit(), uint!(10_000_000_U256)); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_gas_limit_disabled_from_config() { + let (api, _handle) = spawn(NodeConfig::test().disable_block_gas_limit(true)).await; + + // see https://github.com/foundry-rs/foundry/pull/8933 + assert_eq!(api.gas_limit(), U256::from(U64::MAX)); +} + #[tokio::test(flavor = "multi_thread")] async fn test_basefee_full_block() { let (_api, handle) = spawn( diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index 0827bbac1..b3e82c380 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -230,14 +230,14 @@ async fn can_reject_too_high_gas_limits() { // #[tokio::test(flavor = "multi_thread")] async fn can_mine_large_gas_limit() { - let (api, handle) = spawn(NodeConfig::test().disable_block_gas_limit(true)).await; + let (_, handle) = spawn(NodeConfig::test().disable_block_gas_limit(true)).await; let provider = handle.http_provider(); let accounts = handle.dev_wallets().collect::>(); let from = accounts[0].address(); let to = accounts[1].address(); - let gas_limit = api.gas_limit().to::(); + let gas_limit = anvil::DEFAULT_GAS_LIMIT; let amount = handle.genesis_balance().checked_div(U256::from(3u64)).unwrap(); let tx = diff --git a/crates/test-utils/src/rpc.rs b/crates/test-utils/src/rpc.rs index 305c8a1c8..6f91be10f 100644 --- a/crates/test-utils/src/rpc.rs +++ b/crates/test-utils/src/rpc.rs @@ -130,6 +130,10 @@ pub fn next_mainnet_etherscan_api_key() -> String { fn next_url(is_ws: bool, chain: NamedChain) -> String { use NamedChain::*; + if matches!(chain, NamedChain::Base) { + return "https://mainnet.base.org".to_string(); + } + let idx = next() % num_keys(); let is_infura = idx < INFURA_KEYS.len(); From 64e7237d98755b773224ec1ceef5045cf9ed55e9 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Tue, 24 Sep 2024 15:50:27 +0200 Subject: [PATCH 022/100] chore: add @grandizzy @yash-atreya @zerosnacks as codeowners (#8951) add @grandizzy @yash-atreya @zerosnacks as codeowners too --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9f19e678f..63b25bbbc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @danipopes @klkvr @mattsse +* @danipopes @klkvr @mattsse @grandizzy @yash-atreya @zerosnacks From ccb3c3726d3d10b22f055515d7331f9351908224 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 24 Sep 2024 15:55:49 +0200 Subject: [PATCH 023/100] chore(deps): update revm-inspector version in manifest (#8950) --- Cargo.lock | 32 ++++++++++++++++---------------- Cargo.toml | 3 +-- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f2c3d9e68..84194dfaf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6488,9 +6488,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "plotters" @@ -6762,7 +6762,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" dependencies = [ "anyhow", - "itertools 0.11.0", + "itertools 0.13.0", "proc-macro2", "quote", "syn 2.0.77", @@ -7161,9 +7161,9 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b57b33a24b5b8b8efa1da3f60d44f02d6e649f06ef925d7780723ff14ff55321" +checksum = "cd8e3bae0d5c824da0ac883e2521c5e83870d6521eeeccd4ee54266aa3cc1a51" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -8431,18 +8431,18 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", @@ -8910,9 +8910,9 @@ dependencies = [ [[package]] name = "tracing-tracy" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6a90519f16f55e5c62ffd5976349f10744435a919ecff83d918300575dfb69b" +checksum = "dc775fdaf33c3dfd19dc354729e65e87914bc67dcdc390ca1210807b8bee5902" dependencies = [ "tracing-core", "tracing-subscriber", @@ -8921,9 +8921,9 @@ dependencies = [ [[package]] name = "tracy-client" -version = "0.17.3" +version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373db47331c3407b343538df77eea2516884a0b126cdfb4b135acfd400015dd7" +checksum = "746b078c6a09ebfd5594609049e07116735c304671eaab06ce749854d23435bc" dependencies = [ "loom", "once_cell", @@ -8932,9 +8932,9 @@ dependencies = [ [[package]] name = "tracy-client-sys" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49cf0064dcb31c99aa1244c1b93439359e53f72ed217eef5db50abd442241e9a" +checksum = "68613466112302fdbeabc5fa55f7d57462a0b247d5a6b7d7e09401fb471a144d" dependencies = [ "cc", ] @@ -9430,7 +9430,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index c7e38cbc9..90936bb11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -172,10 +172,9 @@ foundry-fork-db = "0.3" solang-parser = "=0.3.3" ## revm -# no default features to avoid c-kzg revm = { version = "14.0.2", default-features = false } revm-primitives = { version = "9.0.2", default-features = false } -revm-inspectors = { version = "0.7", features = ["serde"] } +revm-inspectors = { version = "0.7.7", features = ["serde"] } ## ethers ethers-contract-abigen = { version = "2.0.14", default-features = false } From b09a88b0ca70acb15f1876ff6528df2ea8987e1f Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Tue, 24 Sep 2024 17:39:25 +0200 Subject: [PATCH 024/100] chore: add Makefile and `codespell` (#8948) * add makefile + codespell * update makefile * fix typos found by codespell * add codespell CI task * fix outdated spec * ignore testdata * switch default profile to dev, add strat to ignored words list --- .codespellrc | 3 + .github/workflows/test.yml | 10 +++ Makefile | 72 +++++++++++++++++++ README.md | 2 +- crates/anvil/core/src/eth/mod.rs | 2 +- crates/anvil/core/src/eth/transaction/mod.rs | 2 +- crates/anvil/src/config.rs | 2 +- crates/anvil/src/eth/api.rs | 4 +- crates/anvil/src/eth/backend/cheats.rs | 2 +- crates/anvil/src/eth/backend/mem/mod.rs | 2 +- crates/anvil/src/eth/backend/mem/storage.rs | 2 +- crates/anvil/src/eth/pool/mod.rs | 2 +- crates/cheatcodes/assets/cheatcodes.json | 2 +- crates/cheatcodes/spec/src/vm.rs | 2 +- crates/cheatcodes/src/inspector.rs | 4 +- crates/cheatcodes/src/json.rs | 2 +- crates/chisel/src/executor.rs | 2 +- crates/cli/src/utils/mod.rs | 2 +- crates/config/README.md | 2 +- crates/config/src/lib.rs | 2 +- crates/evm/core/src/utils.rs | 2 +- crates/evm/coverage/src/anchors.rs | 2 +- crates/evm/evm/src/executors/fuzz/mod.rs | 4 +- crates/evm/evm/src/executors/invariant/mod.rs | 6 +- .../evm/evm/src/executors/invariant/shrink.rs | 10 +-- crates/evm/evm/src/executors/mod.rs | 2 +- crates/evm/fuzz/src/strategies/param.rs | 4 +- crates/evm/traces/src/debug/mod.rs | 4 +- crates/fmt/src/buffer.rs | 2 +- crates/fmt/src/formatter.rs | 2 +- crates/fmt/src/visit.rs | 2 +- crates/forge/README.md | 2 +- crates/forge/bin/cmd/bind_json.rs | 2 +- crates/forge/bin/cmd/test/mod.rs | 2 +- crates/forge/src/gas_report.rs | 2 +- crates/forge/tests/cli/cmd.rs | 2 +- crates/forge/tests/cli/script.rs | 4 +- crates/script/src/broadcast.rs | 2 +- crates/script/src/progress.rs | 2 +- crates/script/src/verify.rs | 2 +- crates/sol-macro-gen/src/lib.rs | 2 +- crates/verify/src/bytecode.rs | 2 +- crates/verify/src/retry.rs | 2 +- testdata/default/repros/Issue6634.t.sol | 4 +- 44 files changed, 139 insertions(+), 54 deletions(-) create mode 100644 .codespellrc create mode 100644 Makefile diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 000000000..929bd589b --- /dev/null +++ b/.codespellrc @@ -0,0 +1,3 @@ +[codespell] +skip = .git,target,testdata,Cargo.toml,Cargo.lock +ignore-words-list = crate,ser,ratatui,Caf,froms,strat diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9e6db23ae..acb63a994 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -51,6 +51,15 @@ jobs: cache-on-failure: true - run: cargo test --workspace --doc + codespell: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - uses: codespell-project/actions-codespell@v2 + with: + skip: "*.json" + clippy: runs-on: ubuntu-latest timeout-minutes: 30 @@ -107,6 +116,7 @@ jobs: - nextest - docs - doctest + - codespell - clippy - rustfmt - forge-fmt diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..920e4de2d --- /dev/null +++ b/Makefile @@ -0,0 +1,72 @@ +# Heavily inspired by: +# - Lighthouse: https://github.com/sigp/lighthouse/blob/693886b94176faa4cb450f024696cb69cda2fe58/Makefile +# - Reth: https://github.com/paradigmxyz/reth/blob/1f642353ca083b374851ab355b5d80207b36445c/Makefile +.DEFAULT_GOAL := help + +# Cargo profile for builds. +PROFILE ?= dev + +# List of features to use when building. Can be overridden via the environment. +# No jemalloc on Windows +ifeq ($(OS),Windows_NT) + FEATURES ?= rustls aws-kms cli asm-keccak +else + FEATURES ?= jemalloc rustls aws-kms cli asm-keccak +endif + +##@ Help + +.PHONY: help +help: ## Display this help. + @awk 'BEGIN {FS = ":.*##"; printf "Usage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + +##@ Build + +.PHONY: build +build: ## Build the project. + cargo build --features "$(FEATURES)" --profile "$(PROFILE)" + +##@ Other + +.PHONY: clean +clean: ## Clean the project. + cargo clean + +## Linting + +fmt: ## Run all formatters. + cargo +nightly fmt + ./.github/scripts/format.sh --check + +lint-foundry: + RUSTFLAGS="-Dwarnings" cargo clippy --workspace --all-targets --all-features + +lint-codespell: ensure-codespell + codespell --skip "*.json" + +ensure-codespell: + @if ! command -v codespell &> /dev/null; then \ + echo "codespell not found. Please install it by running the command `pip install codespell` or refer to the following link for more information: https://github.com/codespell-project/codespell" \ + exit 1; \ + fi + +lint: ## Run all linters. + make fmt && \ + make lint-foundry && \ + make lint-codespell + +## Testing + +test-foundry: + cargo nextest run -E 'kind(test) & !test(/issue|forge_std|ext_integration/)' + +test-doc: + cargo test --doc --workspace + +test: ## Run all tests. + make test-foundry && \ + make test-doc + +pr: ## Run all tests and linters in preparation for a PR. + make lint && \ + make test \ No newline at end of file diff --git a/README.md b/README.md index 684b33556..ec0884aa2 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ By default `forge config` shows the currently selected foundry profile and its v ### DappTools Compatibility -You can re-use your `.dapprc` environment variables by running `source .dapprc` before using a Foundry tool. +You can reuse your `.dapprc` environment variables by running `source .dapprc` before using a Foundry tool. ### Additional Configuration diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index 70c62ed56..d53473666 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -522,7 +522,7 @@ pub enum EthRequest { EvmSetTime(U256), /// Serializes the current state (including contracts code, contract's storage, accounts - /// properties, etc.) into a savable data blob + /// properties, etc.) into a saveable data blob #[cfg_attr(feature = "serde", serde(rename = "anvil_dumpState", alias = "hardhat_dumpState"))] DumpState(#[cfg_attr(feature = "serde", serde(default))] Option>>), diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 463c3de13..25e42e001 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -672,7 +672,7 @@ pub enum TypedTransaction { /// 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 +/// This function is purely for convenience 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; diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 61ab278d0..96f4c53d5 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -1401,7 +1401,7 @@ impl PruneStateHistoryConfig { !self.enabled || self.max_memory_history.is_some() } - /// Returns tru if this setting was enabled. + /// Returns true if this setting was enabled. pub fn is_config_enabled(&self) -> bool { self.enabled } diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index ec2d0dd6a..88698e13c 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -1936,7 +1936,7 @@ impl EthApi { Ok(()) } - /// Reorg the chain to a specific depth and mine new blocks back to the cannonical height. + /// Reorg the chain to a specific depth and mine new blocks back to the canonical height. /// /// e.g depth = 3 /// A -> B -> C -> D -> E @@ -2566,7 +2566,7 @@ impl EthApi { // current midpoint, as spending any less gas would make no // sense (as the TX would still revert due to lack of gas). // - // We don't care about the reason here, as we known that trasaction is correct + // We don't care about the reason here, as we known that transaction is correct // as it succeeded earlier lowest_gas_limit = mid_gas_limit; } diff --git a/crates/anvil/src/eth/backend/cheats.rs b/crates/anvil/src/eth/backend/cheats.rs index 5b498f963..5e941d004 100644 --- a/crates/anvil/src/eth/backend/cheats.rs +++ b/crates/anvil/src/eth/backend/cheats.rs @@ -24,7 +24,7 @@ impl CheatsManager { let mut state = self.state.write(); // When somebody **explicitly** impersonates an account we need to store it so we are able // to return it from `eth_accounts`. That's why we do not simply call `is_impersonated()` - // which does not check that list when auto impersonation is enabeld. + // which does not check that list when auto impersonation is enabled. if state.impersonated_accounts.contains(&addr) { // need to check if already impersonated, so we don't overwrite the code return true diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 0a11fb38c..89cd0bd4b 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -149,7 +149,7 @@ pub struct Backend { /// which the write-lock is active depends on whether the `ForkDb` can provide all requested /// data from memory or whether it has to retrieve it via RPC calls first. This means that it /// potentially blocks for some time, even taking into account the rate limits of RPC - /// endpoints. Therefor the `Db` is guarded by a `tokio::sync::RwLock` here so calls that + /// endpoints. Therefore the `Db` is guarded by a `tokio::sync::RwLock` here so calls that /// need to read from it, while it's currently written to, don't block. E.g. a new block is /// currently mined and a new [`Self::set_storage_at()`] request is being executed. db: Arc>>, diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 441bd1cbb..9afc9f669 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -569,7 +569,7 @@ impl MinedTransaction { pub struct MinedTransactionReceipt { /// The actual json rpc receipt object pub inner: ReceiptResponse, - /// Output data fo the transaction + /// Output data for the transaction pub out: Option, } diff --git a/crates/anvil/src/eth/pool/mod.rs b/crates/anvil/src/eth/pool/mod.rs index 9ef45ace2..544d7eac9 100644 --- a/crates/anvil/src/eth/pool/mod.rs +++ b/crates/anvil/src/eth/pool/mod.rs @@ -20,7 +20,7 @@ //! used to determine whether it can be included in a block (transaction is ready) or whether it //! still _requires_ other transactions to be mined first (transaction is pending). //! A transaction is associated with the nonce of the account it's sent from. A unique identifying -//! marker for a transaction is therefor the pair `(nonce + account)`. An incoming transaction with +//! marker for a transaction is therefore the pair `(nonce + account)`. An incoming transaction with //! a `nonce > nonce on chain` will _require_ `(nonce -1, account)` first, before it is ready to be //! included in a block. //! diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 4357d82a2..d40d7353e 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -3634,7 +3634,7 @@ { "func": { "id": "deployCode_1", - "description": "Deploys a contract from an artifact file. Takes in the relative path to the json file or the path to the\nartifact in the form of :: where and parts are optional.\nAdditionaly accepts abi-encoded constructor arguments.", + "description": "Deploys a contract from an artifact file. Takes in the relative path to the json file or the path to the\nartifact in the form of :: where and parts are optional.\nAdditionally accepts abi-encoded constructor arguments.", "declaration": "function deployCode(string calldata artifactPath, bytes calldata constructorArgs) external returns (address deployedAddress);", "visibility": "external", "mutability": "", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index a29b0cf80..5fbd94ce0 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1544,7 +1544,7 @@ interface Vm { /// Deploys a contract from an artifact file. Takes in the relative path to the json file or the path to the /// artifact in the form of :: where and parts are optional. /// - /// Additionaly accepts abi-encoded constructor arguments. + /// Additionally accepts abi-encoded constructor arguments. #[cheatcode(group = Filesystem)] function deployCode(string calldata artifactPath, bytes calldata constructorArgs) external returns (address deployedAddress); diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 401966c46..5f4ee9408 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -222,7 +222,7 @@ pub struct BroadcastableTransaction { pub struct GasMetering { /// True if gas metering is paused. pub paused: bool, - /// True if gas metering was resumed or reseted during the test. + /// True if gas metering was resumed or reset 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. @@ -1196,7 +1196,7 @@ impl Inspector for Cheatcodes { call.target_address == HARDHAT_CONSOLE_ADDRESS; // Clean up pranks/broadcasts if it's not a cheatcode call end. We shouldn't do - // it for cheatcode calls because they are not appplied for cheatcodes in the `call` hook. + // it for cheatcode calls because they are not applied for cheatcodes in the `call` hook. // This should be placed before the revert handling, because we might exit early there if !cheatcode_call { // Clean up pranks diff --git a/crates/cheatcodes/src/json.rs b/crates/cheatcodes/src/json.rs index ba32b4ebe..0908f247c 100644 --- a/crates/cheatcodes/src/json.rs +++ b/crates/cheatcodes/src/json.rs @@ -594,7 +594,7 @@ fn serialize_value_as_json(value: DynSolValue) -> Result { match value { DynSolValue::Bool(b) => Ok(Value::Bool(b)), DynSolValue::String(s) => { - // Strings are allowed to contain strigified JSON objects, so we try to parse it like + // Strings are allowed to contain stringified JSON objects, so we try to parse it like // one first. if let Ok(map) = serde_json::from_str(&s) { Ok(Value::Object(map)) diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index a0b9fc391..3d5fce295 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -1161,7 +1161,7 @@ impl Type { Self::ethabi(&return_parameter.ty, Some(intermediate)).map(|p| (contract_expr.unwrap(), p)) } - /// Inverts Int to Uint and viceversa. + /// Inverts Int to Uint and vice-versa. fn invert_int(self) -> Self { match self { Self::Builtin(DynSolType::Uint(n)) => Self::Builtin(DynSolType::Int(n)), diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index f5bee0a77..736793e67 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -163,7 +163,7 @@ pub fn block_on(future: F) -> F::Output { /// Conditionally print a message /// -/// This macro accepts a predicate and the message to print if the predicate is tru +/// This macro accepts a predicate and the message to print if the predicate is true /// /// ```ignore /// let quiet = true; diff --git a/crates/config/README.md b/crates/config/README.md index 99f02e5e6..139c2a9f5 100644 --- a/crates/config/README.md +++ b/crates/config/README.md @@ -100,7 +100,7 @@ model_checker = { contracts = { 'a.sol' = [ ], timeout = 10000 } verbosity = 0 eth_rpc_url = "https://example.com/" -# Setting this option enables decoding of error traces from mainnet deployed / verfied contracts via etherscan +# Setting this option enables decoding of error traces from mainnet deployed / verified contracts via etherscan etherscan_api_key = "YOURETHERSCANAPIKEY" # ignore solc warnings for missing license and exceeded contract size # known error codes are: ["unreachable", "unused-return", "unused-param", "unused-var", "code-size", "shadowing", "func-mutability", "license", "pragma-solidity", "virtual-interfaces", "same-varname", "too-many-warnings", "constructor-visibility", "init-code-size", "missing-receive-ether", "unnamed-return", "transient-storage"] diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index d77e30492..452365ce9 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -2717,7 +2717,7 @@ impl Provider for OptionalStrictProfileProvider

{ figment.data().map_err(|err| { // figment does tag metadata and tries to map metadata to an error, since we use a new // figment in this provider this new figment does not know about the metadata of the - // provider and can't map the metadata to the error. Therefor we return the root error + // provider and can't map the metadata to the error. Therefore 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; diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index 27db37eaf..11f8bbb31 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -119,7 +119,7 @@ fn get_create2_factory_call_inputs(salt: U256, inputs: CreateInputs) -> CallInpu /// Used for routing certain CREATE2 invocations through [DEFAULT_CREATE2_DEPLOYER]. /// /// Overrides create hook with CALL frame if [InspectorExt::should_use_create2_factory] returns -/// true. Keeps track of overriden frames and handles outcome in the overriden insert_call_outcome +/// true. Keeps track of overridden frames and handles outcome in the overridden insert_call_outcome /// hook by inserting decoded address directly into interpreter. /// /// Should be installed after [revm::inspector_handle_register] and before any other registers. diff --git a/crates/evm/coverage/src/anchors.rs b/crates/evm/coverage/src/anchors.rs index 9e82ec1cb..c5bb8196a 100644 --- a/crates/evm/coverage/src/anchors.rs +++ b/crates/evm/coverage/src/anchors.rs @@ -71,7 +71,7 @@ pub fn find_anchor_simple( Ok(ItemAnchor { instruction: ic_pc_map.get(instruction).ok_or_else(|| { - eyre::eyre!("We found an anchor, but we cant translate it to a program counter") + eyre::eyre!("We found an anchor, but we can't translate it to a program counter") })?, item_id, }) diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index 54480181f..775b7e73e 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -88,7 +88,7 @@ impl FuzzedExecutor { let execution_data = RefCell::new(FuzzTestData::default()); let state = self.build_fuzz_state(); let dictionary_weight = self.config.dictionary.dictionary_weight.min(100); - let strat = proptest::prop_oneof![ + let strategy = proptest::prop_oneof![ 100 - dictionary_weight => fuzz_calldata(func.clone(), fuzz_fixtures), dictionary_weight => fuzz_calldata_from_state(func.clone(), &state), ]; @@ -96,7 +96,7 @@ impl FuzzedExecutor { let max_traces_to_collect = std::cmp::max(1, self.config.gas_report_samples) as usize; let show_logs = self.config.show_logs; - let run_result = self.runner.clone().run(&strat, |calldata| { + let run_result = self.runner.clone().run(&strategy, |calldata| { let fuzz_res = self.single_fuzz(address, should_fail, calldata)?; // If running with progress then increment current run. diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index b5854d1d7..2e2e06e6d 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -243,7 +243,7 @@ impl InvariantTestRun { } } -/// Wrapper around any [`Executor`] implementor which provides fuzzing support using [`proptest`]. +/// Wrapper around any [`Executor`] implementer which provides fuzzing support using [`proptest`]. /// /// After instantiation, calling `invariant_fuzz` will proceed to hammer the deployed smart /// contracts with inputs, until it finds a counterexample sequence. The provided [`TestRunner`] @@ -463,7 +463,7 @@ impl<'a> InvariantExecutor<'a> { EvmFuzzState::new(self.executor.backend().mem_db(), self.config.dictionary); // Creates the invariant strategy. - let strat = invariant_strat( + let strategy = invariant_strat( fuzz_state.clone(), targeted_senders, targeted_contracts.clone(), @@ -519,7 +519,7 @@ impl<'a> InvariantExecutor<'a> { last_call_results, self.runner.clone(), ), - strat, + strategy, )) } diff --git a/crates/evm/evm/src/executors/invariant/shrink.rs b/crates/evm/evm/src/executors/invariant/shrink.rs index 318163534..ca5196b4a 100644 --- a/crates/evm/evm/src/executors/invariant/shrink.rs +++ b/crates/evm/evm/src/executors/invariant/shrink.rs @@ -21,13 +21,13 @@ struct Shrink { /// If the failure is not reproducible then restore removed call and moves to next one. #[derive(Debug)] struct CallSequenceShrinker { - /// Length of call sequence to be shrinked. + /// Length of call sequence to be shrunk. call_sequence_len: usize, - /// Call ids contained in current shrinked sequence. + /// Call ids contained in current shrunk sequence. included_calls: VarBitSet, - /// Current shrinked call id. + /// Current shrunk call id. shrink: Shrink, - /// Previous shrinked call id. + /// Previous shrunk call id. prev_shrink: Option, } @@ -82,7 +82,7 @@ impl CallSequenceShrinker { /// Maximal shrinkage is guaranteed if the shrink_run_limit is not set to a value lower than the /// length of failed call sequence. /// -/// The shrinked call sequence always respect the order failure is reproduced as it is tested +/// The shrunk call sequence always respect the order failure is reproduced as it is tested /// top-down. pub(crate) fn shrink_sequence( failed_case: &FailedInvariantCaseData, diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 655418fad..dc923d6e0 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -576,7 +576,7 @@ impl Executor { /// Creates the environment to use when executing a transaction in a test context /// /// If using a backend with cheatcodes, `tx.gas_price` and `block.number` will be overwritten by - /// the cheatcode state inbetween calls. + /// the cheatcode state in between calls. fn build_test_env( &self, caller: Address, diff --git a/crates/evm/fuzz/src/strategies/param.rs b/crates/evm/fuzz/src/strategies/param.rs index 10b166956..7e5218fd8 100644 --- a/crates/evm/fuzz/src/strategies/param.rs +++ b/crates/evm/fuzz/src/strategies/param.rs @@ -218,12 +218,12 @@ mod tests { let func = get_func(f).unwrap(); let db = CacheDB::new(EmptyDB::default()); let state = EvmFuzzState::new(&db, FuzzDictionaryConfig::default()); - let strat = proptest::prop_oneof![ + let strategy = proptest::prop_oneof![ 60 => fuzz_calldata(func.clone(), &FuzzFixtures::default()), 40 => fuzz_calldata_from_state(func, &state), ]; let cfg = proptest::test_runner::Config { failure_persistence: None, ..Default::default() }; let mut runner = proptest::test_runner::TestRunner::new(cfg); - let _ = runner.run(&strat, |_| Ok(())); + let _ = runner.run(&strategy, |_| Ok(())); } } diff --git a/crates/evm/traces/src/debug/mod.rs b/crates/evm/traces/src/debug/mod.rs index a651a3acc..a56f4ab2b 100644 --- a/crates/evm/traces/src/debug/mod.rs +++ b/crates/evm/traces/src/debug/mod.rs @@ -110,7 +110,7 @@ impl<'a> DebugStepsWalker<'a> { loc.index() == other_loc.index() } - /// Invoked when current step is a JUMPDEST preceeded by a JUMP marked as [Jump::In]. + /// Invoked when current step is a JUMPDEST preceded by a JUMP marked as [Jump::In]. fn jump_in(&mut self) { // This usually means that this is a jump into the external function which is an // entrypoint for the current frame. We don't want to include this to avoid @@ -128,7 +128,7 @@ impl<'a> DebugStepsWalker<'a> { } } - /// Invoked when current step is a JUMPDEST preceeded by a JUMP marked as [Jump::Out]. + /// Invoked when current step is a JUMPDEST preceded by a JUMP marked as [Jump::Out]. fn jump_out(&mut self) { let Some((i, _)) = self.stack.iter().enumerate().rfind(|(_, (_, step_idx))| { self.is_same_loc(*step_idx, self.current_step) || diff --git a/crates/fmt/src/buffer.rs b/crates/fmt/src/buffer.rs index b09e9e620..9226d5f6b 100644 --- a/crates/fmt/src/buffer.rs +++ b/crates/fmt/src/buffer.rs @@ -173,7 +173,7 @@ impl FormatBuffer { 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 - // case with temporary buffers as this will be readded by the underlying IndentWriter + // case with temporary buffers as this will be re-added by the underlying IndentWriter // later on let (new_comment_state, line_start) = line .comment_state_char_indices() diff --git a/crates/fmt/src/formatter.rs b/crates/fmt/src/formatter.rs index 2ae083e5d..1dbf63eac 100644 --- a/crates/fmt/src/formatter.rs +++ b/crates/fmt/src/formatter.rs @@ -3244,7 +3244,7 @@ impl<'a, W: Write> Visitor for Formatter<'a, W> { let is_constructor = self.context.is_constructor_function(); // we can't make any decisions here regarding trailing `()` because we'd need to // find out if the `base` is a solidity modifier or an - // interface/contract therefor we we its raw content. + // interface/contract therefore we we its raw content. // we can however check if the contract `is` the `base`, this however also does // not cover all cases diff --git a/crates/fmt/src/visit.rs b/crates/fmt/src/visit.rs index b977ba5b3..3fff8893d 100644 --- a/crates/fmt/src/visit.rs +++ b/crates/fmt/src/visit.rs @@ -5,7 +5,7 @@ use crate::solang_ext::pt::*; /// A trait that is invoked while traversing the Solidity Parse Tree. /// Each method of the [Visitor] trait is a hook that can be potentially overridden. /// -/// Currently the main implementor of this trait is the [`Formatter`](crate::Formatter<'_>) struct. +/// Currently the main implementer of this trait is the [`Formatter`](crate::Formatter<'_>) struct. pub trait Visitor { type Error: std::error::Error; diff --git a/crates/forge/README.md b/crates/forge/README.md index d4cfeede2..a40a852db 100644 --- a/crates/forge/README.md +++ b/crates/forge/README.md @@ -206,7 +206,7 @@ contract MyTest { function testBarExpectedRevert() public { vm.expectRevert("My expected revert string"); - // This would fail *if* we didnt expect revert. Since we expect the revert, + // This would fail *if* we didn't expect revert. Since we expect the revert, // it doesn't, unless the revert string is wrong. foo.bar(101); } diff --git a/crates/forge/bin/cmd/bind_json.rs b/crates/forge/bin/cmd/bind_json.rs index bd2d0ea30..d01abd60b 100644 --- a/crates/forge/bin/cmd/bind_json.rs +++ b/crates/forge/bin/cmd/bind_json.rs @@ -187,7 +187,7 @@ struct StructToWrite { } impl StructToWrite { - /// Returns the name of the imported item. If struct is definied at the file level, returns the + /// Returns the name of the imported item. If struct is defined at the file level, returns the /// struct name, otherwise returns the parent contract name. fn struct_or_contract_name(&self) -> &str { self.contract_name.as_deref().unwrap_or(&self.name) diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 0eebbb9a0..6bf1ffd81 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -187,7 +187,7 @@ impl TestArgs { /// Returns sources which include any tests to be executed. /// If no filters are provided, sources are filtered by existence of test/invariant methods in - /// them, If filters are provided, sources are additionaly filtered by them. + /// them, If filters are provided, sources are additionally filtered by them. pub fn get_sources_to_compile( &self, config: &Config, diff --git a/crates/forge/src/gas_report.rs b/crates/forge/src/gas_report.rs index 4fb438844..85f53b5f5 100644 --- a/crates/forge/src/gas_report.rs +++ b/crates/forge/src/gas_report.rs @@ -79,7 +79,7 @@ impl GasReport { return; } - // Only include top-level calls which accout for calldata and base (21.000) cost. + // Only include top-level calls which account for calldata and base (21.000) cost. // Only include Calls and Creates as only these calls are isolated in inspector. if trace.depth > 1 && (trace.kind == CallKind::Call || diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 4e860bc64..1144830c5 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -984,7 +984,7 @@ Warning: SPDX license identifier not provided in source file. Before publishing, "#]]); }); -// test that `forge build` does not print `(with warnings)` if there arent any +// test that `forge build` does not print `(with warnings)` if there aren't any forgetest!(can_compile_without_warnings, |prj, cmd| { let config = Config { ignored_error_codes: vec![SolidityErrorCode::SpdxLicenseNotProvided], diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 9fc398c90..78e27d653 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -930,7 +930,7 @@ forgetest_async!(check_broadcast_log, |prj, cmd| { // Check broadcast logs // Ignore timestamp, blockHash, blockNumber, cumulativeGasUsed, effectiveGasPrice, - // transactionIndex and logIndex values since they can change inbetween runs + // transactionIndex and logIndex values since they can change in between runs let re = Regex::new(r#"((timestamp":).[0-9]*)|((blockHash":).*)|((blockNumber":).*)|((cumulativeGasUsed":).*)|((effectiveGasPrice":).*)|((transactionIndex":).*)|((logIndex":).*)"#).unwrap(); let fixtures_log = std::fs::read_to_string( @@ -954,7 +954,7 @@ forgetest_async!(check_broadcast_log, |prj, cmd| { // ); // Check sensitive logs - // Ignore port number since it can change inbetween runs + // Ignore port number since it can change in between runs let re = Regex::new(r":[0-9]+").unwrap(); let fixtures_log = std::fs::read_to_string( diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index 302be1509..ff4e4205e 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -295,7 +295,7 @@ impl BundledState { tx.set_chain_id(sequence.chain); - // Set TxKind::Create explicitly to satify `check_reqd_fields` in + // Set TxKind::Create explicitly to satisfy `check_reqd_fields` in // alloy if tx.to.is_none() { tx.set_create(); diff --git a/crates/script/src/progress.rs b/crates/script/src/progress.rs index e88885de3..819ebca99 100644 --- a/crates/script/src/progress.rs +++ b/crates/script/src/progress.rs @@ -16,7 +16,7 @@ use yansi::Paint; /// State of [ProgressBar]s displayed for the given [ScriptSequence]. #[derive(Debug)] pub struct SequenceProgressState { - /// The top spinner with containt of the format "Sequence #{id} on {network} | {status}"" + /// The top spinner with content of the format "Sequence #{id} on {network} | {status}"" top_spinner: ProgressBar, /// Progress bar with the count of transactions. txs: ProgressBar, diff --git a/crates/script/src/verify.rs b/crates/script/src/verify.rs index 10c877ce6..18ee6ccf2 100644 --- a/crates/script/src/verify.rs +++ b/crates/script/src/verify.rs @@ -89,7 +89,7 @@ impl VerifyBundle { /// Configures the chain and sets the etherscan key, if available pub fn set_chain(&mut self, config: &Config, chain: Chain) { - // If dealing with multiple chains, we need to be able to change inbetween the config + // If dealing with multiple chains, we need to be able to change in between the config // chain_id. self.etherscan.key = config.get_etherscan_api_key(Some(chain)); self.etherscan.chain = Some(chain); diff --git a/crates/sol-macro-gen/src/lib.rs b/crates/sol-macro-gen/src/lib.rs index 0202827f2..9984c6cce 100644 --- a/crates/sol-macro-gen/src/lib.rs +++ b/crates/sol-macro-gen/src/lib.rs @@ -1,4 +1,4 @@ -//! This crate constains the logic for Rust bindings generating from Solidity contracts +//! This crate contains the logic for Rust bindings generating from Solidity contracts pub mod sol_macro_gen; diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index e08cf3a5a..034bb49a6 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -468,7 +468,7 @@ impl VerifyBytecodeArgs { &transaction, )?; - // State commited using deploy_with_env, now get the runtime bytecode from the db. + // State committed 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, diff --git a/crates/verify/src/retry.rs b/crates/verify/src/retry.rs index 528fd7497..a12dde140 100644 --- a/crates/verify/src/retry.rs +++ b/crates/verify/src/retry.rs @@ -20,7 +20,7 @@ pub struct RetryArgs { )] pub retries: u32, - /// Optional delay to apply inbetween verification attempts, in seconds. + /// Optional delay to apply in between verification attempts, in seconds. #[arg( long, value_parser = RangedU64ValueParser::::new().range(0..=30), diff --git a/testdata/default/repros/Issue6634.t.sol b/testdata/default/repros/Issue6634.t.sol index 22294f6df..64d92e9d1 100644 --- a/testdata/default/repros/Issue6634.t.sol +++ b/testdata/default/repros/Issue6634.t.sol @@ -58,7 +58,7 @@ contract Issue6634Test is DSTest { assertEq(called.length, 2, "incorrect length"); assertEq(uint256(called[0].kind), uint256(Vm.AccountAccessKind.Call), "first AccountAccess is incorrect kind"); - assertEq(called[0].account, CREATE2_DEPLOYER, "first AccountAccess accout is incorrect"); + assertEq(called[0].account, CREATE2_DEPLOYER, "first AccountAccess account is incorrect"); assertEq(called[0].accessor, accessor, "first AccountAccess accessor is incorrect"); assertEq( uint256(called[1].kind), uint256(Vm.AccountAccessKind.Create), "second AccountAccess is incorrect kind" @@ -84,7 +84,7 @@ contract Issue6634Test is DSTest { assertEq(called.length, 2, "incorrect length"); assertEq(uint256(called[0].kind), uint256(Vm.AccountAccessKind.Call), "first AccountAccess is incorrect kind"); - assertEq(called[0].account, CREATE2_DEPLOYER, "first AccountAccess accout is incorrect"); + assertEq(called[0].account, CREATE2_DEPLOYER, "first AccountAccess account is incorrect"); assertEq(called[0].accessor, accessor, "first AccountAccess accessor is incorrect"); assertEq( uint256(called[1].kind), uint256(Vm.AccountAccessKind.Create), "second AccountAccess is incorrect kind" From 883bb1c39f56a525657116874e59e80c2b881b10 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 24 Sep 2024 20:48:44 +0200 Subject: [PATCH 025/100] chore: add comments for alloy-core patches (#8955) --- Cargo.toml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 90936bb11..99f079a75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -276,6 +276,18 @@ proptest = "1" comfy-table = "7" # [patch.crates-io] +## alloy-core +# alloy-dyn-abi = { path = "../../alloy-rs/core/crates/dyn-abi" } +# alloy-json-abi = { path = "../../alloy-rs/core/crates/json-abi" } +# alloy-primitives = { path = "../../alloy-rs/core/crates/primitives" } +# alloy-sol-macro = { path = "../../alloy-rs/core/crates/sol-macro" } +# alloy-sol-macro-expander = { path = "../../alloy-rs/core/crates/sol-macro-expander" } +# alloy-sol-macro-input = { path = "../../alloy-rs/core/crates/sol-macro-input" } +# alloy-sol-type-parser = { path = "../../alloy-rs/core/crates/sol-type-parser" } +# alloy-sol-types = { path = "../../alloy-rs/core/crates/sol-types" } +# syn-solidity = { path = "../../alloy-rs/core/crates/syn-solidity" } + +## alloy # 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" } From 8d5a66d90cfbf3e68b0188112898735cdd7562e9 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 25 Sep 2024 10:20:36 +0300 Subject: [PATCH 026/100] fix(coverage): better find of loc start byte position (#8958) --- crates/evm/coverage/src/analysis.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/evm/coverage/src/analysis.rs b/crates/evm/coverage/src/analysis.rs index daf676c2c..1b68c89d3 100644 --- a/crates/evm/coverage/src/analysis.rs +++ b/crates/evm/coverage/src/analysis.rs @@ -470,12 +470,14 @@ impl<'a> ContractVisitor<'a> { } fn source_location_for(&self, loc: &ast::LowFidelitySourceLocation) -> SourceLocation { + let loc_start = + self.source.char_indices().map(|(i, _)| i).nth(loc.start).unwrap_or_default(); SourceLocation { source_id: self.source_id, contract_name: self.contract_name.clone(), start: loc.start as u32, length: loc.length.map(|x| x as u32), - line: self.source[..loc.start].lines().count(), + line: self.source[..loc_start].lines().count(), } } } From ccabf8b71831ed872988bba0137fb760b18dc519 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 25 Sep 2024 13:31:23 +0300 Subject: [PATCH 027/100] chore: improve fuzz scrape bytecode test (#8953) * chore: improve fuzz scrape bytecode test * Remove duped comments, Trigger CI --- crates/forge/tests/it/fuzz.rs | 67 ++++++++++++++----- .../default/fuzz/FuzzScrapeBytecode.t.sol | 35 ---------- 2 files changed, 50 insertions(+), 52 deletions(-) delete mode 100644 testdata/default/fuzz/FuzzScrapeBytecode.t.sol diff --git a/crates/forge/tests/it/fuzz.rs b/crates/forge/tests/it/fuzz.rs index 1d810c9c5..d6b047a17 100644 --- a/crates/forge/tests/it/fuzz.rs +++ b/crates/forge/tests/it/fuzz.rs @@ -155,27 +155,60 @@ async fn test_persist_fuzz_failure() { assert_ne!(initial_calldata, new_calldata); } -#[tokio::test(flavor = "multi_thread")] -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(100u32)); - let suite_result = runner.test_collect(&filter); +forgetest_init!(test_can_scrape_bytecode, |prj, cmd| { + prj.add_source( + "FuzzerDict.sol", + r#" +// https://github.com/foundry-rs/foundry/issues/1168 +contract FuzzerDict { + // Immutables should get added to the dictionary. + address public immutable immutableOwner; + // Regular storage variables should also get added to the dictionary. + address public storageOwner; + + constructor(address _immutableOwner, address _storageOwner) { + immutableOwner = _immutableOwner; + storageOwner = _storageOwner; + } +} + "#, + ) + .unwrap(); - assert!(!suite_result.is_empty()); + prj.add_test( + "FuzzerDictTest.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; +import "src/FuzzerDict.sol"; - for (_, SuiteResult { test_results, .. }) in suite_result { - for (test_name, result) in test_results { - match test_name.as_str() { - "testImmutableOwner(address)" | "testStorageOwner(address)" => { - assert_eq!(result.status, TestStatus::Failure) - } - _ => {} - } - } +contract FuzzerDictTest is Test { + FuzzerDict fuzzerDict; + + function setUp() public { + fuzzerDict = new FuzzerDict(address(100), address(200)); + } + + /// forge-config: default.fuzz.runs = 2000 + function testImmutableOwner(address who) public { + assertTrue(who != fuzzerDict.immutableOwner()); + } + + /// forge-config: default.fuzz.runs = 2000 + function testStorageOwner(address who) public { + assertTrue(who != fuzzerDict.storageOwner()); } } + "#, + ) + .unwrap(); + + // Test that immutable address is used as fuzzed input, causing test to fail. + cmd.args(["test", "--fuzz-seed", "100", "--mt", "testImmutableOwner"]).assert_failure(); + // Test that storage address is used as fuzzed input, causing test to fail. + cmd.forge_fuse() + .args(["test", "--fuzz-seed", "100", "--mt", "testStorageOwner"]) + .assert_failure(); +}); // tests that inline max-test-rejects config is properly applied forgetest_init!(test_inline_max_test_rejects, |prj, cmd| { diff --git a/testdata/default/fuzz/FuzzScrapeBytecode.t.sol b/testdata/default/fuzz/FuzzScrapeBytecode.t.sol deleted file mode 100644 index ffded67f0..000000000 --- a/testdata/default/fuzz/FuzzScrapeBytecode.t.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; - -import "ds-test/test.sol"; - -// https://github.com/foundry-rs/foundry/issues/1168 -contract FuzzerDict { - // Immutables should get added to the dictionary. - address public immutable immutableOwner; - // Regular storage variables should also get added to the dictionary. - address public storageOwner; - - constructor(address _immutableOwner, address _storageOwner) { - immutableOwner = _immutableOwner; - storageOwner = _storageOwner; - } -} - -contract FuzzerDictTest is DSTest { - FuzzerDict fuzzerDict; - - function setUp() public { - fuzzerDict = new FuzzerDict(address(100), address(200)); - } - - // Fuzzer should try `fuzzerDict.immutableOwner()` as input, causing this to fail - function testImmutableOwner(address who) public { - assertTrue(who != fuzzerDict.immutableOwner()); - } - - // Fuzzer should try `fuzzerDict.storageOwner()` as input, causing this to fail - function testStorageOwner(address who) public { - assertTrue(who != fuzzerDict.storageOwner()); - } -} From 9a0f66ec57d65a4546b8af915238b55d536c47b6 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 25 Sep 2024 13:53:56 +0300 Subject: [PATCH 028/100] chore: fire shutdown signal on anvil node handle drop (#8947) * chore: add anvil NodeHandle.fire_shutdown_signal * Remove DAPP remappings from env vars from cli tests. * Unwrap fire shutdown * Fix clippy * track_caller on fire shutdown * fire shutdown signal on drop --- crates/anvil/src/lib.rs | 51 ++++++++++++++++++------------ crates/anvil/tests/it/anvil_api.rs | 6 +--- crates/anvil/tests/it/gas.rs | 6 +--- crates/anvil/tests/it/proof.rs | 6 +--- crates/anvil/tests/it/traces.rs | 6 +--- crates/forge/tests/cli/config.rs | 18 ----------- 6 files changed, 34 insertions(+), 59 deletions(-) diff --git a/crates/anvil/src/lib.rs b/crates/anvil/src/lib.rs index 370ba9f23..ee66caa67 100644 --- a/crates/anvil/src/lib.rs +++ b/crates/anvil/src/lib.rs @@ -255,32 +255,41 @@ pub async fn try_spawn(mut config: NodeConfig) -> io::Result<(EthApi, NodeHandle type IpcTask = JoinHandle<()>; -/// A handle to the spawned node and server tasks +/// A handle to the spawned node and server tasks. /// /// This future will resolve if either the node or server task resolve/fail. pub struct NodeHandle { config: NodeConfig, - /// The address of the running rpc server + /// The address of the running rpc server. addresses: Vec, - /// Join handle for the Node Service + /// Join handle for the Node Service. pub node_service: JoinHandle>, /// Join handles (one per socket) for the Anvil server. pub servers: Vec>>, - // The future that joins the ipc server, if any + /// The future that joins the ipc server, if any. ipc_task: Option, /// A signal that fires the shutdown, fired on drop. _signal: Option, - /// A task manager that can be used to spawn additional tasks + /// A task manager that can be used to spawn additional tasks. task_manager: TaskManager, } +impl Drop for NodeHandle { + fn drop(&mut self) { + // Fire shutdown signal to make sure anvil instance is terminated. + if let Some(signal) = self._signal.take() { + signal.fire().unwrap() + } + } +} + impl NodeHandle { - /// The [NodeConfig] the node was launched with + /// The [NodeConfig] the node was launched with. pub fn config(&self) -> &NodeConfig { &self.config } - /// Prints the launch info + /// Prints the launch info. pub(crate) fn print(&self, fork: Option<&ClientFork>) { self.config.print(fork); if !self.config.silent { @@ -298,25 +307,25 @@ impl NodeHandle { } } - /// The address of the launched server + /// The address of the launched server. /// /// **N.B.** this may not necessarily be the same `host + port` as configured in the - /// `NodeConfig`, if port was set to 0, then the OS auto picks an available port + /// `NodeConfig`, if port was set to 0, then the OS auto picks an available port. pub fn socket_address(&self) -> &SocketAddr { &self.addresses[0] } - /// Returns the http endpoint + /// Returns the http endpoint. pub fn http_endpoint(&self) -> String { format!("http://{}", self.socket_address()) } - /// Returns the websocket endpoint + /// Returns the websocket endpoint. pub fn ws_endpoint(&self) -> String { format!("ws://{}", self.socket_address()) } - /// Returns the path of the launched ipc server, if any + /// Returns the path of the launched ipc server, if any. pub fn ipc_path(&self) -> Option { self.config.get_ipc_path() } @@ -336,44 +345,44 @@ impl NodeHandle { ProviderBuilder::new(&self.config.get_ipc_path()?).build().ok() } - /// Signer accounts that can sign messages/transactions from the EVM node + /// Signer accounts that can sign messages/transactions from the EVM node. pub fn dev_accounts(&self) -> impl Iterator + '_ { self.config.signer_accounts.iter().map(|wallet| wallet.address()) } - /// Signer accounts that can sign messages/transactions from the EVM node + /// Signer accounts that can sign messages/transactions from the EVM node. pub fn dev_wallets(&self) -> impl Iterator + '_ { self.config.signer_accounts.iter().cloned() } - /// Accounts that will be initialised with `genesis_balance` in the genesis block + /// Accounts that will be initialised with `genesis_balance` in the genesis block. pub fn genesis_accounts(&self) -> impl Iterator + '_ { self.config.genesis_accounts.iter().map(|w| w.address()) } - /// Native token balance of every genesis account in the genesis block + /// Native token balance of every genesis account in the genesis block. pub fn genesis_balance(&self) -> U256 { self.config.genesis_balance } - /// Default gas price for all txs + /// Default gas price for all txs. pub fn gas_price(&self) -> u128 { self.config.get_gas_price() } - /// Returns the shutdown signal + /// Returns the shutdown signal. pub fn shutdown_signal(&self) -> &Option { &self._signal } - /// Returns mutable access to the shutdown signal + /// Returns mutable access to the shutdown signal. /// - /// This can be used to extract the Signal + /// This can be used to extract the Signal. pub fn shutdown_signal_mut(&mut self) -> &mut Option { &mut self._signal } - /// Returns the task manager that can be used to spawn new tasks + /// Returns the task manager that can be used to spawn new tasks. /// /// ``` /// use anvil::NodeHandle; diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index 803802313..0e8001853 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -667,7 +667,7 @@ async fn can_remove_pool_transactions() { #[tokio::test(flavor = "multi_thread")] async fn test_reorg() { - let (api, mut handle) = spawn(NodeConfig::test()).await; + let (api, handle) = spawn(NodeConfig::test()).await; let provider = handle.ws_provider(); let accounts = handle.dev_wallets().collect::>(); @@ -792,8 +792,4 @@ async fn test_reorg() { }) .await; assert!(res.is_err()); - - if let Some(signal) = handle.shutdown_signal_mut().take() { - signal.fire().unwrap(); - } } diff --git a/crates/anvil/tests/it/gas.rs b/crates/anvil/tests/it/gas.rs index 6561e2ada..e80a52462 100644 --- a/crates/anvil/tests/it/gas.rs +++ b/crates/anvil/tests/it/gas.rs @@ -190,7 +190,7 @@ async fn test_tip_above_fee_cap() { #[tokio::test(flavor = "multi_thread")] async fn test_can_use_fee_history() { let base_fee = 50u128; - let (_api, mut handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee))).await; + let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee))).await; let provider = handle.http_provider(); for _ in 0..10 { @@ -215,8 +215,4 @@ async fn test_can_use_fee_history() { assert_eq!(latest_block.header.base_fee_per_gas.unwrap(), *latest_fee_history_fee); assert_eq!(latest_fee_history_fee, next_base_fee); } - - if let Some(signal) = handle.shutdown_signal_mut().take() { - signal.fire().unwrap(); - } } diff --git a/crates/anvil/tests/it/proof.rs b/crates/anvil/tests/it/proof.rs index 33c51b2e0..757d36082 100644 --- a/crates/anvil/tests/it/proof.rs +++ b/crates/anvil/tests/it/proof.rs @@ -122,7 +122,7 @@ async fn test_storage_proof() { #[tokio::test(flavor = "multi_thread")] async fn can_get_random_account_proofs() { - let (api, mut handle) = spawn(NodeConfig::test()).await; + let (api, _handle) = spawn(NodeConfig::test()).await; for acc in std::iter::repeat_with(Address::random).take(10) { let _ = api @@ -130,8 +130,4 @@ async fn can_get_random_account_proofs() { .await .unwrap_or_else(|_| panic!("Failed to get proof for {acc:?}")); } - - if let Some(signal) = handle.shutdown_signal_mut().take() { - signal.fire().unwrap(); - } } diff --git a/crates/anvil/tests/it/traces.rs b/crates/anvil/tests/it/traces.rs index 9ab304541..aaa2ca298 100644 --- a/crates/anvil/tests/it/traces.rs +++ b/crates/anvil/tests/it/traces.rs @@ -722,7 +722,7 @@ async fn test_trace_address_fork2() { #[tokio::test(flavor = "multi_thread")] async fn test_trace_filter() { - let (api, mut handle) = spawn(NodeConfig::test()).await; + let (api, handle) = spawn(NodeConfig::test()).await; let provider = handle.ws_provider(); let accounts = handle.dev_wallets().collect::>(); @@ -859,8 +859,4 @@ async fn test_trace_filter() { let traces = api.trace_filter(tracer).await.unwrap(); assert_eq!(traces.len(), 5); - - if let Some(signal) = handle.shutdown_signal_mut().take() { - signal.fire().unwrap(); - } } diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 3d3feba67..42a2b5142 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -204,18 +204,6 @@ forgetest_init!(can_override_config, |prj, cmd| { Remapping::from(config.remappings[0].clone()).to_string() ); - // env vars work - std::env::remove_var("DAPP_REMAPPINGS"); - 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())); - assert_eq!( - format!( - "ds-test/={}/", - prj.root().join("lib/forge-std/lib/ds-test/from-env").to_slash_lossy() - ), - Remapping::from(config.remappings[0].clone()).to_string() - ); - let config = prj.config_from_output(["--remappings", "ds-test/=lib/forge-std/lib/ds-test/from-cli"]); assert_eq!( @@ -234,7 +222,6 @@ forgetest_init!(can_override_config, |prj, cmd| { Remapping::from(config.remappings[0].clone()).to_string() ); - std::env::remove_var("DAPP_REMAPPINGS"); pretty_err(&remappings_txt, fs::remove_file(&remappings_txt)); let expected = profile.into_basic().to_string_pretty().unwrap().trim().to_string(); @@ -506,7 +493,6 @@ 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!( @@ -518,7 +504,6 @@ forgetest_init!(can_detect_lib_foundry_toml, |prj, cmd| { ); // create a new lib directly in the `lib` folder with a remapping - std::env::remove_var("DAPP_REMAPPINGS"); let mut config = config; config.remappings = vec![Remapping::from_str("nested/=lib/nested").unwrap().into()]; let nested = prj.paths().libraries[0].join("nested-lib"); @@ -526,7 +511,6 @@ 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 config = cmd.config(); let remappings = config.remappings.iter().cloned().map(Remapping::from).collect::>(); similar_asserts::assert_eq!( @@ -549,7 +533,6 @@ 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::>(); @@ -569,7 +552,6 @@ 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!( From c59d97e8c1994684062f69305ce7cfacd52fceff Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:16:13 +0530 Subject: [PATCH 029/100] feat(`cheatcodes`): `getArtifactPathByCode` and `getArtifactPathByDeployedCode` (#8938) * feat(`cheatcodes`): vm.getArtifactPath * cargo cheats * nit * nit * fix * test: vm.getArtifactPath * feat: vm.getArtifactPath(creationCode) * cheats * nit * change seed * rm vm.getArtifactPath(contractName) * fmt * nit * fix * nit * rename * nit * fix --------- Co-authored-by: grandizzy --- crates/cheatcodes/assets/cheatcodes.json | 40 +++++++++++++++++++ crates/cheatcodes/spec/src/vm.rs | 8 ++++ crates/cheatcodes/src/fs.rs | 28 +++++++++++++ testdata/cheats/Vm.sol | 2 + testdata/default/cheats/GetArtifactPath.t.sol | 37 +++++++++++++++++ 5 files changed, 115 insertions(+) create mode 100644 testdata/default/cheats/GetArtifactPath.t.sol diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index d40d7353e..d3a0d49ed 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -5011,6 +5011,46 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "getArtifactPathByCode", + "description": "Gets the artifact path from code (aka. creation code).", + "declaration": "function getArtifactPathByCode(bytes calldata code) external view returns (string memory path);", + "visibility": "external", + "mutability": "view", + "signature": "getArtifactPathByCode(bytes)", + "selector": "0xeb74848c", + "selectorBytes": [ + 235, + 116, + 132, + 140 + ] + }, + "group": "filesystem", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "getArtifactPathByDeployedCode", + "description": "Gets the artifact path from deployed code (aka. runtime code).", + "declaration": "function getArtifactPathByDeployedCode(bytes calldata deployedCode) external view returns (string memory path);", + "visibility": "external", + "mutability": "view", + "signature": "getArtifactPathByDeployedCode(bytes)", + "selector": "0x6d853ba5", + "selectorBytes": [ + 109, + 133, + 59, + 165 + ] + }, + "group": "filesystem", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "getBlobBaseFee", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 5fbd94ce0..f518d9471 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1531,6 +1531,14 @@ interface Vm { #[cheatcode(group = Filesystem)] function writeLine(string calldata path, string calldata data) external; + /// Gets the artifact path from code (aka. creation code). + #[cheatcode(group = Filesystem)] + function getArtifactPathByCode(bytes calldata code) external view returns (string memory path); + + /// Gets the artifact path from deployed code (aka. runtime code). + #[cheatcode(group = Filesystem)] + function getArtifactPathByDeployedCode(bytes calldata deployedCode) external view returns (string memory path); + /// Gets the creation bytecode from an artifact file. Takes in the relative path to the json file or the path to the /// artifact in the form of :: where and parts are optional. #[cheatcode(group = Filesystem)] diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index da6909401..4185b2d79 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -250,6 +250,34 @@ impl Cheatcode for writeLineCall { } } +impl Cheatcode for getArtifactPathByCodeCall { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { code } = self; + let (artifact_id, _) = state + .config + .available_artifacts + .as_ref() + .and_then(|artifacts| artifacts.find_by_creation_code(code)) + .ok_or_else(|| fmt_err!("no matching artifact found"))?; + + Ok(artifact_id.path.to_string_lossy().abi_encode()) + } +} + +impl Cheatcode for getArtifactPathByDeployedCodeCall { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { deployedCode } = self; + let (artifact_id, _) = state + .config + .available_artifacts + .as_ref() + .and_then(|artifacts| artifacts.find_by_deployed_code(deployedCode)) + .ok_or_else(|| fmt_err!("no matching artifact found"))?; + + Ok(artifact_id.path.to_string_lossy().abi_encode()) + } +} + impl Cheatcode for getCodeCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { artifactPath: path } = self; diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 7316521ec..459b16c8c 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -246,6 +246,8 @@ interface Vm { function fee(uint256 newBasefee) external; function ffi(string[] calldata commandInput) external returns (bytes memory result); function fsMetadata(string calldata path) external view returns (FsMetadata memory metadata); + function getArtifactPathByCode(bytes calldata code) external view returns (string memory path); + function getArtifactPathByDeployedCode(bytes calldata deployedCode) external view returns (string memory path); function getBlobBaseFee() external view returns (uint256 blobBaseFee); function getBlobhashes() external view returns (bytes32[] memory hashes); function getBlockNumber() external view returns (uint256 height); diff --git a/testdata/default/cheats/GetArtifactPath.t.sol b/testdata/default/cheats/GetArtifactPath.t.sol new file mode 100644 index 000000000..d92d41e01 --- /dev/null +++ b/testdata/default/cheats/GetArtifactPath.t.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract DummyForGetArtifactPath {} + +contract GetArtifactPathTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function testGetArtifactPathByCode() public { + DummyForGetArtifactPath dummy = new DummyForGetArtifactPath(); + bytes memory dummyCreationCode = type(DummyForGetArtifactPath).creationCode; + + string memory root = vm.projectRoot(); + string memory path = vm.getArtifactPathByCode(dummyCreationCode); + + string memory expectedPath = + string.concat(root, "/out/default/GetArtifactPath.t.sol/DummyForGetArtifactPath.json"); + + assertEq(path, expectedPath); + } + + function testGetArtifactPathByDeployedCode() public { + DummyForGetArtifactPath dummy = new DummyForGetArtifactPath(); + bytes memory dummyRuntimeCode = address(dummy).code; + + string memory root = vm.projectRoot(); + string memory path = vm.getArtifactPathByDeployedCode(dummyRuntimeCode); + + string memory expectedPath = + string.concat(root, "/out/default/GetArtifactPath.t.sol/DummyForGetArtifactPath.json"); + + assertEq(path, expectedPath); + } +} From a0ff7bd3334073819d41934451f67f3323016480 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 26 Sep 2024 13:49:57 +0300 Subject: [PATCH 030/100] chore: fix base gas limit test and clippy (#8961) --- crates/anvil/src/eth/backend/mem/storage.rs | 2 ++ crates/anvil/tests/it/fork.rs | 4 ++-- crates/anvil/tests/it/main.rs | 2 ++ crates/cast/bin/main.rs | 1 + crates/cast/src/rlp_converter.rs | 2 +- crates/cheatcodes/src/test/expect.rs | 2 +- crates/chisel/bin/main.rs | 1 + crates/common/src/selectors.rs | 2 ++ crates/evm/core/src/backend/mod.rs | 2 ++ crates/evm/core/src/fork/database.rs | 2 ++ crates/evm/traces/src/identifier/signatures.rs | 2 ++ crates/forge/bin/cmd/clone.rs | 2 ++ crates/forge/bin/cmd/doc/server.rs | 1 + crates/forge/tests/it/main.rs | 2 ++ crates/script/src/sequence.rs | 3 +-- crates/test-utils/src/rpc.rs | 2 ++ crates/verify/src/etherscan/mod.rs | 2 ++ crates/wallets/src/wallet.rs | 2 ++ 18 files changed, 30 insertions(+), 6 deletions(-) diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 9afc9f669..4cb588221 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -575,6 +575,8 @@ pub struct MinedTransactionReceipt { #[cfg(test)] mod tests { + #![allow(clippy::needless_return)] + use super::*; use crate::eth::backend::db::Db; use alloy_primitives::{hex, Address}; diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 8d5b0c588..3f96513c5 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -1289,8 +1289,8 @@ async fn test_base_fork_gas_limit() { .unwrap() .unwrap(); - assert_eq!(api.gas_limit(), uint!(120_000_000_U256)); - assert_eq!(block.header.gas_limit, 120_000_000_u128); + assert!(api.gas_limit() >= uint!(132_000_000_U256)); + assert!(block.header.gas_limit >= 132_000_000_u128); } // diff --git a/crates/anvil/tests/it/main.rs b/crates/anvil/tests/it/main.rs index f3f5eca15..256edb813 100644 --- a/crates/anvil/tests/it/main.rs +++ b/crates/anvil/tests/it/main.rs @@ -1,3 +1,5 @@ +#![allow(clippy::needless_return)] + mod abi; mod anvil; mod anvil_api; diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 27a052fe6..0b861f90d 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -42,6 +42,7 @@ fn main() -> Result<()> { main_args(args) } +#[allow(clippy::needless_return)] #[tokio::main] async fn main_args(args: CastArgs) -> Result<()> { match args.cmd { diff --git a/crates/cast/src/rlp_converter.rs b/crates/cast/src/rlp_converter.rs index ad1787438..05e3462cb 100644 --- a/crates/cast/src/rlp_converter.rs +++ b/crates/cast/src/rlp_converter.rs @@ -47,7 +47,7 @@ impl Decodable for Item { impl Item { pub(crate) fn value_to_item(value: &Value) -> eyre::Result { - return match value { + match value { Value::Null => Ok(Self::Data(vec![])), Value::Bool(_) => { eyre::bail!("RLP input can not contain booleans") diff --git a/crates/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs index 066c900b3..f38776f94 100644 --- a/crates/cheatcodes/src/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -597,7 +597,7 @@ pub(crate) fn handle_expect_emit( 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. - if event_to_fill_or_check.anonymous || log.topics().first().is_some() { + if event_to_fill_or_check.anonymous || !log.topics().is_empty() { event_to_fill_or_check.log = Some(log.data.clone()); // 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); diff --git a/crates/chisel/bin/main.rs b/crates/chisel/bin/main.rs index 7a0703e85..ba4d7e9bc 100644 --- a/crates/chisel/bin/main.rs +++ b/crates/chisel/bin/main.rs @@ -102,6 +102,7 @@ fn main() -> eyre::Result<()> { main_args(args) } +#[allow(clippy::needless_return)] #[tokio::main] async fn main_args(args: Chisel) -> eyre::Result<()> { // Keeps track of whether or not an interrupt was the last input diff --git a/crates/common/src/selectors.rs b/crates/common/src/selectors.rs index c22ee3076..16565a07e 100644 --- a/crates/common/src/selectors.rs +++ b/crates/common/src/selectors.rs @@ -581,6 +581,8 @@ pub fn parse_signatures(tokens: Vec) -> ParsedSignatures { #[cfg(test)] mod tests { + #![allow(clippy::needless_return)] + use super::*; #[tokio::test(flavor = "multi_thread")] diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 4d1d7d9ef..6fbd2086d 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -1979,6 +1979,8 @@ fn apply_state_changeset( #[cfg(test)] mod tests { + #![allow(clippy::needless_return)] + use crate::{backend::Backend, fork::CreateFork, opts::EvmOpts}; use alloy_primitives::{Address, U256}; use alloy_provider::Provider; diff --git a/crates/evm/core/src/fork/database.rs b/crates/evm/core/src/fork/database.rs index de6b2a6b9..873e8a896 100644 --- a/crates/evm/core/src/fork/database.rs +++ b/crates/evm/core/src/fork/database.rs @@ -263,6 +263,8 @@ impl DatabaseRef for ForkDbSnapshot { #[cfg(test)] mod tests { + #![allow(clippy::needless_return)] + use super::*; use crate::backend::BlockchainDbMeta; use foundry_common::provider::get_http_provider; diff --git a/crates/evm/traces/src/identifier/signatures.rs b/crates/evm/traces/src/identifier/signatures.rs index 2a2f6a2f2..498385ef1 100644 --- a/crates/evm/traces/src/identifier/signatures.rs +++ b/crates/evm/traces/src/identifier/signatures.rs @@ -162,6 +162,8 @@ impl Drop for SignaturesIdentifier { #[cfg(test)] mod tests { + #![allow(clippy::needless_return)] + use super::*; #[tokio::test(flavor = "multi_thread")] diff --git a/crates/forge/bin/cmd/clone.rs b/crates/forge/bin/cmd/clone.rs index 98ef95d40..f3879b931 100644 --- a/crates/forge/bin/cmd/clone.rs +++ b/crates/forge/bin/cmd/clone.rs @@ -609,6 +609,8 @@ impl EtherscanClient for Client { #[cfg(test)] mod tests { + #![allow(clippy::needless_return)] + use super::*; use alloy_primitives::hex; use foundry_compilers::Artifact; diff --git a/crates/forge/bin/cmd/doc/server.rs b/crates/forge/bin/cmd/doc/server.rs index f5991ba43..72585a2bf 100644 --- a/crates/forge/bin/cmd/doc/server.rs +++ b/crates/forge/bin/cmd/doc/server.rs @@ -90,6 +90,7 @@ impl Server { } } +#[allow(clippy::needless_return)] #[tokio::main] async fn serve(build_dir: PathBuf, address: SocketAddr, file_404: &str) -> io::Result<()> { let file_404 = build_dir.join(file_404); diff --git a/crates/forge/tests/it/main.rs b/crates/forge/tests/it/main.rs index aaa129796..ba1cff382 100644 --- a/crates/forge/tests/it/main.rs +++ b/crates/forge/tests/it/main.rs @@ -1,3 +1,5 @@ +#![allow(clippy::needless_return)] + pub mod config; pub mod test_helpers; diff --git a/crates/script/src/sequence.rs b/crates/script/src/sequence.rs index 53212f4eb..8e2101155 100644 --- a/crates/script/src/sequence.rs +++ b/crates/script/src/sequence.rs @@ -374,8 +374,7 @@ impl ScriptSequence { /// Converts the `sig` argument into the corresponding file path. /// -/// This accepts either the signature of the function or the raw calldata - +/// This accepts either the signature of the function or the raw calldata. pub fn sig_to_file_name(sig: &str) -> String { if let Some((name, _)) = sig.split_once('(') { // strip until call argument parenthesis diff --git a/crates/test-utils/src/rpc.rs b/crates/test-utils/src/rpc.rs index 6f91be10f..51ec2c8e0 100644 --- a/crates/test-utils/src/rpc.rs +++ b/crates/test-utils/src/rpc.rs @@ -178,6 +178,8 @@ fn next_url(is_ws: bool, chain: NamedChain) -> String { #[cfg(test)] mod tests { + #![allow(clippy::needless_return)] + use super::*; use alloy_primitives::address; use foundry_config::Chain; diff --git a/crates/verify/src/etherscan/mod.rs b/crates/verify/src/etherscan/mod.rs index 0a1d39c19..3839b845b 100644 --- a/crates/verify/src/etherscan/mod.rs +++ b/crates/verify/src/etherscan/mod.rs @@ -452,6 +452,8 @@ async fn ensure_solc_build_metadata(version: Version) -> Result { #[cfg(test)] mod tests { + #![allow(clippy::needless_return)] + use super::*; use clap::Parser; use foundry_common::fs; diff --git a/crates/wallets/src/wallet.rs b/crates/wallets/src/wallet.rs index 14a9f7422..37b731ddb 100644 --- a/crates/wallets/src/wallet.rs +++ b/crates/wallets/src/wallet.rs @@ -149,6 +149,8 @@ impl From for WalletOpts { #[cfg(test)] mod tests { + #![allow(clippy::needless_return)] + use alloy_signer::Signer; use std::{path::Path, str::FromStr}; From d15d71ac0182e41091631225fcbb517926eda3fa Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 26 Sep 2024 17:36:29 +0300 Subject: [PATCH 031/100] feat(cheatcodes): random* cheatcodes to aid in symbolic testing (#8882) * feat(cheatcodes): additional random cheatcodes to aid in symbolic testing * Use arbitraryUint/address in tests * Test changes after review * Fix test * Add deprecated replacements * Changes after review: - add fn rng back - make sure cheats for uint/int/bytes doesn't panic + added tests * Update crates/cheatcodes/spec/src/vm.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * Cargo cheats * Fix test * Rename Arbitrary -> Random * Review changes: simplify randomBytes and bool --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/cheatcodes/Cargo.toml | 2 - crates/cheatcodes/assets/cheatcodes.json | 102 ++++++++++++++- crates/cheatcodes/spec/src/vm.rs | 22 +++- crates/cheatcodes/src/inspector.rs | 23 ++-- crates/cheatcodes/src/utils.rs | 117 ++++++++++++++---- testdata/cheats/Vm.sol | 5 + .../default/cheats/ArbitraryStorage.t.sol | 12 +- testdata/default/cheats/CopyStorage.t.sol | 4 +- .../default/cheats/RandomCheatcodes.t.sol | 104 ++++++++++++++++ 9 files changed, 351 insertions(+), 40 deletions(-) create mode 100644 testdata/default/cheats/RandomCheatcodes.t.sol diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index 412c5b898..cbf66d684 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -65,6 +65,4 @@ thiserror.workspace = true toml = { workspace = true, features = ["preserve_order"] } tracing.workspace = true walkdir.workspace = true - -[dev-dependencies] proptest.workspace = true diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index d3a0d49ed..451ce6cb4 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -6953,6 +6953,86 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "randomBool", + "description": "Returns an random `bool`.", + "declaration": "function randomBool() external view returns (bool);", + "visibility": "external", + "mutability": "view", + "signature": "randomBool()", + "selector": "0xcdc126bd", + "selectorBytes": [ + 205, + 193, + 38, + 189 + ] + }, + "group": "utilities", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "randomBytes", + "description": "Returns an random byte array value of the given length.", + "declaration": "function randomBytes(uint256 len) external view returns (bytes memory);", + "visibility": "external", + "mutability": "view", + "signature": "randomBytes(uint256)", + "selector": "0x6c5d32a9", + "selectorBytes": [ + 108, + 93, + 50, + 169 + ] + }, + "group": "utilities", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "randomInt_0", + "description": "Returns an random `int256` value.", + "declaration": "function randomInt() external view returns (int256);", + "visibility": "external", + "mutability": "view", + "signature": "randomInt()", + "selector": "0x111f1202", + "selectorBytes": [ + 17, + 31, + 18, + 2 + ] + }, + "group": "utilities", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "randomInt_1", + "description": "Returns an random `int256` value of given bits.", + "declaration": "function randomInt(uint256 bits) external view returns (int256);", + "visibility": "external", + "mutability": "view", + "signature": "randomInt(uint256)", + "selector": "0x12845966", + "selectorBytes": [ + 18, + 132, + 89, + 102 + ] + }, + "group": "utilities", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "randomUint_0", @@ -6976,7 +7056,7 @@ { "func": { "id": "randomUint_1", - "description": "Returns random uin256 value between the provided range (=min..=max).", + "description": "Returns random uint256 value between the provided range (=min..=max).", "declaration": "function randomUint(uint256 min, uint256 max) external returns (uint256);", "visibility": "external", "mutability": "", @@ -6993,6 +7073,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "randomUint_2", + "description": "Returns an random `uint256` value of given bits.", + "declaration": "function randomUint(uint256 bits) external view returns (uint256);", + "visibility": "external", + "mutability": "view", + "signature": "randomUint(uint256)", + "selector": "0xcf81e69c", + "selectorBytes": [ + 207, + 129, + 230, + 156 + ] + }, + "group": "utilities", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "readCallers", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index f518d9471..a53d4f908 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -2337,14 +2337,34 @@ interface Vm { #[cheatcode(group = Utilities)] function randomUint() external returns (uint256); - /// Returns random uin256 value between the provided range (=min..=max). + /// Returns random uint256 value between the provided range (=min..=max). #[cheatcode(group = Utilities)] function randomUint(uint256 min, uint256 max) external returns (uint256); + /// Returns an random `uint256` value of given bits. + #[cheatcode(group = Utilities)] + function randomUint(uint256 bits) external view returns (uint256); + /// Returns a random `address`. #[cheatcode(group = Utilities)] function randomAddress() external returns (address); + /// Returns an random `int256` value. + #[cheatcode(group = Utilities)] + function randomInt() external view returns (int256); + + /// Returns an random `int256` value of given bits. + #[cheatcode(group = Utilities)] + function randomInt(uint256 bits) external view returns (int256); + + /// Returns an random `bool`. + #[cheatcode(group = Utilities)] + function randomBool() external view returns (bool); + + /// Returns an random byte array value of the given length. + #[cheatcode(group = Utilities)] + function randomBytes(uint256 len) external view returns (bytes memory); + /// 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)] diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 5f4ee9408..58d8ca5c9 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -34,7 +34,8 @@ use foundry_evm_core::{ }; use foundry_evm_traces::TracingInspector; use itertools::Itertools; -use rand::{rngs::StdRng, Rng, SeedableRng}; +use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner}; +use rand::Rng; use revm::{ interpreter::{ opcode as op, CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, @@ -439,8 +440,9 @@ pub struct Cheatcodes { /// `char -> (address, pc)` pub breakpoints: Breakpoints, - /// Optional RNG algorithm. - rng: Option, + /// Optional cheatcodes `TestRunner`. Used for generating random values from uint and int + /// strategies. + test_runner: Option, /// Ignored traces. pub ignored_traces: IgnoredTraces, @@ -491,7 +493,7 @@ impl Cheatcodes { mapping_slots: Default::default(), pc: Default::default(), breakpoints: Default::default(), - rng: Default::default(), + test_runner: Default::default(), ignored_traces: Default::default(), arbitrary_storage: Default::default(), deprecated: Default::default(), @@ -1068,9 +1070,16 @@ impl Cheatcodes { } 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(), + self.test_runner().rng() + } + + pub fn test_runner(&mut self) -> &mut TestRunner { + self.test_runner.get_or_insert_with(|| match self.config.seed { + Some(seed) => TestRunner::new_with_rng( + proptest::test_runner::Config::default(), + TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()), + ), + None => TestRunner::new(proptest::test_runner::Config::default()), }) } diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index a96a44832..b4e327483 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -1,11 +1,13 @@ //! Implementations of [`Utilities`](spec::Group::Utilities) cheatcodes. use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Result, Vm::*}; -use alloy_primitives::{Address, U256}; +use alloy_dyn_abi::{DynSolType, DynSolValue}; +use alloy_primitives::U256; use alloy_sol_types::SolValue; use foundry_common::ens::namehash; use foundry_evm_core::{backend::DatabaseExt, constants::DEFAULT_CREATE2_DEPLOYER}; -use rand::Rng; +use proptest::strategy::{Strategy, ValueTree}; +use rand::{Rng, RngCore}; use std::collections::HashMap; /// Contains locations of traces ignored via cheatcodes. @@ -71,36 +73,64 @@ impl Cheatcode for ensNamehashCall { impl Cheatcode for randomUint_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { - let Self {} = self; - let rng = state.rng(); - let random_number: U256 = rng.gen(); - Ok(random_number.abi_encode()) + random_uint(state, None, None) } } impl Cheatcode for randomUint_1Call { 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 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); - random_number %= inclusive_modulo; - } - random_number += min; - Ok(random_number.abi_encode()) + random_uint(state, None, Some((min, max))) + } +} + +impl Cheatcode for randomUint_2Call { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { bits } = *self; + random_uint(state, Some(bits), None) } } impl Cheatcode for randomAddressCall { fn apply(&self, state: &mut Cheatcodes) -> Result { - let Self {} = self; - let rng = state.rng(); - let addr = Address::random_with(rng); - Ok(addr.abi_encode()) + Ok(DynSolValue::type_strategy(&DynSolType::Address) + .new_tree(state.test_runner()) + .unwrap() + .current() + .abi_encode()) + } +} + +impl Cheatcode for randomInt_0Call { + fn apply(&self, state: &mut Cheatcodes) -> Result { + random_int(state, None) + } +} + +impl Cheatcode for randomInt_1Call { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { bits } = *self; + random_int(state, Some(bits)) + } +} + +impl Cheatcode for randomBoolCall { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let rand_bool: bool = state.rng().gen(); + Ok(rand_bool.abi_encode()) + } +} + +impl Cheatcode for randomBytesCall { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { len } = *self; + ensure!( + len <= U256::from(usize::MAX), + format!("bytes length cannot exceed {}", usize::MAX) + ); + let mut bytes = vec![0u8; len.to::()]; + state.rng().fill_bytes(&mut bytes); + Ok(bytes.abi_encode()) } } @@ -181,3 +211,48 @@ impl Cheatcode for copyStorageCall { Ok(Default::default()) } } + +/// Helper to generate a random `uint` value (with given bits or bounded if specified) +/// from type strategy. +fn random_uint(state: &mut Cheatcodes, bits: Option, bounds: Option<(U256, U256)>) -> Result { + if let Some(bits) = bits { + // Generate random with specified bits. + ensure!(bits <= U256::from(256), "number of bits cannot exceed 256"); + return Ok(DynSolValue::type_strategy(&DynSolType::Uint(bits.to::())) + .new_tree(state.test_runner()) + .unwrap() + .current() + .abi_encode()) + } + + if let Some((min, max)) = bounds { + ensure!(min <= max, "min must be less than or equal to max"); + // Generate random between range min..=max + let exclusive_modulo = max - min; + let mut random_number: U256 = state.rng().gen(); + if exclusive_modulo != U256::MAX { + let inclusive_modulo = exclusive_modulo + U256::from(1); + random_number %= inclusive_modulo; + } + random_number += min; + return Ok(random_number.abi_encode()) + } + + // Generate random `uint256` value. + Ok(DynSolValue::type_strategy(&DynSolType::Uint(256)) + .new_tree(state.test_runner()) + .unwrap() + .current() + .abi_encode()) +} + +/// Helper to generate a random `int` value (with given bits if specified) from type strategy. +fn random_int(state: &mut Cheatcodes, bits: Option) -> Result { + let no_bits = bits.unwrap_or(U256::from(256)); + ensure!(no_bits <= U256::from(256), "number of bits cannot exceed 256"); + Ok(DynSolValue::type_strategy(&DynSolType::Int(no_bits.to::())) + .new_tree(state.test_runner()) + .unwrap() + .current() + .abi_encode()) +} diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 459b16c8c..2b9347382 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -343,8 +343,13 @@ interface Vm { 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 randomBool() external view returns (bool); + function randomBytes(uint256 len) external view returns (bytes memory); + function randomInt() external view returns (int256); + function randomInt(uint256 bits) external view returns (int256); function randomUint() external returns (uint256); function randomUint(uint256 min, uint256 max) external returns (uint256); + function randomUint(uint256 bits) external view returns (uint256); function readCallers() external returns (CallerMode callerMode, address msgSender, address txOrigin); function readDir(string calldata path) external view returns (DirEntry[] memory entries); function readDir(string calldata path, uint64 maxDepth) external view returns (DirEntry[] memory entries); diff --git a/testdata/default/cheats/ArbitraryStorage.t.sol b/testdata/default/cheats/ArbitraryStorage.t.sol index 86910279e..f763ab7af 100644 --- a/testdata/default/cheats/ArbitraryStorage.t.sol +++ b/testdata/default/cheats/ArbitraryStorage.t.sol @@ -83,10 +83,10 @@ contract AContractArbitraryStorageWithSeedTest is DSTest { 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); + assertEq(target.a(11), 112807530564575719000382171275495171195982096112439764207649185248041477080234); + assertEq(target.b(22), 0x9dce87df97C81f2529877E8127b4b8c13E4b2b31); + assertEq(target.c(33), 85); + assertEq(target.d(44), 0x6ceda712fc9d694d72afeea6c44d370b789a18e1a3d640068c11069e421d25f6); } } @@ -104,7 +104,7 @@ contract SymbolicStorageWithSeedTest is DSTest { address addr = 0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8; vm.setArbitraryStorage(addr); bytes32 value = vm.load(addr, bytes32(slot)); - assertEq(uint256(value), 85286582241781868037363115933978803127245343755841464083427462398552335014708); + assertEq(uint256(value), 112807530564575719000382171275495171195982096112439764207649185248041477080234); // Load slot again and make sure we get same value. bytes32 value1 = vm.load(addr, bytes32(slot)); assertEq(uint256(value), uint256(value1)); @@ -115,7 +115,7 @@ contract SymbolicStorageWithSeedTest is DSTest { SymbolicStore myStore = new SymbolicStore(); vm.setArbitraryStorage(address(myStore)); bytes32 value = vm.load(address(myStore), bytes32(uint256(slot))); - assertEq(uint256(value), 85286582241781868037363115933978803127245343755841464083427462398552335014708); + assertEq(uint256(value), 112807530564575719000382171275495171195982096112439764207649185248041477080234); } function testEmptyInitialStorage(uint256 slot) public { diff --git a/testdata/default/cheats/CopyStorage.t.sol b/testdata/default/cheats/CopyStorage.t.sol index 895847497..3ca5d8dab 100644 --- a/testdata/default/cheats/CopyStorage.t.sol +++ b/testdata/default/cheats/CopyStorage.t.sol @@ -57,8 +57,8 @@ contract CounterWithSeedTest is DSTest { counter.setA(1000); counter1.setB(address(50)); assertEq(counter.a(), 1000); - assertEq(counter1.a(), 40426841063417815470953489044557166618267862781491517122018165313568904172524); - assertEq(counter.b(), 0x485E9Cc0ef187E54A3AB45b50c3DcE43f2C223B1); + assertEq(counter1.a(), 67350900536747027229585709178274816969402970928486983076982664581925078789474); + assertEq(counter.b(), 0x5A61ACa23C478d83A72425c386Eb5dB083FBd0e4); assertEq(counter1.b(), address(50)); } } diff --git a/testdata/default/cheats/RandomCheatcodes.t.sol b/testdata/default/cheats/RandomCheatcodes.t.sol new file mode 100644 index 000000000..4f7d559fa --- /dev/null +++ b/testdata/default/cheats/RandomCheatcodes.t.sol @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract RandomCheatcodesTest is DSTest { + Vm vm = Vm(HEVM_ADDRESS); + + int128 constant min = -170141183460469231731687303715884105728; + int128 constant max = 170141183460469231731687303715884105727; + + function test_int128() public { + vm.expectRevert("vm.randomInt: number of bits cannot exceed 256"); + int256 val = vm.randomInt(type(uint256).max); + + val = vm.randomInt(128); + assertGe(val, min); + assertLe(val, max); + } + + function testFail_int128() public { + int256 val = vm.randomInt(128); + assertGt(val, max); + } + + function test_address() public { + address fresh_address = vm.randomAddress(); + assert(fresh_address != address(this)); + assert(fresh_address != address(vm)); + } + + function test_randomUintLimit() public { + vm.expectRevert("vm.randomUint: number of bits cannot exceed 256"); + uint256 val = vm.randomUint(type(uint256).max); + } + + function test_randomUints(uint256 x) public { + x = vm.randomUint(0, 256); + uint256 freshUint = vm.randomUint(x); + + assert(0 <= freshUint); + if (x == 256) { + assert(freshUint <= type(uint256).max); + } else { + assert(freshUint <= 2 ** x - 1); + } + } + + function test_randomSymbolicWord() public { + uint256 freshUint192 = vm.randomUint(192); + + assert(0 <= freshUint192); + assert(freshUint192 <= type(uint192).max); + } +} + +contract RandomBytesTest is DSTest { + Vm vm = Vm(HEVM_ADDRESS); + + bytes1 local_byte; + bytes local_bytes; + + function manip_symbolic_bytes(bytes memory b) public { + uint256 middle = b.length / 2; + b[middle] = hex"aa"; + } + + function test_symbolic_bytes_revert() public { + vm.expectRevert(); + bytes memory val = vm.randomBytes(type(uint256).max); + } + + function test_symbolic_bytes_1() public { + uint256 length = uint256(vm.randomUint(1, type(uint8).max)); + bytes memory fresh_bytes = vm.randomBytes(length); + uint256 index = uint256(vm.randomUint(1)); + + local_byte = fresh_bytes[index]; + assertEq(fresh_bytes[index], local_byte); + } + + function test_symbolic_bytes_2() public { + uint256 length = uint256(vm.randomUint(1, type(uint8).max)); + bytes memory fresh_bytes = vm.randomBytes(length); + + local_bytes = fresh_bytes; + assertEq(fresh_bytes, local_bytes); + } + + function test_symbolic_bytes_3() public { + uint256 length = uint256(vm.randomUint(1, type(uint8).max)); + bytes memory fresh_bytes = vm.randomBytes(length); + + manip_symbolic_bytes(fresh_bytes); + assertEq(hex"aa", fresh_bytes[length / 2]); + } + + function test_symbolic_bytes_length(uint8 l) public { + vm.assume(0 < l); + bytes memory fresh_bytes = vm.randomBytes(l); + assertEq(fresh_bytes.length, l); + } +} From f7e920488846629ba4977063d43b37a544d653a1 Mon Sep 17 00:00:00 2001 From: Drake Evans <31104161+DrakeEvans@users.noreply.github.com> Date: Thu, 26 Sep 2024 10:55:02 -0400 Subject: [PATCH 032/100] =?UTF-8?q?feat:=20use=20multi-architecture=20imag?= =?UTF-8?q?es=20in=20Dockerfile=20to=20support=20apple=20si=E2=80=A6=20(#8?= =?UTF-8?q?964)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat: use multi-architecture images in Dockerfile to support apple silicon --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index cbdf08b25..87e52b708 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,9 +30,9 @@ RUN --mount=type=cache,target=/root/.cargo/registry --mount=type=cache,target=/r && strip out/chisel \ && strip out/anvil; -FROM docker.io/frolvlad/alpine-glibc:alpine-3.16_glibc-2.34 as foundry-client +FROM alpine:3.18 as foundry-client -RUN apk add --no-cache linux-headers git +RUN apk add --no-cache linux-headers git gcompat libstdc++ COPY --from=build-environment /opt/foundry/out/forge /usr/local/bin/forge COPY --from=build-environment /opt/foundry/out/cast /usr/local/bin/cast From e485eebec933d5e615fe968264e58ca4adfd951d Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 26 Sep 2024 20:55:36 +0300 Subject: [PATCH 033/100] fix: enable `revm/blst` (#8965) * fix: enable revm/blst * fix * fix --- Cargo.lock | 1 + crates/evm/core/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 84194dfaf..5a9f4a3b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7194,6 +7194,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f73010c271d53fa7904e9845338e95f3955eb1200a0355e0abfdb89c41aaa9cd" dependencies = [ "aurora-engine-modexp", + "blst", "c-kzg", "cfg-if", "k256", diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index 6fbf67453..ce06dc9d0 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -45,6 +45,7 @@ revm = { workspace = true, features = [ "arbitrary", "optimism", "c-kzg", + "blst", ] } revm-inspectors.workspace = true From 9dbfb2f1115466b28f2697e158131f90df6b2590 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 26 Sep 2024 20:14:45 +0200 Subject: [PATCH 034/100] test: redact forge version (#8967) --- crates/forge/tests/cli/cmd.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 1144830c5..8179e9eaa 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -276,7 +276,7 @@ forgetest!(can_init_no_git, |prj, cmd| { 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 + Installed forge-std [..] Initialized forge project "#]]); From 98bcd8e115bb542e28cbc1b9df7263c472ea2f04 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 09:49:44 +0000 Subject: [PATCH 035/100] chore(tests): bump forge-std version (#8970) 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 239d2091e..c6c6e2da6 100644 --- a/testdata/forge-std-rev +++ b/testdata/forge-std-rev @@ -1 +1 @@ -1714bee72e286e73f76e320d110e0eaf5c4e649d \ No newline at end of file +8f24d6b04c92975e0795b5868aa0d783251cdeaa \ No newline at end of file From 658bb88a7228189f1724048c7078929dcb934938 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 27 Sep 2024 13:18:20 +0200 Subject: [PATCH 036/100] chore(traces): remove unreachable decoding of expectRevert (#8969) * chore(traces): remove unreachable decoding of expectRevert * chore: clippy --- crates/cheatcodes/spec/src/vm.rs | 15 +++++++++ crates/evm/core/src/decode.rs | 52 +++----------------------------- 2 files changed, 20 insertions(+), 47 deletions(-) diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index a53d4f908..22500ec98 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -6,6 +6,7 @@ use super::*; use crate::Vm::ForgeContext; use alloy_sol_types::sol; use foundry_macros::Cheatcode; +use std::fmt; sol! { // Cheatcodes are marked as view/pure/none using the following rules: @@ -2407,6 +2408,20 @@ impl PartialEq for ForgeContext { } } +impl fmt::Display for Vm::CheatcodeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.message.fmt(f) + } +} + +impl fmt::Display for Vm::VmErrors { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::CheatcodeError(err) => err.fmt(f), + } + } +} + #[track_caller] const fn panic_unknown_safety() -> ! { panic!("cannot determine safety from the group, add a `#[cheatcode(safety = ...)]` attribute") diff --git a/crates/evm/core/src/decode.rs b/crates/evm/core/src/decode.rs index 5f3e86281..95fcaf02a 100644 --- a/crates/evm/core/src/decode.rs +++ b/crates/evm/core/src/decode.rs @@ -4,7 +4,7 @@ use crate::abi::{Console, Vm}; use alloy_dyn_abi::JsonAbiExt; use alloy_json_abi::{Error, JsonAbi}; use alloy_primitives::{hex, Log, Selector}; -use alloy_sol_types::{SolCall, SolError, SolEventInterface, SolInterface, SolValue}; +use alloy_sol_types::{SolEventInterface, SolInterface, SolValue}; use foundry_common::SELECTOR_LEN; use itertools::Itertools; use revm::interpreter::InstructionResult; @@ -139,7 +139,7 @@ impl RevertDecoder { /// /// See [`decode`](Self::decode) for more information. pub fn maybe_decode(&self, err: &[u8], status: Option) -> Option { - if err.len() < SELECTOR_LEN { + let Some((selector, data)) = err.split_first_chunk::() else { if let Some(status) = status { if !status.is_ok() { return Some(format!("EvmError: {status:?}")); @@ -150,59 +150,17 @@ impl RevertDecoder { } else { Some(format!("custom error bytes {}", hex::encode_prefixed(err))) }; - } + }; if let Some(reason) = SkipReason::decode(err) { return Some(reason.to_string()); } - // Solidity's `Error(string)` or `Panic(uint256)` - if let Ok(e) = alloy_sol_types::GenericContractError::abi_decode(err, false) { + // Solidity's `Error(string)` or `Panic(uint256)`, or `Vm`'s custom errors. + if let Ok(e) = alloy_sol_types::ContractError::::abi_decode(err, false) { return Some(e.to_string()); } - let (selector, data) = err.split_at(SELECTOR_LEN); - let selector: &[u8; 4] = selector.try_into().unwrap(); - - match *selector { - // `CheatcodeError(string)` - Vm::CheatcodeError::SELECTOR => { - let e = Vm::CheatcodeError::abi_decode_raw(data, false).ok()?; - return Some(e.message); - } - // `expectRevert(bytes)` - Vm::expectRevert_2Call::SELECTOR => { - 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); - } - _ => {} - } - // Custom errors. if let Some(errors) = self.errors.get(selector) { for error in errors { From 20cb9038e203c2f11162e9e3b91db22f25a71c76 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 27 Sep 2024 14:23:26 +0300 Subject: [PATCH 037/100] fix(`invariant`): replay should not fail for magic assume (#8966) * fix(invariant): shrink should not fail for magic assume * Test & Code Comment --- .../evm/evm/src/executors/invariant/shrink.rs | 6 +- crates/forge/tests/it/invariant.rs | 71 ++++++++++++++++++- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/crates/evm/evm/src/executors/invariant/shrink.rs b/crates/evm/evm/src/executors/invariant/shrink.rs index ca5196b4a..c468c58ee 100644 --- a/crates/evm/evm/src/executors/invariant/shrink.rs +++ b/crates/evm/evm/src/executors/invariant/shrink.rs @@ -5,6 +5,7 @@ use crate::executors::{ Executor, }; use alloy_primitives::{Address, Bytes, U256}; +use foundry_evm_core::constants::MAGIC_ASSUME; use foundry_evm_fuzz::invariant::BasicTxDetails; use indicatif::ProgressBar; use proptest::bits::{BitSetLike, VarBitSet}; @@ -160,7 +161,10 @@ pub fn check_sequence( tx.call_details.calldata.clone(), U256::ZERO, )?; - if call_result.reverted && fail_on_revert { + // Ignore calls reverted with `MAGIC_ASSUME`. This is needed to handle failed scenarios that + // are replayed with a modified version of test driver (that use new `vm.assume` + // cheatcodes). + if call_result.reverted && fail_on_revert && call_result.result.as_ref() != MAGIC_ASSUME { // Candidate sequence fails test. // We don't have to apply remaining calls to check sequence. return Ok((false, false)); diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index 5a571f772..a6fa61512 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -3,7 +3,8 @@ use crate::{config::*, test_helpers::TEST_DATA_DEFAULT}; use alloy_primitives::U256; use forge::fuzz::CounterExample; -use foundry_test_utils::Filter; +use foundry_config::{Config, InvariantConfig}; +use foundry_test_utils::{forgetest_init, str, Filter}; use std::collections::BTreeMap; macro_rules! get_counterexample { @@ -700,3 +701,71 @@ async fn test_no_reverts_in_counterexample() { } }; } + +// Tests that a persisted failure doesn't fail due to assume revert if test driver is changed. +forgetest_init!(should_not_fail_replay_assume, |prj, cmd| { + let config = Config { + invariant: { + InvariantConfig { fail_on_revert: true, max_assume_rejects: 10, ..Default::default() } + }, + ..Default::default() + }; + prj.write_config(config); + + // Add initial test that breaks invariant. + prj.add_test( + "AssumeTest.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract AssumeHandler is Test { + function fuzzMe(uint256 a) public { + require(false, "Invariant failure"); + } +} + +contract AssumeTest is Test { + function setUp() public { + AssumeHandler handler = new AssumeHandler(); + } + function invariant_assume() public {} +} + "#, + ) + .unwrap(); + + cmd.args(["test", "--mt", "invariant_assume"]).assert_failure().stdout_eq(str![[r#" +... +[FAIL: revert: Invariant failure] +... +"#]]); + + // Change test to use assume instead require. Same test should fail with too many inputs + // rejected message instead persisted failure revert. + prj.add_test( + "AssumeTest.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract AssumeHandler is Test { + function fuzzMe(uint256 a) public { + vm.assume(false); + } +} + +contract AssumeTest is Test { + function setUp() public { + AssumeHandler handler = new AssumeHandler(); + } + function invariant_assume() public {} +} + "#, + ) + .unwrap(); + + cmd.assert_failure().stdout_eq(str![[r#" +... +[FAIL: `vm.assume` rejected too many inputs (10 allowed)] invariant_assume() (runs: 0, calls: 0, reverts: 0) +... +"#]]); +}); From 92e7ad511246e57016f97468767c4fc7999e6589 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 27 Sep 2024 15:13:37 +0200 Subject: [PATCH 038/100] chore: bump max allowed verification delay (#8974) --- crates/verify/src/retry.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/verify/src/retry.rs b/crates/verify/src/retry.rs index a12dde140..6067d9d85 100644 --- a/crates/verify/src/retry.rs +++ b/crates/verify/src/retry.rs @@ -23,7 +23,7 @@ pub struct RetryArgs { /// Optional delay to apply in between verification attempts, in seconds. #[arg( long, - value_parser = RangedU64ValueParser::::new().range(0..=30), + value_parser = RangedU64ValueParser::::new().range(0..=180), default_value = "5", )] pub delay: u32, From dd86b30d1b880525b3b008785fab3c65f9e2797d Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Fri, 27 Sep 2024 19:43:01 +0200 Subject: [PATCH 039/100] chore: rename `snapshot` to be more specific (#8945) * update internal naming * further internals * deprecate cheats * update Solidity tests and add dedicated test for testing deprecated cheatcodes * clarify gas snapshots * fix build * final fixes * fix build * fix repro 6355 rename --- crates/anvil/src/eth/api.rs | 7 +- crates/anvil/src/eth/backend/db.rs | 61 +++--- crates/anvil/src/eth/backend/mem/fork_db.rs | 42 ++-- .../anvil/src/eth/backend/mem/in_memory_db.rs | 32 +-- crates/anvil/src/eth/backend/mem/mod.rs | 48 ++--- crates/anvil/src/eth/backend/mem/storage.rs | 14 +- crates/anvil/tests/it/anvil_api.rs | 8 +- crates/anvil/tests/it/fork.rs | 32 ++- crates/cheatcodes/assets/cheatcodes.json | 124 ++++++++++- crates/cheatcodes/spec/src/vm.rs | 34 ++- crates/cheatcodes/src/evm.rs | 131 +++++++++--- crates/evm/core/src/backend/cow.rs | 28 +-- crates/evm/core/src/backend/in_memory_db.rs | 6 +- crates/evm/core/src/backend/mod.rs | 92 ++++---- crates/evm/core/src/backend/snapshot.rs | 26 +-- crates/evm/core/src/fork/database.rs | 64 +++--- crates/evm/core/src/lib.rs | 2 +- crates/evm/core/src/snapshot.rs | 74 ------- crates/evm/core/src/state_snapshot.rs | 74 +++++++ crates/evm/evm/src/executors/mod.rs | 16 +- crates/forge/bin/cmd/snapshot.rs | 87 ++++---- crates/forge/bin/cmd/watch.rs | 4 +- crates/forge/bin/main.rs | 2 +- crates/forge/bin/opts.rs | 4 +- crates/forge/tests/it/repros.rs | 2 +- testdata/cheats/Vm.sol | 5 + testdata/default/cheats/Prevrandao.t.sol | 4 +- testdata/default/cheats/Snapshots.t.sol | 105 --------- testdata/default/cheats/StateSnapshots.t.sol | 201 ++++++++++++++++++ testdata/default/cheats/loadAllocs.t.sol | 8 +- testdata/default/repros/Issue2984.t.sol | 4 +- testdata/default/repros/Issue3055.t.sol | 12 +- testdata/default/repros/Issue3792.t.sol | 4 +- testdata/default/repros/Issue6355.t.sol | 8 +- 34 files changed, 835 insertions(+), 530 deletions(-) delete mode 100644 crates/evm/core/src/snapshot.rs create mode 100644 crates/evm/core/src/state_snapshot.rs delete mode 100644 testdata/default/cheats/Snapshots.t.sol create mode 100644 testdata/default/cheats/StateSnapshots.t.sol diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 88698e13c..1f005d443 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -1913,7 +1913,6 @@ impl EthApi { pub async fn anvil_metadata(&self) -> Result { node_info!("anvil_metadata"); let fork_config = self.backend.get_fork(); - let snapshots = self.backend.list_snapshots(); Ok(Metadata { client_version: CLIENT_VERSION.to_string(), @@ -1926,7 +1925,7 @@ impl EthApi { fork_block_number: cfg.block_number(), fork_block_hash: cfg.block_hash(), }), - snapshots, + snapshots: self.backend.list_state_snapshots(), }) } @@ -2059,7 +2058,7 @@ impl EthApi { /// Handler for RPC call: `evm_snapshot` pub async fn evm_snapshot(&self) -> Result { node_info!("evm_snapshot"); - Ok(self.backend.create_snapshot().await) + Ok(self.backend.create_state_snapshot().await) } /// Revert the state of the blockchain to a previous snapshot. @@ -2068,7 +2067,7 @@ impl EthApi { /// Handler for RPC call: `evm_revert` pub async fn evm_revert(&self, id: U256) -> Result { node_info!("evm_revert"); - self.backend.revert_snapshot(id).await + self.backend.revert_state_snapshot(id).await } /// Jump forward in time by the given amount of time, in seconds. diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index a02f9d0e3..0aa0f8d13 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -11,7 +11,8 @@ use anvil_core::eth::{ use foundry_common::errors::FsPathError; use foundry_evm::{ backend::{ - BlockchainDb, DatabaseError, DatabaseResult, MemDb, RevertSnapshotAction, StateSnapshot, + BlockchainDb, DatabaseError, DatabaseResult, MemDb, RevertStateSnapshotAction, + StateSnapshot, }, revm::{ db::{CacheDB, DatabaseRef, DbAccount}, @@ -35,19 +36,19 @@ pub trait MaybeFullDatabase: DatabaseRef { None } - /// Clear the state and move it into a new `StateSnapshot` - fn clear_into_snapshot(&mut self) -> StateSnapshot; + /// Clear the state and move it into a new `StateSnapshot`. + fn clear_into_state_snapshot(&mut self) -> StateSnapshot; - /// Read the state snapshot + /// Read the state snapshot. /// - /// This clones all the states and returns a new `StateSnapshot` - fn read_as_snapshot(&self) -> StateSnapshot; + /// This clones all the states and returns a new `StateSnapshot`. + fn read_as_state_snapshot(&self) -> StateSnapshot; /// Clears the entire database fn clear(&mut self); - /// Reverses `clear_into_snapshot` by initializing the db's state with the snapshot - fn init_from_snapshot(&mut self, snapshot: StateSnapshot); + /// Reverses `clear_into_snapshot` by initializing the db's state with the state snapshot. + fn init_from_state_snapshot(&mut self, state_snapshot: StateSnapshot); } impl<'a, T: 'a + MaybeFullDatabase + ?Sized> MaybeFullDatabase for &'a T @@ -62,17 +63,17 @@ where T::maybe_as_full_db(self) } - fn clear_into_snapshot(&mut self) -> StateSnapshot { + fn clear_into_state_snapshot(&mut self) -> StateSnapshot { unreachable!("never called for DatabaseRef") } - fn read_as_snapshot(&self) -> StateSnapshot { + fn read_as_state_snapshot(&self) -> StateSnapshot { unreachable!("never called for DatabaseRef") } fn clear(&mut self) {} - fn init_from_snapshot(&mut self, _snapshot: StateSnapshot) {} + fn init_from_state_snapshot(&mut self, _state_snapshot: StateSnapshot) {} } /// Helper trait to reset the DB if it's forked @@ -176,13 +177,13 @@ pub trait Db: Ok(true) } - /// Creates a new snapshot - fn snapshot(&mut self) -> U256; + /// Creates a new state snapshot. + fn snapshot_state(&mut self) -> U256; - /// Reverts a snapshot + /// Reverts a state snapshot. /// - /// Returns `true` if the snapshot was reverted - fn revert(&mut self, snapshot: U256, action: RevertSnapshotAction) -> bool; + /// Returns `true` if the state snapshot was reverted. + fn revert_state(&mut self, state_snapshot: U256, action: RevertStateSnapshotAction) -> bool; /// Returns the state root if possible to compute fn maybe_state_root(&self) -> Option { @@ -228,11 +229,11 @@ impl + Send + Sync + Clone + fmt::Debug> D Ok(None) } - fn snapshot(&mut self) -> U256 { + fn snapshot_state(&mut self) -> U256 { U256::ZERO } - fn revert(&mut self, _snapshot: U256, _action: RevertSnapshotAction) -> bool { + fn revert_state(&mut self, _state_snapshot: U256, _action: RevertStateSnapshotAction) -> bool { false } @@ -250,7 +251,7 @@ impl> MaybeFullDatabase for CacheDB { Some(&self.accounts) } - fn clear_into_snapshot(&mut self) -> StateSnapshot { + fn clear_into_state_snapshot(&mut self) -> StateSnapshot { let db_accounts = std::mem::take(&mut self.accounts); let mut accounts = HashMap::new(); let mut account_storage = HashMap::new(); @@ -265,7 +266,7 @@ impl> MaybeFullDatabase for CacheDB { StateSnapshot { accounts, storage: account_storage, block_hashes } } - fn read_as_snapshot(&self) -> StateSnapshot { + fn read_as_state_snapshot(&self) -> StateSnapshot { let db_accounts = self.accounts.clone(); let mut accounts = HashMap::new(); let mut account_storage = HashMap::new(); @@ -282,11 +283,11 @@ impl> MaybeFullDatabase for CacheDB { } fn clear(&mut self) { - self.clear_into_snapshot(); + self.clear_into_state_snapshot(); } - fn init_from_snapshot(&mut self, snapshot: StateSnapshot) { - let StateSnapshot { accounts, mut storage, block_hashes } = snapshot; + fn init_from_state_snapshot(&mut self, state_snapshot: StateSnapshot) { + let StateSnapshot { accounts, mut storage, block_hashes } = state_snapshot; for (addr, mut acc) in accounts { if let Some(code) = acc.code.take() { @@ -330,7 +331,7 @@ impl StateDb { 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() + self.read_as_state_snapshot() } } @@ -362,20 +363,20 @@ impl MaybeFullDatabase for StateDb { self.0.maybe_as_full_db() } - fn clear_into_snapshot(&mut self) -> StateSnapshot { - self.0.clear_into_snapshot() + fn clear_into_state_snapshot(&mut self) -> StateSnapshot { + self.0.clear_into_state_snapshot() } - fn read_as_snapshot(&self) -> StateSnapshot { - self.0.read_as_snapshot() + fn read_as_state_snapshot(&self) -> StateSnapshot { + self.0.read_as_state_snapshot() } fn clear(&mut self) { self.0.clear() } - fn init_from_snapshot(&mut self, snapshot: StateSnapshot) { - self.0.init_from_snapshot(snapshot) + fn init_from_state_snapshot(&mut self, state_snapshot: StateSnapshot) { + self.0.init_from_state_snapshot(state_snapshot) } } diff --git a/crates/anvil/src/eth/backend/mem/fork_db.rs b/crates/anvil/src/eth/backend/mem/fork_db.rs index a5f6cf455..a4528a8f0 100644 --- a/crates/anvil/src/eth/backend/mem/fork_db.rs +++ b/crates/anvil/src/eth/backend/mem/fork_db.rs @@ -8,8 +8,10 @@ use crate::{ use alloy_primitives::{Address, B256, U256, U64}; use alloy_rpc_types::BlockId; use foundry_evm::{ - backend::{BlockchainDb, DatabaseError, DatabaseResult, RevertSnapshotAction, StateSnapshot}, - fork::database::ForkDbSnapshot, + backend::{ + BlockchainDb, DatabaseError, DatabaseResult, RevertStateSnapshotAction, StateSnapshot, + }, + fork::database::ForkDbStateSnapshot, revm::{primitives::BlockEnv, Database}, }; use revm::DatabaseRef; @@ -72,16 +74,16 @@ impl Db for ForkedDatabase { })) } - fn snapshot(&mut self) -> U256 { - self.insert_snapshot() + fn snapshot_state(&mut self) -> U256 { + self.insert_state_snapshot() } - fn revert(&mut self, id: U256, action: RevertSnapshotAction) -> bool { - self.revert_snapshot(id, action) + fn revert_state(&mut self, id: U256, action: RevertStateSnapshotAction) -> bool { + self.revert_state_snapshot(id, action) } fn current_state(&self) -> StateDb { - StateDb::new(self.create_snapshot()) + StateDb::new(self.create_state_snapshot()) } } @@ -90,7 +92,7 @@ impl MaybeFullDatabase for ForkedDatabase { self } - fn clear_into_snapshot(&mut self) -> StateSnapshot { + fn clear_into_state_snapshot(&mut self) -> StateSnapshot { let db = self.inner().db(); let accounts = std::mem::take(&mut *db.accounts.write()); let storage = std::mem::take(&mut *db.storage.write()); @@ -98,7 +100,7 @@ impl MaybeFullDatabase for ForkedDatabase { StateSnapshot { accounts, storage, block_hashes } } - fn read_as_snapshot(&self) -> StateSnapshot { + fn read_as_state_snapshot(&self) -> StateSnapshot { let db = self.inner().db(); let accounts = db.accounts.read().clone(); let storage = db.storage.read().clone(); @@ -108,38 +110,38 @@ impl MaybeFullDatabase for ForkedDatabase { fn clear(&mut self) { self.flush_cache(); - self.clear_into_snapshot(); + self.clear_into_state_snapshot(); } - fn init_from_snapshot(&mut self, snapshot: StateSnapshot) { + fn init_from_state_snapshot(&mut self, state_snapshot: StateSnapshot) { let db = self.inner().db(); - let StateSnapshot { accounts, storage, block_hashes } = snapshot; + let StateSnapshot { accounts, storage, block_hashes } = state_snapshot; *db.accounts.write() = accounts; *db.storage.write() = storage; *db.block_hashes.write() = block_hashes; } } -impl MaybeFullDatabase for ForkDbSnapshot { +impl MaybeFullDatabase for ForkDbStateSnapshot { fn as_dyn(&self) -> &dyn DatabaseRef { self } - fn clear_into_snapshot(&mut self) -> StateSnapshot { - std::mem::take(&mut self.snapshot) + fn clear_into_state_snapshot(&mut self) -> StateSnapshot { + std::mem::take(&mut self.state_snapshot) } - fn read_as_snapshot(&self) -> StateSnapshot { - self.snapshot.clone() + fn read_as_state_snapshot(&self) -> StateSnapshot { + self.state_snapshot.clone() } fn clear(&mut self) { - std::mem::take(&mut self.snapshot); + std::mem::take(&mut self.state_snapshot); self.local.clear() } - fn init_from_snapshot(&mut self, snapshot: StateSnapshot) { - self.snapshot = snapshot; + fn init_from_state_snapshot(&mut self, state_snapshot: StateSnapshot) { + self.state_snapshot = state_snapshot; } } 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 f984b93bb..01243c90f 100644 --- a/crates/anvil/src/eth/backend/mem/in_memory_db.rs +++ b/crates/anvil/src/eth/backend/mem/in_memory_db.rs @@ -17,7 +17,7 @@ use foundry_evm::{ // reexport for convenience pub use foundry_evm::{backend::MemDb, revm::db::DatabaseRef}; -use foundry_evm::{backend::RevertSnapshotAction, revm::primitives::BlockEnv}; +use foundry_evm::{backend::RevertStateSnapshotAction, revm::primitives::BlockEnv}; impl Db for MemDb { fn insert_account(&mut self, address: Address, account: AccountInfo) { @@ -74,22 +74,22 @@ impl Db for MemDb { } /// Creates a new snapshot - fn snapshot(&mut self) -> U256 { - let id = self.snapshots.insert(self.inner.clone()); - trace!(target: "backend::memdb", "Created new snapshot {}", id); + fn snapshot_state(&mut self) -> U256 { + let id = self.state_snapshots.insert(self.inner.clone()); + trace!(target: "backend::memdb", "Created new state snapshot {}", id); id } - fn revert(&mut self, id: U256, action: RevertSnapshotAction) -> bool { - if let Some(snapshot) = self.snapshots.remove(id) { + fn revert_state(&mut self, id: U256, action: RevertStateSnapshotAction) -> bool { + if let Some(state_snapshot) = self.state_snapshots.remove(id) { if action.is_keep() { - self.snapshots.insert_at(snapshot.clone(), id); + self.state_snapshots.insert_at(state_snapshot.clone(), id); } - self.inner = snapshot; - trace!(target: "backend::memdb", "Reverted snapshot {}", id); + self.inner = state_snapshot; + trace!(target: "backend::memdb", "Reverted state snapshot {}", id); true } else { - warn!(target: "backend::memdb", "No snapshot to revert for {}", id); + warn!(target: "backend::memdb", "No state snapshot to revert for {}", id); false } } @@ -112,20 +112,20 @@ impl MaybeFullDatabase for MemDb { Some(&self.inner.accounts) } - fn clear_into_snapshot(&mut self) -> StateSnapshot { - self.inner.clear_into_snapshot() + fn clear_into_state_snapshot(&mut self) -> StateSnapshot { + self.inner.clear_into_state_snapshot() } - fn read_as_snapshot(&self) -> StateSnapshot { - self.inner.read_as_snapshot() + fn read_as_state_snapshot(&self) -> StateSnapshot { + self.inner.read_as_state_snapshot() } fn clear(&mut self) { self.inner.clear(); } - fn init_from_snapshot(&mut self, snapshot: StateSnapshot) { - self.inner.init_from_snapshot(snapshot) + fn init_from_state_snapshot(&mut self, snapshot: StateSnapshot) { + self.inner.init_from_state_snapshot(snapshot) } } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 89cd0bd4b..cd23fa6d3 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -68,7 +68,7 @@ use anvil_rpc::error::RpcError; use alloy_chains::NamedChain; use flate2::{read::GzDecoder, write::GzEncoder, Compression}; use foundry_evm::{ - backend::{DatabaseError, DatabaseResult, RevertSnapshotAction}, + backend::{DatabaseError, DatabaseResult, RevertStateSnapshotAction}, constants::DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE, decode::RevertDecoder, inspectors::AccessListInspector, @@ -153,26 +153,26 @@ pub struct Backend { /// need to read from it, while it's currently written to, don't block. E.g. a new block is /// currently mined and a new [`Self::set_storage_at()`] request is being executed. db: Arc>>, - /// stores all block related data in memory + /// stores all block related data in memory. blockchain: Blockchain, - /// Historic states of previous blocks + /// Historic states of previous blocks. states: Arc>, - /// env data of the chain + /// Env data of the chain env: Arc>, - /// this is set if this is currently forked off another client + /// This is set if this is currently forked off another client. fork: Arc>>, - /// provides time related info, like timestamp + /// Provides time related info, like timestamp. time: TimeManager, - /// Contains state of custom overrides + /// Contains state of custom overrides. cheats: CheatsManager, - /// contains fee data + /// Contains fee data. fees: FeeManager, - /// initialised genesis + /// Initialised genesis. genesis: GenesisConfig, - /// listeners for new blocks that get notified when a new block was imported + /// Listeners for new blocks that get notified when a new block was imported. new_block_listeners: Arc>>>, - /// keeps track of active snapshots at a specific block - active_snapshots: Arc>>, + /// Keeps track of active state snapshots at a specific block. + active_state_snapshots: Arc>>, enable_steps_tracing: bool, print_logs: bool, alphanet: bool, @@ -256,7 +256,7 @@ impl Backend { new_block_listeners: Default::default(), fees, genesis, - active_snapshots: Arc::new(Mutex::new(Default::default())), + active_state_snapshots: Arc::new(Mutex::new(Default::default())), enable_steps_tracing, print_logs, alphanet, @@ -695,21 +695,21 @@ impl Backend { self.blockchain.storage.read().total_difficulty } - /// Creates a new `evm_snapshot` at the current height + /// Creates a new `evm_snapshot` at the current height. /// - /// Returns the id of the snapshot created - pub async fn create_snapshot(&self) -> U256 { + /// Returns the id of the snapshot created. + pub async fn create_state_snapshot(&self) -> U256 { let num = self.best_number(); let hash = self.best_hash(); - let id = self.db.write().await.snapshot(); + let id = self.db.write().await.snapshot_state(); trace!(target: "backend", "creating snapshot {} at {}", id, num); - self.active_snapshots.lock().insert(id, (num, hash)); + self.active_state_snapshots.lock().insert(id, (num, hash)); id } - /// Reverts the state to the snapshot identified by the given `id`. - pub async fn revert_snapshot(&self, id: U256) -> Result { - let block = { self.active_snapshots.lock().remove(&id) }; + /// Reverts the state to the state snapshot identified by the given `id`. + pub async fn revert_state_snapshot(&self, id: U256) -> Result { + let block = { self.active_state_snapshots.lock().remove(&id) }; if let Some((num, hash)) = block { let best_block_hash = { // revert the storage that's newer than the snapshot @@ -752,11 +752,11 @@ impl Backend { ..Default::default() }; } - Ok(self.db.write().await.revert(id, RevertSnapshotAction::RevertRemove)) + Ok(self.db.write().await.revert_state(id, RevertStateSnapshotAction::RevertRemove)) } - pub fn list_snapshots(&self) -> BTreeMap { - self.active_snapshots.lock().clone().into_iter().collect() + pub fn list_state_snapshots(&self) -> BTreeMap { + self.active_state_snapshots.lock().clone().into_iter().collect() } /// Get the current state. diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 4cb588221..928fb66e4 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -147,8 +147,8 @@ impl InMemoryBlockStates { { // only write to disk if supported if !self.is_memory_only() { - let snapshot = state.0.clear_into_snapshot(); - self.disk_cache.write(hash, snapshot); + let state_snapshot = state.0.clear_into_state_snapshot(); + self.disk_cache.write(hash, state_snapshot); self.on_disk_states.insert(hash, state); self.oldest_on_disk.push_back(hash); } @@ -170,7 +170,7 @@ impl InMemoryBlockStates { self.states.get(hash).or_else(|| { if let Some(state) = self.on_disk_states.get_mut(hash) { if let Some(cached) = self.disk_cache.read(*hash) { - state.init_from_snapshot(cached); + state.init_from_state_snapshot(cached); return Some(state); } } @@ -204,8 +204,8 @@ impl InMemoryBlockStates { // 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)); + if let Some(state_snapshot) = self.disk_cache.read(*hash) { + states.push((*hash, state_snapshot)); } }); @@ -214,9 +214,9 @@ impl InMemoryBlockStates { /// Load states from serialized data pub fn load_states(&mut self, states: SerializableHistoricalStates) { - for (hash, snapshot) in states { + for (hash, state_snapshot) in states { let mut state_db = StateDb::new(MemDb::default()); - state_db.init_from_snapshot(snapshot); + state_db.init_from_state_snapshot(state_snapshot); self.insert(hash, state_db); } } diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index 0e8001853..31e37c98b 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -591,9 +591,9 @@ async fn test_fork_revert_next_block_timestamp() { api.mine_one().await; let latest_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); - let snapshot_id = api.evm_snapshot().await.unwrap(); + let state_snapshot = api.evm_snapshot().await.unwrap(); api.mine_one().await; - api.evm_revert(snapshot_id).await.unwrap(); + api.evm_revert(state_snapshot).await.unwrap(); let block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); assert_eq!(block, latest_block); @@ -613,9 +613,9 @@ async fn test_fork_revert_call_latest_block_timestamp() { api.mine_one().await; let latest_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); - let snapshot_id = api.evm_snapshot().await.unwrap(); + let state_snapshot = api.evm_snapshot().await.unwrap(); api.mine_one().await; - api.evm_revert(snapshot_id).await.unwrap(); + api.evm_revert(state_snapshot).await.unwrap(); let multicall_contract = Multicall::new(address!("eefba1e63905ef1d7acba5a8513c70307c1ce441"), &provider); diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 3f96513c5..f93fa92a1 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -302,10 +302,10 @@ async fn test_fork_reset_setup() { } #[tokio::test(flavor = "multi_thread")] -async fn test_fork_snapshotting() { +async fn test_fork_state_snapshotting() { let (api, handle) = spawn(fork_config()).await; let provider = handle.http_provider(); - let snapshot = api.evm_snapshot().await.unwrap(); + let state_snapshot = api.evm_snapshot().await.unwrap(); let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); @@ -329,7 +329,7 @@ async fn test_fork_snapshotting() { let to_balance = provider.get_balance(to).await.unwrap(); assert_eq!(balance_before.saturating_add(amount), to_balance); - assert!(api.evm_revert(snapshot).await.unwrap()); + assert!(api.evm_revert(state_snapshot).await.unwrap()); let nonce = provider.get_transaction_count(from).await.unwrap(); assert_eq!(nonce, initial_nonce); @@ -341,11 +341,11 @@ async fn test_fork_snapshotting() { } #[tokio::test(flavor = "multi_thread")] -async fn test_fork_snapshotting_repeated() { +async fn test_fork_state_snapshotting_repeated() { let (api, handle) = spawn(fork_config()).await; let provider = handle.http_provider(); - let snapshot = api.evm_snapshot().await.unwrap(); + let state_snapshot = api.evm_snapshot().await.unwrap(); let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); @@ -366,9 +366,9 @@ async fn test_fork_snapshotting_repeated() { let to_balance = provider.get_balance(to).await.unwrap(); assert_eq!(balance_before.saturating_add(amount), to_balance); - let _second_snapshot = api.evm_snapshot().await.unwrap(); + let _second_state_snapshot = api.evm_snapshot().await.unwrap(); - assert!(api.evm_revert(snapshot).await.unwrap()); + assert!(api.evm_revert(state_snapshot).await.unwrap()); let nonce = provider.get_transaction_count(from).await.unwrap(); assert_eq!(nonce, initial_nonce); @@ -383,17 +383,16 @@ async fn test_fork_snapshotting_repeated() { // assert!(!api.evm_revert(second_snapshot).await.unwrap()); // nothing is reverted, snapshot gone - assert!(!api.evm_revert(snapshot).await.unwrap()); + assert!(!api.evm_revert(state_snapshot).await.unwrap()); } // #[tokio::test(flavor = "multi_thread")] -async fn test_fork_snapshotting_blocks() { +async fn test_fork_state_snapshotting_blocks() { let (api, handle) = spawn(fork_config()).await; let provider = handle.http_provider(); - // create a snapshot - let snapshot = api.evm_snapshot().await.unwrap(); + let state_snapshot = api.evm_snapshot().await.unwrap(); let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); @@ -417,8 +416,7 @@ async fn test_fork_snapshotting_blocks() { let to_balance = provider.get_balance(to).await.unwrap(); assert_eq!(balance_before.saturating_add(amount), to_balance); - // revert snapshot - assert!(api.evm_revert(snapshot).await.unwrap()); + assert!(api.evm_revert(state_snapshot).await.unwrap()); assert_eq!(initial_nonce, provider.get_transaction_count(from).await.unwrap()); let block_number_after = provider.get_block_number().await.unwrap(); @@ -429,8 +427,8 @@ async fn test_fork_snapshotting_blocks() { let nonce = provider.get_transaction_count(from).await.unwrap(); assert_eq!(nonce, initial_nonce + 1); - // revert again: nothing to revert since snapshot gone - assert!(!api.evm_revert(snapshot).await.unwrap()); + // revert again: nothing to revert since state snapshot gone + assert!(!api.evm_revert(state_snapshot).await.unwrap()); let nonce = provider.get_transaction_count(from).await.unwrap(); assert_eq!(nonce, initial_nonce + 1); let block_number_after = provider.get_block_number().await.unwrap(); @@ -1244,7 +1242,7 @@ async fn test_arbitrum_fork_block_number() { assert_eq!(block_number, initial_block_number); // take snapshot at initial block number - let snapshot = api.evm_snapshot().await.unwrap(); + let snapshot_state = api.evm_snapshot().await.unwrap(); // mine new block and check block number returned by `eth_blockNumber` api.mine_one().await; @@ -1257,7 +1255,7 @@ async fn test_arbitrum_fork_block_number() { assert!(block_by_number.other.get("l1BlockNumber").is_some()); // revert to recorded snapshot and check block number - assert!(api.evm_revert(snapshot).await.unwrap()); + assert!(api.evm_revert(snapshot_state).await.unwrap()); let block_number = api.block_number().unwrap().to::(); assert_eq!(block_number, initial_block_number); diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 451ce6cb4..fbf33e500 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -3574,7 +3574,7 @@ { "func": { "id": "deleteSnapshot", - "description": "Removes the snapshot with the given ID created by `snapshot`.\nTakes the snapshot ID to delete.\nReturns `true` if the snapshot was successfully deleted.\nReturns `false` if the snapshot does not exist.", + "description": "`deleteSnapshot` is being deprecated in favor of `deleteStateSnapshot`. It will be removed in future versions.", "declaration": "function deleteSnapshot(uint256 snapshotId) external returns (bool success);", "visibility": "external", "mutability": "", @@ -3588,13 +3588,15 @@ ] }, "group": "evm", - "status": "stable", + "status": { + "deprecated": "replaced by `deleteStateSnapshot`" + }, "safety": "unsafe" }, { "func": { "id": "deleteSnapshots", - "description": "Removes _all_ snapshots previously created by `snapshot`.", + "description": "`deleteSnapshots` is being deprecated in favor of `deleteStateSnapshots`. It will be removed in future versions.", "declaration": "function deleteSnapshots() external;", "visibility": "external", "mutability": "", @@ -3608,6 +3610,48 @@ ] }, "group": "evm", + "status": { + "deprecated": "replaced by `deleteStateSnapshots`" + }, + "safety": "unsafe" + }, + { + "func": { + "id": "deleteStateSnapshot", + "description": "Removes the snapshot with the given ID created by `snapshot`.\nTakes the snapshot ID to delete.\nReturns `true` if the snapshot was successfully deleted.\nReturns `false` if the snapshot does not exist.", + "declaration": "function deleteStateSnapshot(uint256 snapshotId) external returns (bool success);", + "visibility": "external", + "mutability": "", + "signature": "deleteStateSnapshot(uint256)", + "selector": "0x08d6b37a", + "selectorBytes": [ + 8, + 214, + 179, + 122 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, + { + "func": { + "id": "deleteStateSnapshots", + "description": "Removes _all_ snapshots previously created by `snapshot`.", + "declaration": "function deleteStateSnapshots() external;", + "visibility": "external", + "mutability": "", + "signature": "deleteStateSnapshots()", + "selector": "0xe0933c74", + "selectorBytes": [ + 224, + 147, + 60, + 116 + ] + }, + "group": "evm", "status": "stable", "safety": "unsafe" }, @@ -7456,7 +7500,7 @@ { "func": { "id": "revertTo", - "description": "Revert the state of the EVM to a previous snapshot\nTakes the snapshot ID to revert to.\nReturns `true` if the snapshot was successfully reverted.\nReturns `false` if the snapshot does not exist.\n**Note:** This does not automatically delete the snapshot. To delete the snapshot use `deleteSnapshot`.", + "description": "`revertTo` is being deprecated in favor of `revertToState`. It will be removed in future versions.", "declaration": "function revertTo(uint256 snapshotId) external returns (bool success);", "visibility": "external", "mutability": "", @@ -7470,13 +7514,15 @@ ] }, "group": "evm", - "status": "stable", + "status": { + "deprecated": "replaced by `revertToState`" + }, "safety": "unsafe" }, { "func": { "id": "revertToAndDelete", - "description": "Revert the state of the EVM to a previous snapshot and automatically deletes the snapshots\nTakes the snapshot ID to revert to.\nReturns `true` if the snapshot was successfully reverted and deleted.\nReturns `false` if the snapshot does not exist.", + "description": "`revertToAndDelete` is being deprecated in favor of `revertToStateAndDelete`. It will be removed in future versions.", "declaration": "function revertToAndDelete(uint256 snapshotId) external returns (bool success);", "visibility": "external", "mutability": "", @@ -7490,6 +7536,48 @@ ] }, "group": "evm", + "status": { + "deprecated": "replaced by `revertToStateAndDelete`" + }, + "safety": "unsafe" + }, + { + "func": { + "id": "revertToState", + "description": "Revert the state of the EVM to a previous snapshot\nTakes the snapshot ID to revert to.\nReturns `true` if the snapshot was successfully reverted.\nReturns `false` if the snapshot does not exist.\n**Note:** This does not automatically delete the snapshot. To delete the snapshot use `deleteStateSnapshot`.", + "declaration": "function revertToState(uint256 snapshotId) external returns (bool success);", + "visibility": "external", + "mutability": "", + "signature": "revertToState(uint256)", + "selector": "0xc2527405", + "selectorBytes": [ + 194, + 82, + 116, + 5 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, + { + "func": { + "id": "revertToStateAndDelete", + "description": "Revert the state of the EVM to a previous snapshot and automatically deletes the snapshots\nTakes the snapshot ID to revert to.\nReturns `true` if the snapshot was successfully reverted and deleted.\nReturns `false` if the snapshot does not exist.", + "declaration": "function revertToStateAndDelete(uint256 snapshotId) external returns (bool success);", + "visibility": "external", + "mutability": "", + "signature": "revertToStateAndDelete(uint256)", + "selector": "0x3a1985dc", + "selectorBytes": [ + 58, + 25, + 133, + 220 + ] + }, + "group": "evm", "status": "stable", "safety": "unsafe" }, @@ -8456,7 +8544,7 @@ { "func": { "id": "snapshot", - "description": "Snapshot the current state of the evm.\nReturns the ID of the snapshot that was created.\nTo revert a snapshot use `revertTo`.", + "description": "`snapshot` is being deprecated in favor of `snapshotState`. It will be removed in future versions.", "declaration": "function snapshot() external returns (uint256 snapshotId);", "visibility": "external", "mutability": "", @@ -8470,6 +8558,28 @@ ] }, "group": "evm", + "status": { + "deprecated": "replaced by `snapshotState`" + }, + "safety": "unsafe" + }, + { + "func": { + "id": "snapshotState", + "description": "Snapshot the current state of the evm.\nReturns the ID of the snapshot that was created.\nTo revert a snapshot use `revertToState`.", + "declaration": "function snapshotState() external returns (uint256 snapshotId);", + "visibility": "external", + "mutability": "", + "signature": "snapshotState()", + "selector": "0x9cd23835", + "selectorBytes": [ + 156, + 210, + 56, + 53 + ] + }, + "group": "evm", "status": "stable", "safety": "unsafe" }, diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 22500ec98..69293d1b6 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -511,11 +511,19 @@ interface Vm { // -------- State Snapshots -------- + /// `snapshot` is being deprecated in favor of `snapshotState`. It will be removed in future versions. + #[cheatcode(group = Evm, safety = Unsafe, status = Deprecated(Some("replaced by `snapshotState`")))] + function snapshot() external returns (uint256 snapshotId); + /// Snapshot the current state of the evm. /// Returns the ID of the snapshot that was created. - /// To revert a snapshot use `revertTo`. + /// To revert a snapshot use `revertToState`. #[cheatcode(group = Evm, safety = Unsafe)] - function snapshot() external returns (uint256 snapshotId); + function snapshotState() external returns (uint256 snapshotId); + + /// `revertTo` is being deprecated in favor of `revertToState`. It will be removed in future versions. + #[cheatcode(group = Evm, safety = Unsafe, status = Deprecated(Some("replaced by `revertToState`")))] + function revertTo(uint256 snapshotId) external returns (bool success); /// Revert the state of the EVM to a previous snapshot /// Takes the snapshot ID to revert to. @@ -523,9 +531,13 @@ interface Vm { /// Returns `true` if the snapshot was successfully reverted. /// Returns `false` if the snapshot does not exist. /// - /// **Note:** This does not automatically delete the snapshot. To delete the snapshot use `deleteSnapshot`. + /// **Note:** This does not automatically delete the snapshot. To delete the snapshot use `deleteStateSnapshot`. #[cheatcode(group = Evm, safety = Unsafe)] - function revertTo(uint256 snapshotId) external returns (bool success); + function revertToState(uint256 snapshotId) external returns (bool success); + + /// `revertToAndDelete` is being deprecated in favor of `revertToStateAndDelete`. It will be removed in future versions. + #[cheatcode(group = Evm, safety = Unsafe, status = Deprecated(Some("replaced by `revertToStateAndDelete`")))] + function revertToAndDelete(uint256 snapshotId) external returns (bool success); /// Revert the state of the EVM to a previous snapshot and automatically deletes the snapshots /// Takes the snapshot ID to revert to. @@ -533,7 +545,11 @@ interface Vm { /// Returns `true` if the snapshot was successfully reverted and deleted. /// Returns `false` if the snapshot does not exist. #[cheatcode(group = Evm, safety = Unsafe)] - function revertToAndDelete(uint256 snapshotId) external returns (bool success); + function revertToStateAndDelete(uint256 snapshotId) external returns (bool success); + + /// `deleteSnapshot` is being deprecated in favor of `deleteStateSnapshot`. It will be removed in future versions. + #[cheatcode(group = Evm, safety = Unsafe, status = Deprecated(Some("replaced by `deleteStateSnapshot`")))] + function deleteSnapshot(uint256 snapshotId) external returns (bool success); /// Removes the snapshot with the given ID created by `snapshot`. /// Takes the snapshot ID to delete. @@ -541,11 +557,15 @@ interface Vm { /// Returns `true` if the snapshot was successfully deleted. /// Returns `false` if the snapshot does not exist. #[cheatcode(group = Evm, safety = Unsafe)] - function deleteSnapshot(uint256 snapshotId) external returns (bool success); + function deleteStateSnapshot(uint256 snapshotId) external returns (bool success); + + /// `deleteSnapshots` is being deprecated in favor of `deleteStateSnapshots`. It will be removed in future versions. + #[cheatcode(group = Evm, safety = Unsafe, status = Deprecated(Some("replaced by `deleteStateSnapshots`")))] + function deleteSnapshots() external; /// Removes _all_ snapshots previously created by `snapshot`. #[cheatcode(group = Evm, safety = Unsafe)] - function deleteSnapshots() external; + function deleteStateSnapshots() external; // -------- Forking -------- // --- Creation and Selection --- diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 45b6f8c75..7ed1ce1a4 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -10,7 +10,7 @@ use alloy_rlp::Decodable; use alloy_sol_types::SolValue; use foundry_common::fs::{read_json_file, write_json_file}; use foundry_evm_core::{ - backend::{DatabaseExt, RevertSnapshotAction}, + backend::{DatabaseExt, RevertStateSnapshotAction}, constants::{CALLER, CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, TEST_CONTRACT_ADDRESS}, }; use rand::Rng; @@ -506,63 +506,78 @@ impl Cheatcode for readCallersCall { } } +// Deprecated in favor of `snapshotStateCall` impl Cheatcode for snapshotCall { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; - Ok(ccx.ecx.db.snapshot(&ccx.ecx.journaled_state, &ccx.ecx.env).abi_encode()) + inner_snapshot_state(ccx) } } +impl Cheatcode for snapshotStateCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self {} = self; + inner_snapshot_state(ccx) + } +} + +// Deprecated in favor of `revertToStateCall` impl Cheatcode for revertToCall { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { snapshotId } = self; - let result = if let Some(journaled_state) = ccx.ecx.db.revert( - *snapshotId, - &ccx.ecx.journaled_state, - &mut ccx.ecx.env, - RevertSnapshotAction::RevertKeep, - ) { - // we reset the evm's journaled_state to the state of the snapshot previous state - ccx.ecx.journaled_state = journaled_state; - true - } else { - false - }; - Ok(result.abi_encode()) + inner_revert_to_state(ccx, *snapshotId) } } +impl Cheatcode for revertToStateCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { snapshotId } = self; + inner_revert_to_state(ccx, *snapshotId) + } +} + +// Deprecated in favor of `revertToStateAndDeleteCall` impl Cheatcode for revertToAndDeleteCall { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { snapshotId } = self; - let result = if let Some(journaled_state) = ccx.ecx.db.revert( - *snapshotId, - &ccx.ecx.journaled_state, - &mut ccx.ecx.env, - RevertSnapshotAction::RevertRemove, - ) { - // we reset the evm's journaled_state to the state of the snapshot previous state - ccx.ecx.journaled_state = journaled_state; - true - } else { - false - }; - Ok(result.abi_encode()) + inner_revert_to_state_and_delete(ccx, *snapshotId) } } +impl Cheatcode for revertToStateAndDeleteCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { snapshotId } = self; + inner_revert_to_state_and_delete(ccx, *snapshotId) + } +} + +// Deprecated in favor of `deleteStateSnapshotCall` impl Cheatcode for deleteSnapshotCall { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { snapshotId } = self; - let result = ccx.ecx.db.delete_snapshot(*snapshotId); - Ok(result.abi_encode()) + inner_delete_state_snapshot(ccx, *snapshotId) + } +} + +impl Cheatcode for deleteStateSnapshotCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { snapshotId } = self; + inner_delete_state_snapshot(ccx, *snapshotId) } } + +// Deprecated in favor of `deleteStateSnapshotsCall` impl Cheatcode for deleteSnapshotsCall { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; - ccx.ecx.db.delete_snapshots(); - Ok(Default::default()) + inner_delete_state_snapshots(ccx) + } +} + +impl Cheatcode for deleteStateSnapshotsCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self {} = self; + inner_delete_state_snapshots(ccx) } } @@ -628,6 +643,58 @@ pub(super) fn get_nonce(ccx: &mut CheatsCtxt, address: &Add Ok(account.info.nonce.abi_encode()) } +fn inner_snapshot_state(ccx: &mut CheatsCtxt) -> Result { + Ok(ccx.ecx.db.snapshot_state(&ccx.ecx.journaled_state, &ccx.ecx.env).abi_encode()) +} + +fn inner_revert_to_state(ccx: &mut CheatsCtxt, snapshot_id: U256) -> Result { + let result = if let Some(journaled_state) = ccx.ecx.db.revert_state( + snapshot_id, + &ccx.ecx.journaled_state, + &mut ccx.ecx.env, + RevertStateSnapshotAction::RevertKeep, + ) { + // we reset the evm's journaled_state to the state of the snapshot previous state + ccx.ecx.journaled_state = journaled_state; + true + } else { + false + }; + Ok(result.abi_encode()) +} + +fn inner_revert_to_state_and_delete( + ccx: &mut CheatsCtxt, + snapshot_id: U256, +) -> Result { + let result = if let Some(journaled_state) = ccx.ecx.db.revert_state( + snapshot_id, + &ccx.ecx.journaled_state, + &mut ccx.ecx.env, + RevertStateSnapshotAction::RevertRemove, + ) { + // we reset the evm's journaled_state to the state of the snapshot previous state + ccx.ecx.journaled_state = journaled_state; + true + } else { + false + }; + Ok(result.abi_encode()) +} + +fn inner_delete_state_snapshot( + ccx: &mut CheatsCtxt, + snapshot_id: U256, +) -> Result { + let result = ccx.ecx.db.delete_state_snapshot(snapshot_id); + Ok(result.abi_encode()) +} + +fn inner_delete_state_snapshots(ccx: &mut CheatsCtxt) -> Result { + ccx.ecx.db.delete_state_snapshots(); + Ok(Default::default()) +} + /// Reads the current caller information and returns the current [CallerMode], `msg.sender` and /// `tx.origin`. /// diff --git a/crates/evm/core/src/backend/cow.rs b/crates/evm/core/src/backend/cow.rs index cba792b32..8156d75fb 100644 --- a/crates/evm/core/src/backend/cow.rs +++ b/crates/evm/core/src/backend/cow.rs @@ -3,7 +3,7 @@ use super::BackendError; use crate::{ backend::{ - diagnostic::RevertDiagnostic, Backend, DatabaseExt, LocalForkId, RevertSnapshotAction, + diagnostic::RevertDiagnostic, Backend, DatabaseExt, LocalForkId, RevertStateSnapshotAction, }, fork::{CreateFork, ForkId}, InspectorExt, @@ -84,11 +84,11 @@ impl<'a> CowBackend<'a> { Ok(res) } - /// Returns whether there was a snapshot failure in the backend. + /// Returns whether there was a state snapshot failure in the backend. /// /// This is bubbled up from the underlying Copy-On-Write backend when a revert occurs. - pub fn has_snapshot_failure(&self) -> bool { - self.backend.has_snapshot_failure() + pub fn has_state_snapshot_failure(&self) -> bool { + self.backend.has_state_snapshot_failure() } /// Returns a mutable instance of the Backend. @@ -115,31 +115,31 @@ impl<'a> CowBackend<'a> { } impl<'a> DatabaseExt for CowBackend<'a> { - fn snapshot(&mut self, journaled_state: &JournaledState, env: &Env) -> U256 { - self.backend_mut(env).snapshot(journaled_state, env) + fn snapshot_state(&mut self, journaled_state: &JournaledState, env: &Env) -> U256 { + self.backend_mut(env).snapshot_state(journaled_state, env) } - fn revert( + fn revert_state( &mut self, id: U256, journaled_state: &JournaledState, current: &mut Env, - action: RevertSnapshotAction, + action: RevertStateSnapshotAction, ) -> Option { - self.backend_mut(current).revert(id, journaled_state, current, action) + self.backend_mut(current).revert_state(id, journaled_state, current, action) } - fn delete_snapshot(&mut self, id: U256) -> bool { - // delete snapshot requires a previous snapshot to be initialized + fn delete_state_snapshot(&mut self, id: U256) -> bool { + // delete state snapshot requires a previous snapshot to be initialized if let Some(backend) = self.initialized_backend_mut() { - return backend.delete_snapshot(id) + return backend.delete_state_snapshot(id) } false } - fn delete_snapshots(&mut self) { + fn delete_state_snapshots(&mut self) { if let Some(backend) = self.initialized_backend_mut() { - backend.delete_snapshots() + backend.delete_state_snapshots() } } diff --git a/crates/evm/core/src/backend/in_memory_db.rs b/crates/evm/core/src/backend/in_memory_db.rs index e819c5313..4e90bfec0 100644 --- a/crates/evm/core/src/backend/in_memory_db.rs +++ b/crates/evm/core/src/backend/in_memory_db.rs @@ -1,6 +1,6 @@ //! In-memory database. -use crate::snapshot::Snapshots; +use crate::state_snapshot::StateSnapshots; use alloy_primitives::{Address, B256, U256}; use foundry_fork_db::DatabaseError; use revm::{ @@ -20,12 +20,12 @@ pub type FoundryEvmInMemoryDB = CacheDB; #[derive(Debug)] pub struct MemDb { pub inner: FoundryEvmInMemoryDB, - pub snapshots: Snapshots, + pub state_snapshots: StateSnapshots, } impl Default for MemDb { fn default() -> Self { - Self { inner: CacheDB::new(Default::default()), snapshots: Default::default() } + Self { inner: CacheDB::new(Default::default()), state_snapshots: Default::default() } } } diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 6fbd2086d..49830bc89 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -3,7 +3,7 @@ use crate::{ constants::{CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, TEST_CONTRACT_ADDRESS}, fork::{CreateFork, ForkId, MultiFork}, - snapshot::Snapshots, + state_snapshot::StateSnapshots, utils::{configure_tx_env, new_evm_with_inspector}, InspectorExt, }; @@ -42,7 +42,7 @@ mod in_memory_db; pub use in_memory_db::{EmptyDBWrapper, FoundryEvmInMemoryDB, MemDb}; mod snapshot; -pub use snapshot::{BackendSnapshot, RevertSnapshotAction, StateSnapshot}; +pub use snapshot::{BackendStateSnapshot, RevertStateSnapshotAction, StateSnapshot}; // A `revm::Database` that is used in forking mode type ForkDB = CacheDB; @@ -71,12 +71,12 @@ pub const GLOBAL_FAIL_SLOT: U256 = /// An extension trait that allows us to easily extend the `revm::Inspector` capabilities #[auto_impl::auto_impl(&mut)] pub trait DatabaseExt: Database + DatabaseCommit { - /// Creates a new snapshot at the current point of execution. + /// Creates a new state snapshot at the current point of execution. /// - /// A snapshot is associated with a new unique id that's created for the snapshot. - /// Snapshots can be reverted: [DatabaseExt::revert], however, depending on the - /// [RevertSnapshotAction], it will keep the snapshot alive or delete it. - fn snapshot(&mut self, journaled_state: &JournaledState, env: &Env) -> U256; + /// A state snapshot is associated with a new unique id that's created for the snapshot. + /// State snapshots can be reverted: [DatabaseExt::revert_state], however, depending on the + /// [RevertStateSnapshotAction], it will keep the snapshot alive or delete it. + fn snapshot_state(&mut self, journaled_state: &JournaledState, env: &Env) -> U256; /// Reverts the snapshot if it exists /// @@ -87,25 +87,25 @@ pub trait DatabaseExt: Database + DatabaseCommit { /// since the snapshots was created. This way we can show logs that were emitted between /// snapshot and its revert. /// This will also revert any changes in the `Env` and replace it with the captured `Env` of - /// `Self::snapshot`. + /// `Self::snapshot_state`. /// - /// Depending on [RevertSnapshotAction] it will keep the snapshot alive or delete it. - fn revert( + /// Depending on [RevertStateSnapshotAction] it will keep the snapshot alive or delete it. + fn revert_state( &mut self, id: U256, journaled_state: &JournaledState, env: &mut Env, - action: RevertSnapshotAction, + action: RevertStateSnapshotAction, ) -> Option; - /// Deletes the snapshot with the given `id` + /// Deletes the state snapshot with the given `id` /// /// Returns `true` if the snapshot was successfully deleted, `false` if no snapshot for that id /// exists. - fn delete_snapshot(&mut self, id: U256) -> bool; + fn delete_state_snapshot(&mut self, id: U256) -> bool; - /// Deletes all snapshots. - fn delete_snapshots(&mut self); + /// Deletes all state snapshots. + fn delete_state_snapshots(&mut self); /// Creates and also selects a new fork /// @@ -410,7 +410,7 @@ struct _ObjectSafe(dyn DatabaseExt); /// afterwards, as well as any snapshots taken after the reverted snapshot, (e.g.: reverting to id /// 0x1 will delete snapshots with ids 0x1, 0x2, etc.) /// -/// **Note:** Snapshots work across fork-swaps, e.g. if fork `A` is currently active, then a +/// **Note:** State snapshots work across fork-swaps, e.g. if fork `A` is currently active, then a /// snapshot is created before fork `B` is selected, then fork `A` will be the active fork again /// after reverting the snapshot. #[derive(Clone, Debug)] @@ -554,8 +554,10 @@ impl Backend { } /// Returns all snapshots created in this backend - pub fn snapshots(&self) -> &Snapshots> { - &self.inner.snapshots + pub fn state_snapshots( + &self, + ) -> &StateSnapshots> { + &self.inner.state_snapshots } /// Sets the address of the `DSTest` contract that is being executed @@ -591,18 +593,18 @@ impl Backend { self.inner.caller } - /// Failures occurred in snapshots are tracked when the snapshot is reverted + /// Failures occurred in state snapshots are tracked when the state snapshot is reverted. /// - /// If an error occurs in a restored snapshot, the test is considered failed. + /// If an error occurs in a restored state snapshot, the test is considered failed. /// - /// This returns whether there was a reverted snapshot that recorded an error - pub fn has_snapshot_failure(&self) -> bool { - self.inner.has_snapshot_failure + /// This returns whether there was a reverted state snapshot that recorded an error. + pub fn has_state_snapshot_failure(&self) -> bool { + self.inner.has_state_snapshot_failure } - /// Sets the snapshot failure flag. - pub fn set_snapshot_failure(&mut self, has_snapshot_failure: bool) { - self.inner.has_snapshot_failure = has_snapshot_failure + /// Sets the state snapshot failure flag. + pub fn set_state_snapshot_failure(&mut self, has_state_snapshot_failure: bool) { + self.inner.has_state_snapshot_failure = has_state_snapshot_failure } /// When creating or switching forks, we update the AccountInfo of the contract @@ -904,9 +906,9 @@ impl Backend { } impl DatabaseExt for Backend { - fn snapshot(&mut self, journaled_state: &JournaledState, env: &Env) -> U256 { + fn snapshot_state(&mut self, journaled_state: &JournaledState, env: &Env) -> U256 { trace!("create snapshot"); - let id = self.inner.snapshots.insert(BackendSnapshot::new( + let id = self.inner.state_snapshots.insert(BackendStateSnapshot::new( self.create_db_snapshot(), journaled_state.clone(), env.clone(), @@ -915,18 +917,18 @@ impl DatabaseExt for Backend { id } - fn revert( + fn revert_state( &mut self, id: U256, current_state: &JournaledState, current: &mut Env, - action: RevertSnapshotAction, + action: RevertStateSnapshotAction, ) -> Option { trace!(?id, "revert snapshot"); - if let Some(mut snapshot) = self.inner.snapshots.remove_at(id) { + if let Some(mut snapshot) = self.inner.state_snapshots.remove_at(id) { // Re-insert snapshot to persist it if action.is_keep() { - self.inner.snapshots.insert_at(snapshot.clone(), id); + self.inner.state_snapshots.insert_at(snapshot.clone(), id); } // https://github.com/foundry-rs/foundry/issues/3055 @@ -936,14 +938,14 @@ impl DatabaseExt for Backend { if let Some(account) = current_state.state.get(&CHEATCODE_ADDRESS) { if let Some(slot) = account.storage.get(&GLOBAL_FAIL_SLOT) { if !slot.present_value.is_zero() { - self.set_snapshot_failure(true); + self.set_state_snapshot_failure(true); } } } // merge additional logs snapshot.merge(current_state); - let BackendSnapshot { db, mut journaled_state, env } = snapshot; + let BackendStateSnapshot { db, mut journaled_state, env } = snapshot; match db { BackendDatabaseSnapshot::InMemory(mem_db) => { self.mem_db = mem_db; @@ -966,7 +968,7 @@ impl DatabaseExt for Backend { } caller_account.into() }); - self.inner.revert_snapshot(id, fork_id, idx, *fork); + self.inner.revert_state_snapshot(id, fork_id, idx, *fork); self.active_fork_ids = Some((id, idx)) } } @@ -981,12 +983,12 @@ impl DatabaseExt for Backend { } } - fn delete_snapshot(&mut self, id: U256) -> bool { - self.inner.snapshots.remove_at(id).is_some() + fn delete_state_snapshot(&mut self, id: U256) -> bool { + self.inner.state_snapshots.remove_at(id).is_some() } - fn delete_snapshots(&mut self) { - self.inner.snapshots.clear() + fn delete_state_snapshots(&mut self) { + self.inner.state_snapshots.clear() } fn create_fork(&mut self, create_fork: CreateFork) -> eyre::Result { @@ -1599,8 +1601,8 @@ pub struct BackendInner { /// Holds all created fork databases // Note: data is stored in an `Option` so we can remove it without reshuffling pub forks: Vec>, - /// Contains snapshots made at a certain point - pub snapshots: Snapshots>, + /// Contains state snapshots made at a certain point + pub state_snapshots: StateSnapshots>, /// Tracks whether there was a failure in a snapshot that was reverted /// /// The Test contract contains a bool variable that is set to true when an `assert` function @@ -1610,7 +1612,7 @@ pub struct BackendInner { /// reverted we get the _current_ `revm::JournaledState` which contains the state that we can /// check if the `_failed` variable is set, /// additionally - pub has_snapshot_failure: bool, + pub has_state_snapshot_failure: bool, /// Tracks the caller of the test function pub caller: Option

, /// Tracks numeric identifiers for forks @@ -1694,7 +1696,7 @@ impl BackendInner { } /// Reverts the entire fork database - pub fn revert_snapshot( + pub fn revert_state_snapshot( &mut self, id: LocalForkId, fork_id: ForkId, @@ -1797,8 +1799,8 @@ impl Default for BackendInner { issued_local_fork_ids: Default::default(), created_forks: Default::default(), forks: vec![], - snapshots: Default::default(), - has_snapshot_failure: false, + state_snapshots: Default::default(), + has_state_snapshot_failure: false, caller: None, next_fork_id: Default::default(), persistent_accounts: Default::default(), diff --git a/crates/evm/core/src/backend/snapshot.rs b/crates/evm/core/src/backend/snapshot.rs index f8961c7a0..7bfed1283 100644 --- a/crates/evm/core/src/backend/snapshot.rs +++ b/crates/evm/core/src/backend/snapshot.rs @@ -13,9 +13,9 @@ pub struct StateSnapshot { pub block_hashes: HashMap, } -/// Represents a snapshot taken during evm execution +/// Represents a state snapshot taken during evm execution #[derive(Clone, Debug)] -pub struct BackendSnapshot { +pub struct BackendStateSnapshot { pub db: T, /// The journaled_state state at a specific point pub journaled_state: JournaledState, @@ -23,38 +23,38 @@ pub struct BackendSnapshot { pub env: Env, } -impl BackendSnapshot { - /// Takes a new snapshot +impl BackendStateSnapshot { + /// Takes a new state snapshot. pub fn new(db: T, journaled_state: JournaledState, env: Env) -> Self { Self { db, journaled_state, env } } - /// Called when this snapshot is reverted. + /// Called when this state snapshot is reverted. /// /// Since we want to keep all additional logs that were emitted since the snapshot was taken /// we'll merge additional logs into the snapshot's `revm::JournaledState`. Additional logs are /// those logs that are missing in the snapshot's journaled_state, since the current /// journaled_state includes the same logs, we can simply replace use that See also - /// `DatabaseExt::revert` + /// `DatabaseExt::revert`. pub fn merge(&mut self, current: &JournaledState) { self.journaled_state.logs.clone_from(¤t.logs); } } -/// What to do when reverting a snapshot +/// What to do when reverting a state snapshot. /// -/// Whether to remove the snapshot or keep it +/// Whether to remove the state snapshot or keep it. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub enum RevertSnapshotAction { - /// Remove the snapshot after reverting +pub enum RevertStateSnapshotAction { + /// Remove the state snapshot after reverting. #[default] RevertRemove, - /// Keep the snapshot after reverting + /// Keep the state snapshot after reverting. RevertKeep, } -impl RevertSnapshotAction { - /// Returns `true` if the action is to keep the snapshot +impl RevertStateSnapshotAction { + /// Returns `true` if the action is to keep the state snapshot. pub fn is_keep(&self) -> bool { matches!(self, Self::RevertKeep) } diff --git a/crates/evm/core/src/fork/database.rs b/crates/evm/core/src/fork/database.rs index 873e8a896..a8d2ccc1d 100644 --- a/crates/evm/core/src/fork/database.rs +++ b/crates/evm/core/src/fork/database.rs @@ -1,8 +1,8 @@ //! A revm database that forks off a remote client use crate::{ - backend::{RevertSnapshotAction, StateSnapshot}, - snapshot::Snapshots, + backend::{RevertStateSnapshotAction, StateSnapshot}, + state_snapshot::StateSnapshots, }; use alloy_primitives::{Address, B256, U256}; use alloy_rpc_types::BlockId; @@ -23,22 +23,22 @@ use std::sync::Arc; /// `backend` will also write (missing) data to the `db` in the background #[derive(Clone, Debug)] pub struct ForkedDatabase { - /// responsible for fetching missing data + /// Responsible for fetching missing data. /// - /// This is responsible for getting data + /// This is responsible for getting data. backend: SharedBackend, /// Cached Database layer, ensures that changes are not written to the database that /// exclusively stores the state of the remote client. /// /// This separates Read/Write operations - /// - reads from the `SharedBackend as DatabaseRef` writes to the internal cache storage + /// - reads from the `SharedBackend as DatabaseRef` writes to the internal cache storage. cache_db: CacheDB, - /// Contains all the data already fetched + /// Contains all the data already fetched. /// - /// This exclusively stores the _unchanged_ remote client state + /// This exclusively stores the _unchanged_ remote client state. db: BlockchainDb, - /// holds the snapshot state of a blockchain - snapshots: Arc>>, + /// Holds the state snapshots of a blockchain. + state_snapshots: Arc>>, } impl ForkedDatabase { @@ -48,7 +48,7 @@ impl ForkedDatabase { cache_db: CacheDB::new(backend.clone()), backend, db, - snapshots: Arc::new(Mutex::new(Default::default())), + state_snapshots: Arc::new(Mutex::new(Default::default())), } } @@ -60,8 +60,8 @@ impl ForkedDatabase { &mut self.cache_db } - pub fn snapshots(&self) -> &Arc>> { - &self.snapshots + pub fn state_snapshots(&self) -> &Arc>> { + &self.state_snapshots } /// Reset the fork to a fresh forked state, and optionally update the fork config @@ -92,35 +92,35 @@ impl ForkedDatabase { &self.db } - pub fn create_snapshot(&self) -> ForkDbSnapshot { + pub fn create_state_snapshot(&self) -> ForkDbStateSnapshot { let db = self.db.db(); - let snapshot = StateSnapshot { + let state_snapshot = StateSnapshot { accounts: db.accounts.read().clone(), storage: db.storage.read().clone(), block_hashes: db.block_hashes.read().clone(), }; - ForkDbSnapshot { local: self.cache_db.clone(), snapshot } + ForkDbStateSnapshot { local: self.cache_db.clone(), state_snapshot } } - pub fn insert_snapshot(&self) -> U256 { - let snapshot = self.create_snapshot(); - let mut snapshots = self.snapshots().lock(); - let id = snapshots.insert(snapshot); + pub fn insert_state_snapshot(&self) -> U256 { + let state_snapshot = self.create_state_snapshot(); + let mut state_snapshots = self.state_snapshots().lock(); + let id = state_snapshots.insert(state_snapshot); trace!(target: "backend::forkdb", "Created new snapshot {}", id); id } /// Removes the snapshot from the tracked snapshot and sets it as the current state - pub fn revert_snapshot(&mut self, id: U256, action: RevertSnapshotAction) -> bool { - let snapshot = { self.snapshots().lock().remove_at(id) }; - if let Some(snapshot) = snapshot { + pub fn revert_state_snapshot(&mut self, id: U256, action: RevertStateSnapshotAction) -> bool { + let state_snapshot = { self.state_snapshots().lock().remove_at(id) }; + if let Some(state_snapshot) = state_snapshot { if action.is_keep() { - self.snapshots().lock().insert_at(snapshot.clone(), id); + self.state_snapshots().lock().insert_at(state_snapshot.clone(), id); } - let ForkDbSnapshot { + let ForkDbStateSnapshot { local, - snapshot: StateSnapshot { accounts, storage, block_hashes }, - } = snapshot; + state_snapshot: StateSnapshot { accounts, storage, block_hashes }, + } = state_snapshot; let db = self.inner().db(); { let mut accounts_lock = db.accounts.write(); @@ -202,12 +202,12 @@ impl DatabaseCommit for ForkedDatabase { /// /// This mimics `revm::CacheDB` #[derive(Clone, Debug)] -pub struct ForkDbSnapshot { +pub struct ForkDbStateSnapshot { pub local: CacheDB, - pub snapshot: StateSnapshot, + pub state_snapshot: StateSnapshot, } -impl ForkDbSnapshot { +impl ForkDbStateSnapshot { fn get_storage(&self, address: Address, index: U256) -> Option { self.local.accounts.get(&address).and_then(|account| account.storage.get(&index)).copied() } @@ -216,14 +216,14 @@ impl ForkDbSnapshot { // This `DatabaseRef` implementation works similar to `CacheDB` which prioritizes modified elements, // and uses another db as fallback // We prioritize stored changed accounts/storage -impl DatabaseRef for ForkDbSnapshot { +impl DatabaseRef for ForkDbStateSnapshot { type Error = DatabaseError; fn basic_ref(&self, address: Address) -> Result, Self::Error> { match self.local.accounts.get(&address) { Some(account) => Ok(Some(account.info.clone())), None => { - let mut acc = self.snapshot.accounts.get(&address).cloned(); + let mut acc = self.state_snapshot.accounts.get(&address).cloned(); if acc.is_none() { acc = self.local.basic_ref(address)?; @@ -254,7 +254,7 @@ impl DatabaseRef for ForkDbSnapshot { } fn block_hash_ref(&self, number: u64) -> Result { - match self.snapshot.block_hashes.get(&U256::from(number)).copied() { + match self.state_snapshot.block_hashes.get(&U256::from(number)).copied() { None => self.local.block_hash_ref(number), Some(block_hash) => Ok(block_hash), } diff --git a/crates/evm/core/src/lib.rs b/crates/evm/core/src/lib.rs index 48c647865..b30a36544 100644 --- a/crates/evm/core/src/lib.rs +++ b/crates/evm/core/src/lib.rs @@ -26,7 +26,7 @@ pub mod fork; pub mod opcodes; pub mod opts; pub mod precompiles; -pub mod snapshot; +pub mod state_snapshot; pub mod utils; /// An extension trait that allows us to add additional hooks to Inspector for later use in diff --git a/crates/evm/core/src/snapshot.rs b/crates/evm/core/src/snapshot.rs deleted file mode 100644 index 423f853ee..000000000 --- a/crates/evm/core/src/snapshot.rs +++ /dev/null @@ -1,74 +0,0 @@ -//! support for snapshotting different states - -use alloy_primitives::U256; -use std::{collections::HashMap, ops::Add}; - -/// Represents all snapshots -#[derive(Clone, Debug)] -pub struct Snapshots { - id: U256, - snapshots: HashMap, -} - -impl Snapshots { - fn next_id(&mut self) -> U256 { - let id = self.id; - self.id = id.saturating_add(U256::from(1)); - id - } - - /// Returns the snapshot with the given id `id` - pub fn get(&self, id: U256) -> Option<&T> { - self.snapshots.get(&id) - } - - /// Removes the snapshot with the given `id`. - /// - /// This will also remove any snapshots taken after the snapshot with the `id`. e.g.: reverting - /// to id 1 will delete snapshots with ids 1, 2, 3, etc.) - pub fn remove(&mut self, id: U256) -> Option { - let snapshot = self.snapshots.remove(&id); - - // revert all snapshots taken after the snapshot - let mut to_revert = id.add(U256::from(1)); - while to_revert < self.id { - self.snapshots.remove(&to_revert); - to_revert += U256::from(1); - } - - snapshot - } - - /// Removes all snapshots - pub fn clear(&mut self) { - self.snapshots.clear(); - } - - /// Removes the snapshot with the given `id`. - /// - /// Does not remove snapshots after it. - pub fn remove_at(&mut self, id: U256) -> Option { - self.snapshots.remove(&id) - } - - /// Inserts the new snapshot and returns the id - pub fn insert(&mut self, snapshot: T) -> U256 { - let id = self.next_id(); - self.snapshots.insert(id, snapshot); - id - } - - /// Inserts the new snapshot at the given `id`. - /// - /// Does not auto-increment the next `id`. - pub fn insert_at(&mut self, snapshot: T, id: U256) -> U256 { - self.snapshots.insert(id, snapshot); - id - } -} - -impl Default for Snapshots { - fn default() -> Self { - Self { id: U256::ZERO, snapshots: HashMap::new() } - } -} diff --git a/crates/evm/core/src/state_snapshot.rs b/crates/evm/core/src/state_snapshot.rs new file mode 100644 index 000000000..e273a5733 --- /dev/null +++ b/crates/evm/core/src/state_snapshot.rs @@ -0,0 +1,74 @@ +//! Support for snapshotting different states + +use alloy_primitives::U256; +use std::{collections::HashMap, ops::Add}; + +/// Represents all state snapshots +#[derive(Clone, Debug)] +pub struct StateSnapshots { + id: U256, + state_snapshots: HashMap, +} + +impl StateSnapshots { + fn next_id(&mut self) -> U256 { + let id = self.id; + self.id = id.saturating_add(U256::from(1)); + id + } + + /// Returns the state snapshot with the given id `id` + pub fn get(&self, id: U256) -> Option<&T> { + self.state_snapshots.get(&id) + } + + /// Removes the state snapshot with the given `id`. + /// + /// This will also remove any state snapshots taken after the state snapshot with the `id`. + /// e.g.: reverting to id 1 will delete snapshots with ids 1, 2, 3, etc.) + pub fn remove(&mut self, id: U256) -> Option { + let snapshot_state = self.state_snapshots.remove(&id); + + // Revert all state snapshots taken after the state snapshot with the `id` + let mut to_revert = id.add(U256::from(1)); + while to_revert < self.id { + self.state_snapshots.remove(&to_revert); + to_revert += U256::from(1); + } + + snapshot_state + } + + /// Removes all state snapshots. + pub fn clear(&mut self) { + self.state_snapshots.clear(); + } + + /// Removes the state snapshot with the given `id`. + /// + /// Does not remove state snapshots after it. + pub fn remove_at(&mut self, id: U256) -> Option { + self.state_snapshots.remove(&id) + } + + /// Inserts the new state snapshot and returns the id. + pub fn insert(&mut self, state_snapshot: T) -> U256 { + let id = self.next_id(); + self.state_snapshots.insert(id, state_snapshot); + id + } + + /// Inserts the new state snapshot at the given `id`. + /// + /// Does not auto-increment the next `id`. + pub fn insert_at(&mut self, state_snapshot: T, id: U256) -> U256 { + self.state_snapshots.insert(id, state_snapshot); + id + } +} + +impl Default for StateSnapshots { + fn default() -> Self { + Self { id: U256::ZERO, state_snapshots: HashMap::new() } + } +} diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index dc923d6e0..7a1a794f6 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -395,7 +395,7 @@ impl Executor { let mut inspector = self.inspector().clone(); let mut backend = CowBackend::new_borrowed(self.backend()); let result = backend.inspect(&mut env, &mut inspector)?; - convert_executed_result(env, inspector, result, backend.has_snapshot_failure()) + convert_executed_result(env, inspector, result, backend.has_state_snapshot_failure()) } /// Execute the transaction configured in `env.tx`. @@ -405,7 +405,7 @@ impl Executor { let backend = self.backend_mut(); let result = backend.inspect(&mut env, &mut inspector)?; let mut result = - convert_executed_result(env, inspector, result, backend.has_snapshot_failure())?; + convert_executed_result(env, inspector, result, backend.has_state_snapshot_failure())?; self.commit(&mut result); Ok(result) } @@ -465,7 +465,7 @@ impl Executor { call_result: &RawCallResult, should_fail: bool, ) -> bool { - if call_result.has_snapshot_failure { + if call_result.has_state_snapshot_failure { // a failure occurred in a reverted snapshot, which is considered a failed test return should_fail; } @@ -517,7 +517,7 @@ impl Executor { } // A failure occurred in a reverted snapshot, which is considered a failed test. - if self.backend().has_snapshot_failure() { + if self.backend().has_state_snapshot_failure() { return false; } @@ -711,7 +711,7 @@ pub struct RawCallResult { /// /// This is tracked separately from revert because a snapshot failure can occur without a /// revert, since assert failures are stored in a global variable (ds-test legacy) - pub has_snapshot_failure: bool, + pub has_state_snapshot_failure: bool, /// The raw result of the call. pub result: Bytes, /// The gas used for the call @@ -747,7 +747,7 @@ impl Default for RawCallResult { Self { exit_reason: InstructionResult::Continue, reverted: false, - has_snapshot_failure: false, + has_state_snapshot_failure: false, result: Bytes::new(), gas_used: 0, gas_refunded: 0, @@ -842,7 +842,7 @@ fn convert_executed_result( env: EnvWithHandlerCfg, inspector: InspectorStack, ResultAndState { result, state: state_changeset }: ResultAndState, - has_snapshot_failure: bool, + has_state_snapshot_failure: bool, ) -> eyre::Result { let (exit_reason, gas_refunded, gas_used, out, exec_logs) = match result { ExecutionResult::Success { reason, gas_used, gas_refunded, output, logs, .. } => { @@ -884,7 +884,7 @@ fn convert_executed_result( Ok(RawCallResult { exit_reason, reverted: !matches!(exit_reason, return_ok!()), - has_snapshot_failure, + has_state_snapshot_failure, result, gas_used, gas_refunded, diff --git a/crates/forge/bin/cmd/snapshot.rs b/crates/forge/bin/cmd/snapshot.rs index 8c3d57fc3..20c5013ec 100644 --- a/crates/forge/bin/cmd/snapshot.rs +++ b/crates/forge/bin/cmd/snapshot.rs @@ -24,8 +24,8 @@ pub static RE_BASIC_SNAPSHOT_ENTRY: LazyLock = LazyLock::new(|| { /// CLI arguments for `forge snapshot`. #[derive(Clone, Debug, Parser)] -pub struct SnapshotArgs { - /// Output a diff against a pre-existing snapshot. +pub struct GasSnapshotArgs { + /// Output a diff against a pre-existing gas snapshot. /// /// By default, the comparison is done with .gas-snapshot. #[arg( @@ -36,9 +36,9 @@ pub struct SnapshotArgs { )] diff: Option>, - /// Compare against a pre-existing snapshot, exiting with code 1 if they do not match. + /// Compare against a pre-existing gas snapshot, exiting with code 1 if they do not match. /// - /// Outputs a diff if the snapshots do not match. + /// Outputs a diff if the gas snapshots do not match. /// /// By default, the comparison is done with .gas-snapshot. #[arg( @@ -54,7 +54,7 @@ pub struct SnapshotArgs { #[arg(long, hide(true))] format: Option, - /// Output file for the snapshot. + /// Output file for the gas snapshot. #[arg( long, default_value = ".gas-snapshot", @@ -77,11 +77,11 @@ pub struct SnapshotArgs { /// Additional configs for test results #[command(flatten)] - config: SnapshotConfig, + config: GasSnapshotConfig, } -impl SnapshotArgs { - /// Returns whether `SnapshotArgs` was configured with `--watch` +impl GasSnapshotArgs { + /// Returns whether `GasSnapshotArgs` was configured with `--watch` pub fn is_watch(&self) -> bool { self.test.is_watch() } @@ -102,18 +102,18 @@ impl SnapshotArgs { if let Some(path) = self.diff { let snap = path.as_ref().unwrap_or(&self.snap); - let snaps = read_snapshot(snap)?; + let snaps = read_gas_snapshot(snap)?; diff(tests, snaps)?; } else if let Some(path) = self.check { let snap = path.as_ref().unwrap_or(&self.snap); - let snaps = read_snapshot(snap)?; + let snaps = read_gas_snapshot(snap)?; if check(tests, snaps, self.tolerance) { std::process::exit(0) } else { std::process::exit(1) } } else { - write_to_snapshot_file(&tests, self.snap, self.format)?; + write_to_gas_snapshot_file(&tests, self.snap, self.format)?; } Ok(()) } @@ -138,7 +138,7 @@ impl FromStr for Format { /// Additional filters that can be applied on the test results #[derive(Clone, Debug, Default, Parser)] -struct SnapshotConfig { +struct GasSnapshotConfig { /// Sort results by gas used (ascending). #[arg(long)] asc: bool, @@ -156,7 +156,7 @@ struct SnapshotConfig { max: Option, } -impl SnapshotConfig { +impl GasSnapshotConfig { fn is_in_gas_range(&self, gas_used: u64) -> bool { if let Some(min) = self.min { if gas_used < min { @@ -187,20 +187,20 @@ impl SnapshotConfig { } } -/// A general entry in a snapshot file +/// A general entry in a gas snapshot file /// /// Has the form: /// `(gas:? 40181)` for normal tests /// `(runs: 256, μ: 40181, ~: 40181)` for fuzz tests /// `(runs: 256, calls: 40181, reverts: 40181)` for invariant tests #[derive(Clone, Debug, PartialEq, Eq)] -pub struct SnapshotEntry { +pub struct GasSnapshotEntry { pub contract_name: String, pub signature: String, pub gas_used: TestKindReport, } -impl FromStr for SnapshotEntry { +impl FromStr for GasSnapshotEntry { type Err = String; fn from_str(s: &str) -> Result { @@ -253,8 +253,8 @@ impl FromStr for SnapshotEntry { } } -/// Reads a list of snapshot entries from a snapshot file -fn read_snapshot(path: impl AsRef) -> Result> { +/// Reads a list of gas snapshot entries from a gas snapshot file. +fn read_gas_snapshot(path: impl AsRef) -> Result> { let path = path.as_ref(); let mut entries = Vec::new(); for line in io::BufReader::new( @@ -263,13 +263,14 @@ fn read_snapshot(path: impl AsRef) -> Result> { ) .lines() { - entries.push(SnapshotEntry::from_str(line?.as_str()).map_err(|err| eyre::eyre!("{err}"))?); + entries + .push(GasSnapshotEntry::from_str(line?.as_str()).map_err(|err| eyre::eyre!("{err}"))?); } Ok(entries) } -/// Writes a series of tests to a snapshot file after sorting them -fn write_to_snapshot_file( +/// Writes a series of tests to a gas snapshot file after sorting them. +fn write_to_gas_snapshot_file( tests: &[SuiteTestResult], path: impl AsRef, _format: Option, @@ -288,15 +289,15 @@ fn write_to_snapshot_file( Ok(fs::write(path, content)?) } -/// A Snapshot entry diff +/// A Gas snapshot entry diff. #[derive(Clone, Debug, PartialEq, Eq)] -pub struct SnapshotDiff { +pub struct GasSnapshotDiff { pub signature: String, pub source_gas_used: TestKindReport, pub target_gas_used: TestKindReport, } -impl SnapshotDiff { +impl GasSnapshotDiff { /// Returns the gas diff /// /// `> 0` if the source used more gas @@ -311,10 +312,14 @@ impl SnapshotDiff { } } -/// Compares the set of tests with an existing snapshot +/// Compares the set of tests with an existing gas snapshot. /// /// Returns true all tests match -fn check(tests: Vec, snaps: Vec, tolerance: Option) -> bool { +fn check( + tests: Vec, + snaps: Vec, + tolerance: Option, +) -> bool { let snaps = snaps .into_iter() .map(|s| ((s.contract_name, s.signature), s.gas_used)) @@ -347,8 +352,8 @@ fn check(tests: Vec, snaps: Vec, tolerance: Opti !has_diff } -/// Compare the set of tests with an existing snapshot -fn diff(tests: Vec, snaps: Vec) -> Result<()> { +/// Compare the set of tests with an existing gas snapshot. +fn diff(tests: Vec, snaps: Vec) -> Result<()> { let snaps = snaps .into_iter() .map(|s| ((s.contract_name, s.signature), s.gas_used)) @@ -358,7 +363,7 @@ fn diff(tests: Vec, snaps: Vec) -> Result<()> { if let Some(target_gas_used) = snaps.get(&(test.contract_name().to_string(), test.signature.clone())).cloned() { - diffs.push(SnapshotDiff { + diffs.push(GasSnapshotDiff { source_gas_used: test.result.kind.report(), signature: test.signature, target_gas_used, @@ -446,12 +451,12 @@ mod tests { } #[test] - fn can_parse_basic_snapshot_entry() { + fn can_parse_basic_gas_snapshot_entry() { let s = "Test:deposit() (gas: 7222)"; - let entry = SnapshotEntry::from_str(s).unwrap(); + let entry = GasSnapshotEntry::from_str(s).unwrap(); assert_eq!( entry, - SnapshotEntry { + GasSnapshotEntry { contract_name: "Test".to_string(), signature: "deposit()".to_string(), gas_used: TestKindReport::Unit { gas: 7222 } @@ -460,12 +465,12 @@ mod tests { } #[test] - fn can_parse_fuzz_snapshot_entry() { + fn can_parse_fuzz_gas_snapshot_entry() { let s = "Test:deposit() (runs: 256, μ: 100, ~:200)"; - let entry = SnapshotEntry::from_str(s).unwrap(); + let entry = GasSnapshotEntry::from_str(s).unwrap(); assert_eq!( entry, - SnapshotEntry { + GasSnapshotEntry { contract_name: "Test".to_string(), signature: "deposit()".to_string(), gas_used: TestKindReport::Fuzz { runs: 256, median_gas: 200, mean_gas: 100 } @@ -474,12 +479,12 @@ mod tests { } #[test] - fn can_parse_invariant_snapshot_entry() { + fn can_parse_invariant_gas_snapshot_entry() { let s = "Test:deposit() (runs: 256, calls: 100, reverts: 200)"; - let entry = SnapshotEntry::from_str(s).unwrap(); + let entry = GasSnapshotEntry::from_str(s).unwrap(); assert_eq!( entry, - SnapshotEntry { + GasSnapshotEntry { contract_name: "Test".to_string(), signature: "deposit()".to_string(), gas_used: TestKindReport::Invariant { runs: 256, calls: 100, reverts: 200 } @@ -488,12 +493,12 @@ mod tests { } #[test] - fn can_parse_invariant_snapshot_entry2() { + fn can_parse_invariant_gas_snapshot_entry2() { let s = "ERC20Invariants:invariantBalanceSum() (runs: 256, calls: 3840, reverts: 2388)"; - let entry = SnapshotEntry::from_str(s).unwrap(); + let entry = GasSnapshotEntry::from_str(s).unwrap(); assert_eq!( entry, - SnapshotEntry { + GasSnapshotEntry { contract_name: "ERC20Invariants".to_string(), signature: "invariantBalanceSum()".to_string(), gas_used: TestKindReport::Invariant { runs: 256, calls: 3840, reverts: 2388 } diff --git a/crates/forge/bin/cmd/watch.rs b/crates/forge/bin/cmd/watch.rs index 10562ba12..19b37009f 100644 --- a/crates/forge/bin/cmd/watch.rs +++ b/crates/forge/bin/cmd/watch.rs @@ -1,4 +1,4 @@ -use super::{build::BuildArgs, doc::DocArgs, snapshot::SnapshotArgs, test::TestArgs}; +use super::{build::BuildArgs, doc::DocArgs, snapshot::GasSnapshotArgs, test::TestArgs}; use clap::Parser; use eyre::Result; use foundry_cli::utils::{self, FoundryPathExt}; @@ -249,7 +249,7 @@ pub async fn watch_build(args: BuildArgs) -> Result<()> { /// Executes a [`Watchexec`] that listens for changes in the project's src dir and reruns `forge /// snapshot` -pub async fn watch_snapshot(args: SnapshotArgs) -> Result<()> { +pub async fn watch_gas_snapshot(args: GasSnapshotArgs) -> Result<()> { let config = args.watchexec_config()?; run(config).await } diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs index f92d3db80..9a98d8aef 100644 --- a/crates/forge/bin/main.rs +++ b/crates/forge/bin/main.rs @@ -88,7 +88,7 @@ fn main() -> Result<()> { } ForgeSubcommand::Snapshot(cmd) => { if cmd.is_watch() { - utils::block_on(watch::watch_snapshot(cmd)) + utils::block_on(watch::watch_gas_snapshot(cmd)) } else { utils::block_on(cmd.run()) } diff --git a/crates/forge/bin/opts.rs b/crates/forge/bin/opts.rs index fbe1f67df..c929d0185 100644 --- a/crates/forge/bin/opts.rs +++ b/crates/forge/bin/opts.rs @@ -123,9 +123,9 @@ pub enum ForgeSubcommand { /// Manage the Foundry cache. Cache(CacheArgs), - /// Create a snapshot of each test's gas usage. + /// Create a gas snapshot of each test's gas usage. #[command(visible_alias = "s")] - Snapshot(snapshot::SnapshotArgs), + Snapshot(snapshot::GasSnapshotArgs), /// Display the current config. #[command(visible_alias = "co")] diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index 0d1e15052..d463d12dd 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -251,7 +251,7 @@ test_repro!(6355, false, None, |res| { let test = res.test_results.remove("test_shouldFail()").unwrap(); assert_eq!(test.status, TestStatus::Failure); - let test = res.test_results.remove("test_shouldFailWithRevertTo()").unwrap(); + let test = res.test_results.remove("test_shouldFailWithRevertToState()").unwrap(); assert_eq!(test.status, TestStatus::Failure); }); diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 2b9347382..09099f540 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -176,6 +176,8 @@ interface Vm { function deal(address account, uint256 newBalance) external; function deleteSnapshot(uint256 snapshotId) external returns (bool success); function deleteSnapshots() external; + function deleteStateSnapshot(uint256 snapshotId) external returns (bool success); + function deleteStateSnapshots() external; function deployCode(string calldata artifactPath) external returns (address deployedAddress); function deployCode(string calldata artifactPath, bytes calldata constructorArgs) external returns (address deployedAddress); function deriveKey(string calldata mnemonic, uint32 index) external pure returns (uint256 privateKey); @@ -370,6 +372,8 @@ interface Vm { function resumeTracing() external view; function revertTo(uint256 snapshotId) external returns (bool success); function revertToAndDelete(uint256 snapshotId) external returns (bool success); + function revertToState(uint256 snapshotId) external returns (bool success); + function revertToStateAndDelete(uint256 snapshotId) external returns (bool success); function revokePersistent(address account) external; function revokePersistent(address[] calldata accounts) external; function roll(uint256 newHeight) external; @@ -419,6 +423,7 @@ interface Vm { function skip(bool skipTest, string calldata reason) external; function sleep(uint256 duration) external; function snapshot() external returns (uint256 snapshotId); + function snapshotState() external returns (uint256 snapshotId); function split(string calldata input, string calldata delimiter) external pure returns (string[] memory outputs); function startBroadcast() external; function startBroadcast(address signer) external; diff --git a/testdata/default/cheats/Prevrandao.t.sol b/testdata/default/cheats/Prevrandao.t.sol index 7011ce3be..75f2b2cc1 100644 --- a/testdata/default/cheats/Prevrandao.t.sol +++ b/testdata/default/cheats/Prevrandao.t.sol @@ -23,12 +23,12 @@ contract PrevrandaoTest is DSTest { function testPrevrandaoSnapshotFuzzed(uint256 newPrevrandao) public { vm.assume(newPrevrandao != block.prevrandao); uint256 oldPrevrandao = block.prevrandao; - uint256 snapshot = vm.snapshot(); + uint256 snapshotId = vm.snapshotState(); vm.prevrandao(newPrevrandao); assertEq(block.prevrandao, newPrevrandao); - assert(vm.revertTo(snapshot)); + assert(vm.revertToState(snapshotId)); assertEq(block.prevrandao, oldPrevrandao); } } diff --git a/testdata/default/cheats/Snapshots.t.sol b/testdata/default/cheats/Snapshots.t.sol deleted file mode 100644 index bb7b4e0e6..000000000 --- a/testdata/default/cheats/Snapshots.t.sol +++ /dev/null @@ -1,105 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; - -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -struct Storage { - uint256 slot0; - uint256 slot1; -} - -contract SnapshotTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - - Storage store; - - function setUp() public { - store.slot0 = 10; - store.slot1 = 20; - } - - function testSnapshot() public { - uint256 snapshot = vm.snapshot(); - store.slot0 = 300; - store.slot1 = 400; - - assertEq(store.slot0, 300); - assertEq(store.slot1, 400); - - vm.revertTo(snapshot); - assertEq(store.slot0, 10, "snapshot revert for slot 0 unsuccessful"); - assertEq(store.slot1, 20, "snapshot revert for slot 1 unsuccessful"); - } - - function testSnapshotRevertDelete() public { - uint256 snapshot = vm.snapshot(); - store.slot0 = 300; - store.slot1 = 400; - - assertEq(store.slot0, 300); - assertEq(store.slot1, 400); - - vm.revertToAndDelete(snapshot); - assertEq(store.slot0, 10, "snapshot revert for slot 0 unsuccessful"); - assertEq(store.slot1, 20, "snapshot revert for slot 1 unsuccessful"); - // nothing to revert to anymore - assert(!vm.revertTo(snapshot)); - } - - function testSnapshotDelete() public { - uint256 snapshot = vm.snapshot(); - store.slot0 = 300; - store.slot1 = 400; - - vm.deleteSnapshot(snapshot); - // nothing to revert to anymore - assert(!vm.revertTo(snapshot)); - } - - function testSnapshotDeleteAll() public { - uint256 snapshot = vm.snapshot(); - store.slot0 = 300; - store.slot1 = 400; - - vm.deleteSnapshots(); - // nothing to revert to anymore - assert(!vm.revertTo(snapshot)); - } - - // - function testSnapshotsMany() public { - uint256 preState; - for (uint256 c = 0; c < 10; c++) { - for (uint256 cc = 0; cc < 10; cc++) { - preState = vm.snapshot(); - vm.revertToAndDelete(preState); - assert(!vm.revertTo(preState)); - } - } - } - - // tests that snapshots can also revert changes to `block` - function testBlockValues() public { - uint256 num = block.number; - uint256 time = block.timestamp; - uint256 prevrandao = block.prevrandao; - - uint256 snapshot = vm.snapshot(); - - vm.warp(1337); - assertEq(block.timestamp, 1337); - - vm.roll(99); - assertEq(block.number, 99); - - vm.prevrandao(uint256(123)); - assertEq(block.prevrandao, 123); - - assert(vm.revertTo(snapshot)); - - assertEq(block.number, num, "snapshot revert for block.number unsuccessful"); - assertEq(block.timestamp, time, "snapshot revert for block.timestamp unsuccessful"); - assertEq(block.prevrandao, prevrandao, "snapshot revert for block.prevrandao unsuccessful"); - } -} diff --git a/testdata/default/cheats/StateSnapshots.t.sol b/testdata/default/cheats/StateSnapshots.t.sol new file mode 100644 index 000000000..92ee38d26 --- /dev/null +++ b/testdata/default/cheats/StateSnapshots.t.sol @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +struct Storage { + uint256 slot0; + uint256 slot1; +} + +contract StateSnapshotTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + Storage store; + + function setUp() public { + store.slot0 = 10; + store.slot1 = 20; + } + + function testStateSnapshot() public { + uint256 snapshotId = vm.snapshotState(); + store.slot0 = 300; + store.slot1 = 400; + + assertEq(store.slot0, 300); + assertEq(store.slot1, 400); + + vm.revertToState(snapshotId); + assertEq(store.slot0, 10, "snapshot revert for slot 0 unsuccessful"); + assertEq(store.slot1, 20, "snapshot revert for slot 1 unsuccessful"); + } + + function testStateSnapshotRevertDelete() public { + uint256 snapshotId = vm.snapshotState(); + store.slot0 = 300; + store.slot1 = 400; + + assertEq(store.slot0, 300); + assertEq(store.slot1, 400); + + vm.revertToStateAndDelete(snapshotId); + assertEq(store.slot0, 10, "snapshot revert for slot 0 unsuccessful"); + assertEq(store.slot1, 20, "snapshot revert for slot 1 unsuccessful"); + // nothing to revert to anymore + assert(!vm.revertToState(snapshotId)); + } + + function testStateSnapshotDelete() public { + uint256 snapshotId = vm.snapshotState(); + store.slot0 = 300; + store.slot1 = 400; + + vm.deleteStateSnapshot(snapshotId); + // nothing to revert to anymore + assert(!vm.revertToState(snapshotId)); + } + + function testStateSnapshotDeleteAll() public { + uint256 snapshotId = vm.snapshotState(); + store.slot0 = 300; + store.slot1 = 400; + + vm.deleteStateSnapshots(); + // nothing to revert to anymore + assert(!vm.revertToState(snapshotId)); + } + + // + function testStateSnapshotsMany() public { + uint256 snapshotId; + for (uint256 c = 0; c < 10; c++) { + for (uint256 cc = 0; cc < 10; cc++) { + snapshotId = vm.snapshotState(); + vm.revertToStateAndDelete(snapshotId); + assert(!vm.revertToState(snapshotId)); + } + } + } + + // tests that snapshots can also revert changes to `block` + function testBlockValues() public { + uint256 num = block.number; + uint256 time = block.timestamp; + uint256 prevrandao = block.prevrandao; + + uint256 snapshotId = vm.snapshotState(); + + vm.warp(1337); + assertEq(block.timestamp, 1337); + + vm.roll(99); + assertEq(block.number, 99); + + vm.prevrandao(uint256(123)); + assertEq(block.prevrandao, 123); + + assert(vm.revertToState(snapshotId)); + + assertEq(block.number, num, "snapshot revert for block.number unsuccessful"); + assertEq(block.timestamp, time, "snapshot revert for block.timestamp unsuccessful"); + assertEq(block.prevrandao, prevrandao, "snapshot revert for block.prevrandao unsuccessful"); + } +} + +// TODO: remove this test suite once `snapshot*` has been deprecated in favor of `snapshotState*`. +contract DeprecatedStateSnapshotTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + Storage store; + + function setUp() public { + store.slot0 = 10; + store.slot1 = 20; + } + + function testSnapshotState() public { + uint256 snapshotId = vm.snapshot(); + store.slot0 = 300; + store.slot1 = 400; + + assertEq(store.slot0, 300); + assertEq(store.slot1, 400); + + vm.revertTo(snapshotId); + assertEq(store.slot0, 10, "snapshot revert for slot 0 unsuccessful"); + assertEq(store.slot1, 20, "snapshot revert for slot 1 unsuccessful"); + } + + function testSnapshotStateRevertDelete() public { + uint256 snapshotId = vm.snapshot(); + store.slot0 = 300; + store.slot1 = 400; + + assertEq(store.slot0, 300); + assertEq(store.slot1, 400); + + vm.revertToAndDelete(snapshotId); + assertEq(store.slot0, 10, "snapshot revert for slot 0 unsuccessful"); + assertEq(store.slot1, 20, "snapshot revert for slot 1 unsuccessful"); + // nothing to revert to anymore + assert(!vm.revertTo(snapshotId)); + } + + function testSnapshotStateDelete() public { + uint256 snapshotId = vm.snapshot(); + store.slot0 = 300; + store.slot1 = 400; + + vm.deleteSnapshot(snapshotId); + // nothing to revert to anymore + assert(!vm.revertTo(snapshotId)); + } + + function testSnapshotStateDeleteAll() public { + uint256 snapshotId = vm.snapshot(); + store.slot0 = 300; + store.slot1 = 400; + + vm.deleteSnapshots(); + // nothing to revert to anymore + assert(!vm.revertTo(snapshotId)); + } + + // + function testSnapshotStatesMany() public { + uint256 snapshotId; + for (uint256 c = 0; c < 10; c++) { + for (uint256 cc = 0; cc < 10; cc++) { + snapshotId = vm.snapshot(); + vm.revertToAndDelete(snapshotId); + assert(!vm.revertTo(snapshotId)); + } + } + } + + // tests that snapshots can also revert changes to `block` + function testBlockValues() public { + uint256 num = block.number; + uint256 time = block.timestamp; + uint256 prevrandao = block.prevrandao; + + uint256 snapshotId = vm.snapshot(); + + vm.warp(1337); + assertEq(block.timestamp, 1337); + + vm.roll(99); + assertEq(block.number, 99); + + vm.prevrandao(uint256(123)); + assertEq(block.prevrandao, 123); + + assert(vm.revertTo(snapshotId)); + + assertEq(block.number, num, "snapshot revert for block.number unsuccessful"); + assertEq(block.timestamp, time, "snapshot revert for block.timestamp unsuccessful"); + assertEq(block.prevrandao, prevrandao, "snapshot revert for block.prevrandao unsuccessful"); + } +} diff --git a/testdata/default/cheats/loadAllocs.t.sol b/testdata/default/cheats/loadAllocs.t.sol index 358608860..a4b72af6b 100644 --- a/testdata/default/cheats/loadAllocs.t.sol +++ b/testdata/default/cheats/loadAllocs.t.sol @@ -16,7 +16,7 @@ contract LoadAllocsTest is DSTest { allocsPath = string.concat(vm.projectRoot(), "/fixtures/Json/test_allocs.json"); // Snapshot the state; We'll restore it in each test that loads allocs inline. - snapshotId = vm.snapshot(); + snapshotId = vm.snapshotState(); // Load the allocs file. vm.loadAllocs(allocsPath); @@ -40,7 +40,7 @@ contract LoadAllocsTest is DSTest { /// @dev Checks that the `loadAllocs` cheatcode persists account info if called inline function testLoadAllocsStatic() public { // Restore the state snapshot prior to the allocs file being loaded. - vm.revertTo(snapshotId); + vm.revertToState(snapshotId); // Load the allocs file vm.loadAllocs(allocsPath); @@ -61,7 +61,7 @@ contract LoadAllocsTest is DSTest { /// @dev Checks that the `loadAllocs` cheatcode overrides existing account information (if present) function testLoadAllocsOverride() public { // Restore the state snapshot prior to the allocs file being loaded. - vm.revertTo(snapshotId); + vm.revertToState(snapshotId); // Populate the alloc'd account's code. vm.etch(ALLOCD, hex"FF"); @@ -88,7 +88,7 @@ contract LoadAllocsTest is DSTest { /// within the allocs/genesis file for the account field (i.e., partial overrides) function testLoadAllocsPartialOverride() public { // Restore the state snapshot prior to the allocs file being loaded. - vm.revertTo(snapshotId); + vm.revertToState(snapshotId); // Populate the alloc'd account's code. vm.etch(ALLOCD_B, hex"FF"); diff --git a/testdata/default/repros/Issue2984.t.sol b/testdata/default/repros/Issue2984.t.sol index 1a181ad53..5f0203369 100644 --- a/testdata/default/repros/Issue2984.t.sol +++ b/testdata/default/repros/Issue2984.t.sol @@ -12,11 +12,11 @@ contract Issue2984Test is DSTest { function setUp() public { fork = vm.createSelectFork("avaxTestnet", 12880747); - snapshot = vm.snapshot(); + snapshot = vm.snapshotState(); } function testForkRevertSnapshot() public { - vm.revertTo(snapshot); + vm.revertToState(snapshot); } function testForkSelectSnapshot() public { diff --git a/testdata/default/repros/Issue3055.t.sol b/testdata/default/repros/Issue3055.t.sol index cacf5282f..72461a6c0 100644 --- a/testdata/default/repros/Issue3055.t.sol +++ b/testdata/default/repros/Issue3055.t.sol @@ -9,15 +9,15 @@ contract Issue3055Test is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); function test_snapshot() external { - uint256 snapId = vm.snapshot(); + uint256 snapshotId = vm.snapshotState(); assertEq(uint256(0), uint256(1)); - vm.revertTo(snapId); + vm.revertToState(snapshotId); } function test_snapshot2() public { - uint256 snapshot = vm.snapshot(); + uint256 snapshotId = vm.snapshotState(); assertTrue(false); - vm.revertTo(snapshot); + vm.revertToState(snapshotId); assertTrue(true); } @@ -29,8 +29,8 @@ contract Issue3055Test is DSTest { } function exposed_snapshot3() public { - uint256 snapshot = vm.snapshot(); + uint256 snapshotId = vm.snapshotState(); assertTrue(false); - vm.revertTo(snapshot); + vm.revertToState(snapshotId); } } diff --git a/testdata/default/repros/Issue3792.t.sol b/testdata/default/repros/Issue3792.t.sol index 723329f93..1adeb88af 100644 --- a/testdata/default/repros/Issue3792.t.sol +++ b/testdata/default/repros/Issue3792.t.sol @@ -16,10 +16,10 @@ contract TestSetup is Config, DSTest { // We now check for keccak256("failed") on the hevm address. // This test should succeed. function testSnapshotStorageShift() public { - uint256 snapshotId = vm.snapshot(); + uint256 snapshotId = vm.snapshotState(); vm.prank(test); - vm.revertTo(snapshotId); + vm.revertToState(snapshotId); } } diff --git a/testdata/default/repros/Issue6355.t.sol b/testdata/default/repros/Issue6355.t.sol index d7830152a..f002b2a87 100644 --- a/testdata/default/repros/Issue6355.t.sol +++ b/testdata/default/repros/Issue6355.t.sol @@ -7,11 +7,11 @@ import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/6355 contract Issue6355Test is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); - uint256 snapshot; + uint256 snapshotId; Target targ; function setUp() public { - snapshot = vm.snapshot(); + snapshotId = vm.snapshotState(); targ = new Target(); } @@ -21,9 +21,9 @@ contract Issue6355Test is DSTest { } // always fails - function test_shouldFailWithRevertTo() public { + function test_shouldFailWithRevertToState() public { assertEq(3, targ.num()); - vm.revertTo(snapshot); + vm.revertToState(snapshotId); } // always fails From 25f24e677a6a32a62512ad4f561995589ac2c7dc Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 27 Sep 2024 20:48:48 +0200 Subject: [PATCH 040/100] fix: 4844 fee fixes (#8963) * fix: use zero blob fee for estimate * add basic test * fix gas_price * support EIP-4844 with with_max_fee_per_blob_gas None * this should run succesfully once Alloy counterpart has been merged * undo max_fee_per_blob_gas != 0 check, not necessary anymore * clean up * fix setup bug from Matt * add test with signer, currently failing on Result::unwrap()` on an `Err` value: ErrorResp(ErrorPayload { code: -32003, message: "Block `blob_versioned_hashes` is not supported before the Cancun hardfork", data: None }) * able to reproduce * apply hotfix by Matt * remove debugs * use or_else, only need to do this if the blob_versioned hashes are non zero * move blob_hashes out --------- Co-authored-by: zerosnacks Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --- crates/anvil/core/src/eth/transaction/mod.rs | 2 +- crates/anvil/src/config.rs | 3 +- crates/anvil/src/eth/backend/mem/mod.rs | 50 ++++++----- crates/anvil/src/eth/fees.rs | 2 +- crates/anvil/tests/it/eip4844.rs | 93 +++++++++++++++++++- 5 files changed, 124 insertions(+), 26 deletions(-) diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 25e42e001..e03a862b8 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -128,7 +128,7 @@ pub fn transaction_request_to_typed( })) } // EIP4844 - (Some(3), None, _, _, _, Some(_), Some(_), Some(sidecar), to) => { + (Some(3), None, _, _, _, _, Some(_), Some(sidecar), to) => { let tx = TxEip4844 { nonce: nonce.unwrap_or_default(), max_fee_per_gas: max_fee_per_gas.unwrap_or_default(), diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 96f4c53d5..d907fc200 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -493,7 +493,8 @@ impl NodeConfig { { BlobExcessGasAndPrice::new(excess_blob_gas as u64) } else { - BlobExcessGasAndPrice { blob_gasprice: 0, excess_blob_gas: 0 } + // If no excess blob gas is configured, default to 0 + BlobExcessGasAndPrice::new(0) } } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index cd23fa6d3..eed634bfa 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -1227,26 +1227,36 @@ impl Backend { }); let caller = from.unwrap_or_default(); let to = to.as_ref().and_then(TxKind::to); - env.tx = TxEnv { - caller, - gas_limit: gas_limit as u64, - gas_price: U256::from(gas_price), - gas_priority_fee: max_priority_fee_per_gas.map(U256::from), - max_fee_per_blob_gas: max_fee_per_blob_gas.map(U256::from), - transact_to: match to { - Some(addr) => TxKind::Call(*addr), - None => TxKind::Create, - }, - value: value.unwrap_or_default(), - data: input.into_input().unwrap_or_default(), - chain_id: None, - // 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() }, - authorization_list: authorization_list.map(Into::into), - }; + let blob_hashes = blob_versioned_hashes.unwrap_or_default(); + env.tx = + TxEnv { + caller, + gas_limit: gas_limit as u64, + gas_price: U256::from(gas_price), + gas_priority_fee: max_priority_fee_per_gas.map(U256::from), + max_fee_per_blob_gas: max_fee_per_blob_gas + .or_else(|| { + if !blob_hashes.is_empty() { + env.block.get_blob_gasprice() + } else { + None + } + }) + .map(U256::from), + transact_to: match to { + Some(addr) => TxKind::Call(*addr), + None => TxKind::Create, + }, + value: value.unwrap_or_default(), + data: input.into_input().unwrap_or_default(), + chain_id: None, + // 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, + optimism: OptimismFields { enveloped_tx: Some(Bytes::new()), ..Default::default() }, + authorization_list: authorization_list.map(Into::into), + }; if env.block.basefee.is_zero() { // this is an edge case because the evm fails if `tx.effective_gas_price < base_fee` diff --git a/crates/anvil/src/eth/fees.rs b/crates/anvil/src/eth/fees.rs index 45b33ad0f..cb1b8508f 100644 --- a/crates/anvil/src/eth/fees.rs +++ b/crates/anvil/src/eth/fees.rs @@ -464,7 +464,7 @@ impl FeeDetails { impl fmt::Debug for FeeDetails { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "Fees {{ ")?; - write!(fmt, "gaPrice: {:?}, ", self.gas_price)?; + write!(fmt, "gas_price: {:?}, ", self.gas_price)?; write!(fmt, "max_fee_per_gas: {:?}, ", self.max_fee_per_gas)?; write!(fmt, "max_priority_fee_per_gas: {:?}, ", self.max_priority_fee_per_gas)?; write!(fmt, "}}")?; diff --git a/crates/anvil/tests/it/eip4844.rs b/crates/anvil/tests/it/eip4844.rs index 353083409..71a8bfd84 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 crate::utils::{http_provider, http_provider_with_signer}; use alloy_consensus::{SidecarBuilder, SimpleCoder}; -use alloy_eips::eip4844::{DATA_GAS_PER_BLOB, MAX_DATA_GAS_PER_BLOCK}; -use alloy_network::{TransactionBuilder, TransactionBuilder4844}; +use alloy_eips::eip4844::{BLOB_TX_MIN_BLOB_GASPRICE, DATA_GAS_PER_BLOB, MAX_DATA_GAS_PER_BLOCK}; +use alloy_network::{EthereumWallet, TransactionBuilder, TransactionBuilder4844}; use alloy_primitives::U256; use alloy_provider::Provider; use alloy_rpc_types::{BlockId, TransactionRequest}; @@ -204,3 +204,90 @@ async fn can_check_blob_fields_on_genesis() { assert_eq!(block.header.blob_gas_used, Some(0)); assert_eq!(block.header.excess_blob_gas, Some(0)); } + +#[tokio::test(flavor = "multi_thread")] +async fn can_correctly_estimate_blob_gas_with_recommended_fillers() { + 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()); + + let accounts = provider.get_accounts().await.unwrap(); + let alice = accounts[0]; + let bob = accounts[1]; + + let sidecar: SidecarBuilder = SidecarBuilder::from_slice(b"Blobs are fun!"); + let sidecar = sidecar.build().unwrap(); + + let tx = TransactionRequest::default().with_to(bob).with_blob_sidecar(sidecar); + let tx = WithOtherFields::new(tx); + + // Send the transaction and wait for the broadcast. + let pending_tx = provider.send_transaction(tx).await.unwrap(); + + println!("Pending transaction... {}", pending_tx.tx_hash()); + + // Wait for the transaction to be included and get the receipt. + let receipt = pending_tx.get_receipt().await.unwrap(); + + // Grab the processed transaction. + let tx = provider.get_transaction_by_hash(receipt.transaction_hash).await.unwrap().unwrap(); + + println!( + "Transaction included in block {}", + receipt.block_number.expect("Failed to get block number") + ); + + assert!(tx.max_fee_per_blob_gas.unwrap() >= BLOB_TX_MIN_BLOB_GASPRICE); + assert_eq!(receipt.from, alice); + assert_eq!(receipt.to, Some(bob)); + assert_eq!( + receipt.blob_gas_used.expect("Expected to be EIP-4844 transaction"), + DATA_GAS_PER_BLOB as u128 + ); +} + +#[tokio::test(flavor = "multi_thread")] +async fn can_correctly_estimate_blob_gas_with_recommended_fillers_with_signer() { + let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Cancun.into())); + let (_api, handle) = spawn(node_config).await; + + let signer = handle.dev_wallets().next().unwrap(); + let wallet: EthereumWallet = signer.clone().into(); + + let provider = http_provider_with_signer(&handle.http_endpoint(), wallet); + + let accounts = provider.get_accounts().await.unwrap(); + let alice = accounts[0]; + let bob = accounts[1]; + + let sidecar: SidecarBuilder = SidecarBuilder::from_slice(b"Blobs are fun!"); + let sidecar = sidecar.build().unwrap(); + + let tx = TransactionRequest::default().with_to(bob).with_blob_sidecar(sidecar); + let tx = WithOtherFields::new(tx); + + // Send the transaction and wait for the broadcast. + let pending_tx = provider.send_transaction(tx).await.unwrap(); + + println!("Pending transaction... {}", pending_tx.tx_hash()); + + // Wait for the transaction to be included and get the receipt. + let receipt = pending_tx.get_receipt().await.unwrap(); + + // Grab the processed transaction. + let tx = provider.get_transaction_by_hash(receipt.transaction_hash).await.unwrap().unwrap(); + + println!( + "Transaction included in block {}", + receipt.block_number.expect("Failed to get block number") + ); + + assert!(tx.max_fee_per_blob_gas.unwrap() >= BLOB_TX_MIN_BLOB_GASPRICE); + assert_eq!(receipt.from, alice); + assert_eq!(receipt.to, Some(bob)); + assert_eq!( + receipt.blob_gas_used.expect("Expected to be EIP-4844 transaction"), + DATA_GAS_PER_BLOB as u128 + ); +} From 7559c09974e6166f945ff1b3136a811dfcc9d5da Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 03:33:06 +0200 Subject: [PATCH 041/100] chore(deps): weekly `cargo update` (#8981) * chore(deps): weekly `cargo update` Locking 41 packages to latest compatible versions Updating alloy-chains v0.1.33 -> v0.1.34 Updating alloy-dyn-abi v0.8.3 -> v0.8.5 Updating alloy-json-abi v0.8.3 -> v0.8.5 Updating alloy-primitives v0.8.3 -> v0.8.5 Updating alloy-sol-macro v0.8.3 -> v0.8.5 Updating alloy-sol-macro-expander v0.8.3 -> v0.8.5 Updating alloy-sol-macro-input v0.8.3 -> v0.8.5 Updating alloy-sol-type-parser v0.8.3 -> v0.8.5 Updating alloy-sol-types v0.8.3 -> v0.8.5 Updating async-trait v0.1.82 -> v0.1.83 Updating autocfg v1.3.0 -> v1.4.0 Updating aws-config v1.5.6 -> v1.5.7 Updating aws-sdk-kms v1.44.0 -> v1.45.0 Updating aws-sdk-sso v1.43.0 -> v1.44.0 Updating aws-sdk-ssooidc v1.44.0 -> v1.45.0 Updating aws-sdk-sts v1.43.0 -> v1.44.0 Updating aws-smithy-types v1.2.6 -> v1.2.7 Updating axum v0.7.6 -> v0.7.7 Updating axum-core v0.4.4 -> v0.4.5 Updating cc v1.1.21 -> v1.1.22 Updating evmole v0.5.0 -> v0.5.1 Updating flate2 v1.0.33 -> v1.0.34 Updating hyper-util v0.1.8 -> v0.1.9 Updating libc v0.2.158 -> v0.2.159 Updating portable-atomic v1.8.0 -> v1.9.0 Updating redox_syscall v0.5.4 -> v0.5.6 Updating revm v14.0.2 -> v14.0.3 Updating revm-interpreter v10.0.2 -> v10.0.3 Updating revm-precompile v11.0.2 -> v11.0.3 Adding revm-primitives v10.0.0 Updating rustls-pki-types v1.8.0 -> v1.9.0 Updating serde_spanned v0.6.7 -> v0.6.8 Updating syn v2.0.77 -> v2.0.79 Updating syn-solidity v0.8.3 -> v0.8.5 Updating tempfile v3.12.0 -> v3.13.0 Adding tokio-tungstenite v0.24.0 Updating toml_edit v0.22.21 -> v0.22.22 Updating tonic v0.12.2 -> v0.12.3 Adding tungstenite v0.24.0 Updating wasm-streams v0.4.0 -> v0.4.1 Updating winnow v0.6.18 -> v0.6.20 note: pass `--verbose` to see 143 unchanged dependencies behind latest * fixes * chore: clippy --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- Cargo.lock | 383 ++++++++++-------- Cargo.toml | 12 +- crates/anvil/src/eth/api.rs | 18 +- crates/anvil/src/eth/backend/db.rs | 8 +- crates/anvil/src/eth/backend/mem/mod.rs | 15 +- crates/chisel/src/session_source.rs | 2 +- crates/config/src/providers/remappings.rs | 4 +- crates/evm/core/src/backend/snapshot.rs | 6 +- crates/evm/core/src/fork/database.rs | 6 +- crates/evm/core/src/state_snapshot.rs | 2 +- crates/evm/evm/src/executors/mod.rs | 2 +- crates/evm/fuzz/src/strategies/calldata.rs | 2 +- .../evm/traces/src/identifier/signatures.rs | 4 +- crates/forge/src/coverage.rs | 2 +- crates/forge/src/runner.rs | 12 +- crates/linking/src/lib.rs | 2 +- crates/script/src/execute.rs | 2 +- crates/script/src/lib.rs | 2 +- crates/script/src/simulate.rs | 2 +- 19 files changed, 267 insertions(+), 219 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5a9f4a3b2..886cc3ff2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,9 +74,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-chains" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "805f7a974de5804f5c053edc6ca43b20883bdd3a733b3691200ae3a4b454a2db" +checksum = "8158b4878c67837e5413721cc44298e6a2d88d39203175ea025e51892a16ba4c" dependencies = [ "num_enum", "serde", @@ -120,9 +120,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4004925bff5ba0a11739ae84dbb6601a981ea692f3bd45b626935ee90a6b8471" +checksum = "0b499852e1d0e9b8c6db0f24c48998e647c0d5762a01090f955106a7700e4611" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -177,7 +177,6 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "alloy-serde", - "arbitrary", "c-kzg", "derive_more 1.0.0", "once_cell", @@ -198,9 +197,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9996daf962fd0a90d3c93b388033228865953b92de7bb1959b891d78750a4091" +checksum = "a438d4486b5d525df3b3004188f9d5cd1d65cd30ecc41e5a3ccef6f6342e8af9" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -257,9 +256,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "411aff151f2a73124ee473708e82ed51b2535f68928b6a1caa8bc1246ae6f7cd" +checksum = "260d3ff3bff0bb84599f032a2f2c6828180b0ea0cd41fdaf44f39cef3ba41861" dependencies = [ "alloy-rlp", "arbitrary", @@ -269,15 +268,20 @@ dependencies = [ "derive_arbitrary", "derive_more 1.0.0", "getrandom", + "hashbrown 0.14.5", "hex-literal", + "indexmap 2.5.0", "itoa", "k256", "keccak-asm", + "paste", "proptest", "proptest-derive", "rand", "ruint", + "rustc-hash", "serde", + "sha3", "tiny-keccak", ] @@ -358,7 +362,7 @@ checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -483,7 +487,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "731f75ec5d383107fd745d781619bd9cedf145836c51ecb991623d41278e71fa" dependencies = [ "alloy-primitives", - "arbitrary", "serde", "serde_json", ] @@ -599,23 +602,23 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0458ccb02a564228fcd76efb8eb5a520521a8347becde37b402afec9a1b83859" +checksum = "68e7f6e8fe5b443f82b3f1e15abfa191128f71569148428e49449d01f6f49e8b" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] name = "alloy-sol-macro-expander" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc65475025fc1e84bf86fc840f04f63fcccdcf3cf12053c99918e4054dfbc69" +checksum = "6b96ce28d2fde09abb6135f410c41fad670a3a770b6776869bd852f1df102e6f" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -625,16 +628,16 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "syn-solidity", "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed10f0715a0b69fde3236ff3b9ae5f6f7c97db5a387747100070d3016b9266b" +checksum = "906746396a8296537745711630d9185746c0b50c033d5e9d18b0a6eba3d53f90" dependencies = [ "alloy-json-abi", "const-hex", @@ -643,15 +646,15 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.77", + "syn 2.0.79", "syn-solidity", ] [[package]] name = "alloy-sol-type-parser" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3edae8ea1de519ccba896b6834dec874230f72fe695ff3c9c118e90ec7cff783" +checksum = "bc85178909a49c8827ffccfc9103a7ce1767ae66a801b69bdc326913870bf8e6" dependencies = [ "serde", "winnow", @@ -659,9 +662,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1eb88e4da0a1b697ed6a9f811fdba223cf4d5c21410804fd1707836af73a462b" +checksum = "d86a533ce22525969661b25dfe296c112d35eb6861f188fd284f8bd4bb3842ae" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -738,21 +741,20 @@ dependencies = [ "rustls 0.23.13", "serde_json", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.23.1", "tracing", "ws_stream_wasm", ] [[package]] name = "alloy-trie" -version = "0.5.3" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a46c9c4fdccda7982e7928904bd85fe235a0404ee3d7e197fff13d61eac8b4f" +checksum = "e9703ce68b97f8faae6f7739d1e003fc97621b856953cbcdbb2b515743f23288" dependencies = [ "alloy-primitives", "alloy-rlp", "derive_more 1.0.0", - "hashbrown 0.14.5", "nybbles", "serde", "smallvec", @@ -1155,7 +1157,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1177,18 +1179,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] name = "async-trait" -version = "0.1.82" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1241,20 +1243,20 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-config" -version = "1.5.6" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "848d7b9b605720989929279fa644ce8f244d0ce3146fcca5b70e4eb7b3c020fc" +checksum = "8191fb3091fa0561d1379ef80333c3c7191c6f0435d986e85821bcf7acbd1126" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1319,9 +1321,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.44.0" +version = "1.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6550445e0913c9383375f4a5a2f550817567a19a178107fce1e1afd767f802a" +checksum = "0caf20b8855dbeb458552e6c8f8f9eb92b95e4a131725b93540ec73d60c38eb3" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1341,9 +1343,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.43.0" +version = "1.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a9d27ed1c12b1140c47daf1bc541606c43fdafd918c4797d520db0043ceef2" +checksum = "0b90cfe6504115e13c41d3ea90286ede5aa14da294f3fe077027a6e83850843c" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1363,9 +1365,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.44.0" +version = "1.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44514a6ca967686cde1e2a1b81df6ef1883d0e3e570da8d8bc5c491dcb6fc29b" +checksum = "167c0fad1f212952084137308359e8e4c4724d1c643038ce163f06de9662c1d0" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1385,9 +1387,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.43.0" +version = "1.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7a4d279762a35b9df97209f6808b95d4fe78547fe2316b4d200a0283960c5a" +checksum = "2cb5f98188ec1435b68097daa2a37d74b9d17c9caa799466338a8d1544e71b9d" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1525,9 +1527,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.2.6" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03701449087215b5369c7ea17fef0dd5d24cb93439ec5af0c7615f58c3f22605" +checksum = "147100a7bea70fa20ef224a6bad700358305f5dc0f84649c53769761395b355b" dependencies = [ "base64-simd", "bytes", @@ -1571,13 +1573,13 @@ dependencies = [ [[package]] name = "axum" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec" +checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" dependencies = [ "async-trait", "axum-core", - "base64 0.21.7", + "base64 0.22.1", "bytes", "futures-util", "http 1.1.0", @@ -1599,7 +1601,7 @@ dependencies = [ "sha1", "sync_wrapper 1.0.1", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.24.0", "tower 0.5.1", "tower-layer", "tower-service", @@ -1608,9 +1610,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6b8ba012a258d63c9adfa28b9ddcf66149da6f986c5b5452e629d5ee64bf00" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" dependencies = [ "async-trait", "bytes", @@ -1770,7 +1772,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", - "regex-automata 0.4.7", + "regex-automata 0.4.8", "serde", ] @@ -1914,9 +1916,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.21" +version = "1.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" +checksum = "9540e661f81799159abee814118cc139a2004b3a3aa3ea37724a1b66530b90e0" dependencies = [ "shlex", ] @@ -2080,7 +2082,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2278,9 +2280,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.12.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6" +checksum = "0121754e84117e65f9d90648ee6aa4882a6e63110307ab73967a4c5e7e69e586" dependencies = [ "cfg-if", "cpufeatures", @@ -2516,7 +2518,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2527,7 +2529,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2600,7 +2602,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2621,7 +2623,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2631,7 +2633,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4abae7035bf79b9877b779505d8cf3749285b80c43941eda66604841889451dc" dependencies = [ "derive_builder_core", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2642,7 +2644,7 @@ checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2663,7 +2665,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "unicode-xid", ] @@ -2771,7 +2773,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2898,7 +2900,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -3033,7 +3035,7 @@ dependencies = [ "regex", "serde", "serde_json", - "syn 2.0.77", + "syn 2.0.79", "toml 0.8.19", "walkdir", ] @@ -3061,7 +3063,7 @@ dependencies = [ "serde", "serde_json", "strum", - "syn 2.0.77", + "syn 2.0.79", "tempfile", "thiserror", "tiny-keccak", @@ -3091,9 +3093,9 @@ dependencies = [ [[package]] name = "evmole" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc8472e812ff8f53a76946fa70b1cc4bf75c78755beb09df4d1376764769c7d" +checksum = "b6fcfb15a14bc209e2b3d2bd32291ec445b1e348d7d9d986aa61a09149350fd7" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -3211,9 +3213,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.33" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", "miniz_oxide 0.8.0", @@ -3427,7 +3429,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -4016,9 +4018,9 @@ dependencies = [ [[package]] name = "foundry-fork-db" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ab0b0379d1838fa1f0da521ffbbac168aaf113498ea8d6c9650195cadfa736" +checksum = "2fb76083f203e341deeb4a03f9cc86df096f09c723c42e44c25052c233ed87b4" dependencies = [ "alloy-primitives", "alloy-provider", @@ -4029,7 +4031,6 @@ dependencies = [ "futures", "parking_lot", "revm", - "rustc-hash", "serde", "serde_json", "thiserror", @@ -4055,7 +4056,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -4214,7 +4215,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -4562,8 +4563,8 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -4730,7 +4731,7 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -4934,9 +4935,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" +checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" dependencies = [ "bytes", "futures-channel", @@ -4947,7 +4948,6 @@ dependencies = [ "pin-project-lite", "socket2", "tokio", - "tower 0.4.13", "tower-service", "tracing", ] @@ -5001,7 +5001,7 @@ dependencies = [ "globset", "log", "memchr", - "regex-automata 0.4.7", + "regex-automata 0.4.8", "same-file", "walkdir", "winapi-util", @@ -5086,8 +5086,10 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ + "arbitrary", "equivalent", "hashbrown 0.14.5", + "serde", ] [[package]] @@ -5162,7 +5164,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c" dependencies = [ "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -5346,7 +5348,7 @@ dependencies = [ "lalrpop-util", "petgraph", "regex", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", "string_cache", "term", "tiny-keccak", @@ -5360,7 +5362,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" dependencies = [ - "regex-automata 0.4.7", + "regex-automata 0.4.8", ] [[package]] @@ -5374,9 +5376,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.158" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libdbus-sys" @@ -5601,7 +5603,7 @@ checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -5692,7 +5694,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -5951,7 +5953,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -5993,9 +5995,12 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" +dependencies = [ + "portable-atomic", +] [[package]] name = "oorandom" @@ -6097,7 +6102,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -6260,7 +6265,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -6319,7 +6324,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -6403,7 +6408,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -6461,7 +6466,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -6522,9 +6527,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d30538d42559de6b034bc76fd6dd4c38961b1ee5c6c56e3808c50128fdbc22ce" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" [[package]] name = "powerfmt" @@ -6580,7 +6585,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -6658,7 +6663,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -6678,7 +6683,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "version_check", "yansi", ] @@ -6728,7 +6733,7 @@ dependencies = [ "rand", "rand_chacha", "rand_xorshift", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", "rusty-fork", "tempfile", "unarray", @@ -6742,7 +6747,7 @@ checksum = "6ff7ff745a347b87471d859a377a9a404361e7efc2a971d73424a6d183c0fc77" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -6765,7 +6770,7 @@ dependencies = [ "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -6945,6 +6950,7 @@ dependencies = [ "libc", "rand_chacha", "rand_core", + "serde", ] [[package]] @@ -7024,9 +7030,9 @@ checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" [[package]] name = "redox_syscall" -version = "0.5.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" +checksum = "355ae415ccd3a04315d3f8246e86d67689ea74d88d915576e1589a351062a13b" dependencies = [ "bitflags 2.6.0", ] @@ -7044,14 +7050,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -7065,13 +7071,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", ] [[package]] @@ -7088,9 +7094,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" @@ -7146,9 +7152,9 @@ dependencies = [ [[package]] name = "revm" -version = "14.0.2" +version = "14.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9f3f55d0414c3d73902d876ba3d55a654f05fe937089fbf5f34b1ced26d78d5" +checksum = "641702b12847f9ed418d552f4fcabe536d867a2c980e96b6e7e25d7b992f929f" dependencies = [ "auto_impl", "cfg-if", @@ -7179,9 +7185,9 @@ dependencies = [ [[package]] name = "revm-interpreter" -version = "10.0.2" +version = "10.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713dbb271acd13afb06dcd460c1dc43da211e7ac9bc73cdf13528f615f55f96b" +checksum = "2e5e14002afae20b5bf1566f22316122f42f57517000e559c55b25bf7a49cba2" dependencies = [ "revm-primitives", "serde", @@ -7189,9 +7195,9 @@ dependencies = [ [[package]] name = "revm-precompile" -version = "11.0.2" +version = "11.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73010c271d53fa7904e9845338e95f3955eb1200a0355e0abfdb89c41aaa9cd" +checksum = "3198c06247e8d4ad0d1312591edf049b0de4ddffa9fecb625c318fd67db8639b" dependencies = [ "aurora-engine-modexp", "blst", @@ -7209,11 +7215,12 @@ dependencies = [ [[package]] name = "revm-primitives" -version = "9.0.2" +version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a6bff9dbde3370a5ac9555104117f7e6039b3cc76e8d5d9d01899088beca2a" +checksum = "6f1525851a03aff9a9d6a1d018b414d76252d6802ab54695b27093ecd7e7a101" dependencies = [ - "alloy-eips", + "alloy-eip2930", + "alloy-eip7702", "alloy-primitives", "auto_impl", "bitflags 2.6.0", @@ -7222,7 +7229,6 @@ dependencies = [ "cfg-if", "dyn-clone", "enumn", - "hashbrown 0.14.5", "hex", "serde", ] @@ -7365,6 +7371,9 @@ name = "rustc-hash" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +dependencies = [ + "rand", +] [[package]] name = "rustc-hex" @@ -7489,9 +7498,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" [[package]] name = "rustls-webpki" @@ -7651,7 +7660,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -7813,7 +7822,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -7824,7 +7833,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -7868,14 +7877,14 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] name = "serde_spanned" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -7914,7 +7923,7 @@ checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -8256,7 +8265,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -8324,9 +8333,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -8335,14 +8344,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b95156f8b577cb59dc0b1df15c6f29a10afc5f8a7ac9786b0b5c68c19149278" +checksum = "0ab661c8148c2261222a4d641ad5477fd4bea79406a99056096a0b41b35617a5" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -8368,9 +8377,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand", @@ -8447,7 +8456,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -8582,7 +8591,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -8652,10 +8661,22 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", - "tungstenite", + "tungstenite 0.23.0", "webpki-roots", ] +[[package]] +name = "tokio-tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.24.0", +] + [[package]] name = "tokio-util" version = "0.7.12" @@ -8702,9 +8723,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.21" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap 2.5.0", "serde", @@ -8715,9 +8736,9 @@ dependencies = [ [[package]] name = "tonic" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f6ba989e4b2c58ae83d862d3a3e27690b6e3ae630d0deb59f3697f32aa88ad" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" dependencies = [ "async-stream", "async-trait", @@ -8734,7 +8755,7 @@ dependencies = [ "percent-encoding", "pin-project 1.1.5", "prost", - "rustls-native-certs 0.7.3", + "rustls-native-certs 0.8.0", "rustls-pemfile 2.1.3", "socket2", "tokio", @@ -8857,7 +8878,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -8980,6 +9001,24 @@ dependencies = [ "utf-8", ] +[[package]] +name = "tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.1.0", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "utf-8", +] + [[package]] name = "typenum" version = "1.17.0" @@ -8988,9 +9027,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "uint" @@ -9251,7 +9290,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-shared", ] @@ -9285,7 +9324,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -9298,9 +9337,9 @@ checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "wasm-streams" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" dependencies = [ "futures-util", "js-sys", @@ -9502,7 +9541,7 @@ checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -9513,7 +9552,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -9524,7 +9563,7 @@ checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -9535,7 +9574,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -9727,9 +9766,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] @@ -9801,7 +9840,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -9821,7 +9860,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 99f079a75..245c6da41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -168,12 +168,12 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.7.3", default-features = false } foundry-compilers = { version = "0.11.1", default-features = false } -foundry-fork-db = "0.3" +foundry-fork-db = "0.3.2" solang-parser = "=0.3.3" ## revm revm = { version = "14.0.2", default-features = false } -revm-primitives = { version = "9.0.2", default-features = false } +revm-primitives = { version = "10.0.0", default-features = false } revm-inspectors = { version = "0.7.7", features = ["serde"] } ## ethers @@ -205,7 +205,11 @@ alloy-transport-ws = { version = "0.3.6", default-features = false } ## alloy-core alloy-dyn-abi = "0.8.1" alloy-json-abi = "0.8.1" -alloy-primitives = { version = "0.8.1", features = ["getrandom", "rand"] } +alloy-primitives = { version = "0.8.1", features = [ + "getrandom", + "rand", + "map-fxhash", +] } alloy-sol-macro-expander = "0.8.1" alloy-sol-macro-input = "0.8.1" alloy-sol-types = "0.8.1" @@ -213,7 +217,7 @@ syn-solidity = "0.8.1" alloy-chains = "0.1" alloy-rlp = "0.3" -alloy-trie = "0.5.0" +alloy-trie = "0.6.0" ## op-alloy for tests in anvil op-alloy-rpc-types = "0.2.9" diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 1f005d443..85d1aceaf 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -35,7 +35,10 @@ use alloy_consensus::{transaction::eip4844::TxEip4844Variant, Account, TxEnvelop use alloy_dyn_abi::TypedData; use alloy_eips::eip2718::Encodable2718; use alloy_network::{eip2718::Decodable2718, BlockResponse}; -use alloy_primitives::{Address, Bytes, Parity, TxHash, TxKind, B256, B64, U256, U64}; +use alloy_primitives::{ + map::{HashMap, HashSet}, + Address, Bytes, Parity, TxHash, TxKind, B256, B64, U256, U64, +}; use alloy_rpc_types::{ anvil::{ ForkedNetwork, Forking, Metadata, MineOptions, NodeEnvironment, NodeForkConfig, NodeInfo, @@ -79,12 +82,7 @@ use foundry_evm::{ }; use futures::channel::{mpsc::Receiver, oneshot}; use parking_lot::RwLock; -use std::{ - collections::{HashMap, HashSet}, - future::Future, - sync::Arc, - time::Duration, -}; +use std::{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")); @@ -1968,7 +1966,7 @@ impl EthApi { // 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() + HashMap::default() } else { let mut pairs = tx_block_pairs; @@ -1985,9 +1983,9 @@ impl EthApi { // Manage nonces for each signer // address -> cumulative nonce - let mut nonces: HashMap = HashMap::new(); + let mut nonces: HashMap = HashMap::default(); - let mut txs: HashMap>> = HashMap::new(); + let mut txs: HashMap>> = HashMap::default(); for pair in pairs { let (tx_data, block_index) = pair; diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index 0aa0f8d13..67ab27b9b 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -253,8 +253,8 @@ impl> MaybeFullDatabase for CacheDB { fn clear_into_state_snapshot(&mut self) -> StateSnapshot { let db_accounts = std::mem::take(&mut self.accounts); - let mut accounts = HashMap::new(); - let mut account_storage = HashMap::new(); + let mut accounts = HashMap::default(); + let mut account_storage = HashMap::default(); for (addr, mut acc) in db_accounts { account_storage.insert(addr, std::mem::take(&mut acc.storage)); @@ -268,8 +268,8 @@ impl> MaybeFullDatabase for CacheDB { fn read_as_state_snapshot(&self) -> StateSnapshot { let db_accounts = self.accounts.clone(); - let mut accounts = HashMap::new(); - let mut account_storage = HashMap::new(); + let mut accounts = HashMap::default(); + let mut account_storage = HashMap::default(); for (addr, acc) in db_accounts { account_storage.insert(addr, acc.storage.clone()); diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index eed634bfa..83d8c1871 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -2456,7 +2456,12 @@ impl Backend { let _ = builder.root(); - let proof = builder.take_proofs().values().cloned().collect::>(); + let proof = builder + .take_proof_nodes() + .into_nodes_sorted() + .into_iter() + .map(|(_, v)| v) + .collect(); let storage_proofs = prove_storage(&account.storage, &keys); let account_proof = AccountProof { @@ -2825,15 +2830,13 @@ pub fn prove_storage(storage: &HashMap, keys: &[B256]) -> Vec Result> { - let mut res_map = HashMap::new(); + let mut res_map = HashMap::default(); let parsed_map = self.compiler_input().sources; for source in parsed_map.values() { Self::get_intermediate_contract(&source.content, &mut res_map); diff --git a/crates/config/src/providers/remappings.rs b/crates/config/src/providers/remappings.rs index 171967934..2eaab0982 100644 --- a/crates/config/src/providers/remappings.rs +++ b/crates/config/src/providers/remappings.rs @@ -39,9 +39,9 @@ impl Remappings { /// Consumes the wrapper and returns the inner remappings vector. pub fn into_inner(self) -> Vec { - let mut tmp = HashSet::new(); + let mut seen = HashSet::new(); let remappings = - self.remappings.iter().filter(|r| tmp.insert(Self::filter_key(r))).cloned().collect(); + self.remappings.iter().filter(|r| seen.insert(Self::filter_key(r))).cloned().collect(); remappings } diff --git a/crates/evm/core/src/backend/snapshot.rs b/crates/evm/core/src/backend/snapshot.rs index 7bfed1283..36c4657c2 100644 --- a/crates/evm/core/src/backend/snapshot.rs +++ b/crates/evm/core/src/backend/snapshot.rs @@ -1,4 +1,4 @@ -use alloy_primitives::{Address, B256, U256}; +use alloy_primitives::{map::AddressHashMap, B256, U256}; use revm::{ primitives::{AccountInfo, Env, HashMap}, JournaledState, @@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize}; /// A minimal abstraction of a state at a certain point in time #[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct StateSnapshot { - pub accounts: HashMap, - pub storage: HashMap>, + pub accounts: AddressHashMap, + pub storage: AddressHashMap>, pub block_hashes: HashMap, } diff --git a/crates/evm/core/src/fork/database.rs b/crates/evm/core/src/fork/database.rs index a8d2ccc1d..1bab76515 100644 --- a/crates/evm/core/src/fork/database.rs +++ b/crates/evm/core/src/fork/database.rs @@ -4,13 +4,13 @@ use crate::{ backend::{RevertStateSnapshotAction, StateSnapshot}, state_snapshot::StateSnapshots, }; -use alloy_primitives::{Address, B256, U256}; +use alloy_primitives::{map::HashMap, Address, B256, U256}; use alloy_rpc_types::BlockId; use foundry_fork_db::{BlockchainDb, DatabaseError, SharedBackend}; use parking_lot::Mutex; use revm::{ db::{CacheDB, DatabaseRef}, - primitives::{Account, AccountInfo, Bytecode, HashMap as Map}, + primitives::{Account, AccountInfo, Bytecode}, Database, DatabaseCommit, }; use std::sync::Arc; @@ -193,7 +193,7 @@ impl DatabaseRef for ForkedDatabase { } impl DatabaseCommit for ForkedDatabase { - fn commit(&mut self, changes: Map) { + fn commit(&mut self, changes: HashMap) { self.database_mut().commit(changes) } } diff --git a/crates/evm/core/src/state_snapshot.rs b/crates/evm/core/src/state_snapshot.rs index e273a5733..0f453b8c6 100644 --- a/crates/evm/core/src/state_snapshot.rs +++ b/crates/evm/core/src/state_snapshot.rs @@ -69,6 +69,6 @@ impl StateSnapshots { impl Default for StateSnapshots { fn default() -> Self { - Self { id: U256::ZERO, state_snapshots: HashMap::new() } + Self { id: U256::ZERO, state_snapshots: HashMap::default() } } } diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 7a1a794f6..421880c0d 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -753,7 +753,7 @@ impl Default for RawCallResult { gas_refunded: 0, stipend: 0, logs: Vec::new(), - labels: HashMap::new(), + labels: HashMap::default(), traces: None, coverage: None, transactions: None, diff --git a/crates/evm/fuzz/src/strategies/calldata.rs b/crates/evm/fuzz/src/strategies/calldata.rs index c341cce9d..b919c477f 100644 --- a/crates/evm/fuzz/src/strategies/calldata.rs +++ b/crates/evm/fuzz/src/strategies/calldata.rs @@ -74,7 +74,7 @@ mod tests { let function = Function::parse("test_fuzzed_address(address addressFixture)").unwrap(); let address_fixture = DynSolValue::Address(Address::random()); - let mut fixtures = HashMap::new(); + let mut fixtures = HashMap::default(); fixtures.insert( "addressFixture".to_string(), DynSolValue::Array(vec![address_fixture.clone()]), diff --git a/crates/evm/traces/src/identifier/signatures.rs b/crates/evm/traces/src/identifier/signatures.rs index 498385ef1..2d7cadad1 100644 --- a/crates/evm/traces/src/identifier/signatures.rs +++ b/crates/evm/traces/src/identifier/signatures.rs @@ -56,12 +56,12 @@ impl SignaturesIdentifier { } CachedSignatures::default() }; - Self { cached, cached_path: Some(path), unavailable: HashSet::new(), client } + Self { cached, cached_path: Some(path), unavailable: HashSet::default(), client } } else { Self { cached: Default::default(), cached_path: None, - unavailable: HashSet::new(), + unavailable: HashSet::default(), client, } }; diff --git a/crates/forge/src/coverage.rs b/crates/forge/src/coverage.rs index 135636d3c..b712c8fc2 100644 --- a/crates/forge/src/coverage.rs +++ b/crates/forge/src/coverage.rs @@ -268,7 +268,7 @@ struct LineNumberCache { impl LineNumberCache { pub fn new(root: PathBuf) -> Self { - Self { root, line_offsets: HashMap::new() } + Self { root, line_offsets: HashMap::default() } } pub fn get_position(&mut self, path: &Path, offset: usize) -> eyre::Result<(usize, usize)> { diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 51f2e5cc1..8f9f7ebdd 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -164,9 +164,13 @@ impl<'a> ContractRunner<'a> { } = *err; (logs, traces, labels, Some(format!("setup failed: {reason}")), coverage) } - Err(err) => { - (Vec::new(), None, HashMap::new(), Some(format!("setup failed: {err}")), None) - } + Err(err) => ( + Vec::new(), + None, + HashMap::default(), + Some(format!("setup failed: {err}")), + None, + ), }; traces.extend(setup_traces.map(|traces| (TraceKind::Setup, traces))); logs.extend(setup_logs); @@ -210,7 +214,7 @@ impl<'a> ContractRunner<'a> { /// returns an array of addresses to be used for fuzzing `owner` named parameter in scope of the /// current test. fn fuzz_fixtures(&mut self, address: Address) -> FuzzFixtures { - let mut fixtures = HashMap::new(); + let mut fixtures = HashMap::default(); let fixture_functions = self.contract.abi.functions().filter(|func| func.is_fixture()); for func in fixture_functions { if func.inputs.is_empty() { diff --git a/crates/linking/src/lib.rs b/crates/linking/src/lib.rs index 1ef9c9add..c62c29e6c 100644 --- a/crates/linking/src/lib.rs +++ b/crates/linking/src/lib.rs @@ -317,7 +317,7 @@ mod tests { output = output.with_stripped_file_prefixes(project.root()); } - Self { project, output, dependency_assertions: HashMap::new() } + Self { project, output, dependency_assertions: HashMap::default() } } fn assert_dependencies( diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index 410d89610..97324e5cb 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -350,7 +350,7 @@ impl ExecutedState { /// Collects the return values from the execution result. fn get_returns(&self) -> Result> { - let mut returns = HashMap::new(); + let mut returns = HashMap::default(); let returned = &self.execution_result.returned; let func = &self.execution_data.func; diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index a20a884f0..b7d4088d4 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -536,7 +536,7 @@ impl ScriptConfig { // dapptools compatibility 1 }; - Ok(Self { config, evm_opts, sender_nonce, backends: HashMap::new() }) + Ok(Self { config, evm_opts, sender_nonce, backends: HashMap::default() }) } pub async fn update_sender(&mut self, sender: Address) -> Result<()> { diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index eea660bcd..7c692ea23 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -253,7 +253,7 @@ impl FilledTransactionsState { eyre::bail!("Multi-chain deployment is not supported with libraries."); } - let mut total_gas_per_rpc: HashMap = HashMap::new(); + let mut total_gas_per_rpc: HashMap = HashMap::default(); // Batches sequence of transactions from different rpcs. let mut new_sequence = VecDeque::new(); From 4bcb309eb8eb49e0033d58cce86bd31d44d7937a Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 30 Sep 2024 15:19:57 +0300 Subject: [PATCH 042/100] fix(forge): generate `evm.legacyAssembly` extra output (#8987) fix(forge): include legacyAssembly output --- Cargo.lock | 21 ++++++++++----------- Cargo.toml | 2 +- crates/forge/bin/cmd/inspect.rs | 7 +++++++ crates/forge/tests/cli/cmd.rs | 22 +++++++++++++++++++--- 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 886cc3ff2..fd4e2e0c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3711,9 +3711,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.11.1" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d91e510bd537970f68f8462dea0e8df0a2302d4749fb57bc8e10bbd32a283e2" +checksum = "588bc1dd21020966a255a578433016d4637c810ed76127997a469bc4a3311e7f" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3749,9 +3749,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" -version = "0.11.1" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9971eefe4eae1cf2ac707beb4d40f63304b34c81c0961d299e461c14a23b1e7" +checksum = "3b12c7a7ab554fde7521428b040205569c187995b817481720e99adf524bf7f8" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3759,9 +3759,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" -version = "0.11.1" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde3d12776c295ad85bcdbbae18f4601e384f40a62b0e3371d880bbcd91c65c" +checksum = "4654933ab983928d8e33e7fdf425bb60553e8a4ac415a6014f1f2e56a5b3741d" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3783,9 +3783,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" -version = "0.11.1" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "569a769f6105248816c253715ec39977d61d369e9c67e4774d6870da8f64dffc" +checksum = "2271a6689d27f42ffe1259f587196f56251508c6c9e362c585ccf234f5919f96" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3798,15 +3798,14 @@ dependencies = [ [[package]] name = "foundry-compilers-core" -version = "0.11.1" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f10ade77fa0eab75e142a76711c42a258781bad0c4516ad64aa413297ebb72e" +checksum = "eb71494112126e7ecb92748913403c35ef949f18734e3ff4566a782ce2c8b986" dependencies = [ "alloy-primitives", "cfg-if", "dunce", "fs_extra", - "memmap2", "once_cell", "path-slash", "regex", diff --git a/Cargo.toml b/Cargo.toml index 245c6da41..228bac290 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -167,7 +167,7 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.7.3", default-features = false } -foundry-compilers = { version = "0.11.1", default-features = false } +foundry-compilers = { version = "0.11.3", default-features = false } foundry-fork-db = "0.3.2" solang-parser = "=0.3.3" diff --git a/crates/forge/bin/cmd/inspect.rs b/crates/forge/bin/cmd/inspect.rs index d3097854c..ddfe60e61 100644 --- a/crates/forge/bin/cmd/inspect.rs +++ b/crates/forge/bin/cmd/inspect.rs @@ -101,6 +101,9 @@ impl InspectArgs { ContractArtifactField::Assembly | ContractArtifactField::AssemblyOptimized => { print_json_str(&artifact.assembly, None)?; } + ContractArtifactField::LegacyAssembly => { + print_json_str(&artifact.legacy_assembly, None)?; + } ContractArtifactField::MethodIdentifiers => { print_json(&artifact.method_identifiers)?; } @@ -210,6 +213,7 @@ pub enum ContractArtifactField { DeployedBytecode, Assembly, AssemblyOptimized, + LegacyAssembly, MethodIdentifiers, GasEstimates, StorageLayout, @@ -292,6 +296,7 @@ impl_value_enum! { DeployedBytecode => "deployedBytecode" | "deployed_bytecode" | "deployed-bytecode" | "deployed" | "deployedbytecode", Assembly => "assembly" | "asm", + LegacyAssembly => "legacyAssembly" | "legacyassembly" | "legacy_assembly", AssemblyOptimized => "assemblyOptimized" | "asmOptimized" | "assemblyoptimized" | "assembly_optimized" | "asmopt" | "assembly-optimized" | "asmo" | "asm-optimized" | "asmoptimized" | "asm_optimized", @@ -324,6 +329,7 @@ impl From for ContractOutputSelection { DeployedBytecodeOutputSelection::All, )), Caf::Assembly | Caf::AssemblyOptimized => Self::Evm(EvmOutputSelection::Assembly), + Caf::LegacyAssembly => Self::Evm(EvmOutputSelection::LegacyAssembly), Caf::MethodIdentifiers => Self::Evm(EvmOutputSelection::MethodIdentifiers), Caf::GasEstimates => Self::Evm(EvmOutputSelection::GasEstimates), Caf::StorageLayout => Self::StorageLayout, @@ -354,6 +360,7 @@ impl PartialEq for ContractArtifactField { (Self::Bytecode, Cos::Evm(Eos::ByteCode(_))) | (Self::DeployedBytecode, Cos::Evm(Eos::DeployedByteCode(_))) | (Self::Assembly | Self::AssemblyOptimized, Cos::Evm(Eos::Assembly)) | + (Self::LegacyAssembly, Cos::Evm(Eos::LegacyAssembly)) | (Self::MethodIdentifiers, Cos::Evm(Eos::MethodIdentifiers)) | (Self::GasEstimates, Cos::Evm(Eos::GasEstimates)) | (Self::StorageLayout, Cos::StorageLayout) | diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 8179e9eaa..5f0a78f9b 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -740,9 +740,17 @@ Compiler run successful! // 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"]) - .assert_success() - .stdout_eq(str![[r#" + cmd.args([ + "build", + "--extra-output", + "metadata", + "legacyAssembly", + "ir-optimized", + "--extra-output", + "ir", + ]) + .assert_success() + .stdout_eq(str![[r#" [COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! @@ -753,6 +761,7 @@ Compiler run successful! let artifact: ConfigurableContractArtifact = foundry_compilers::utils::read_json_file(&artifact_path).unwrap(); assert!(artifact.metadata.is_some()); + assert!(artifact.legacy_assembly.is_some()); assert!(artifact.ir.is_some()); assert!(artifact.ir_optimized.is_some()); @@ -763,6 +772,7 @@ Compiler run successful! "metadata", "ir-optimized", "evm.bytecode.sourceMap", + "evm.legacyAssembly", "--force", ]) .root_arg() @@ -784,6 +794,12 @@ Compiler run successful! let sourcemap = prj.paths().artifacts.join(format!("{TEMPLATE_CONTRACT_ARTIFACT_BASE}.sourcemap")); std::fs::read_to_string(sourcemap).unwrap(); + + let legacy_assembly = prj + .paths() + .artifacts + .join(format!("{TEMPLATE_CONTRACT_ARTIFACT_BASE}.legacyAssembly.json")); + std::fs::read_to_string(legacy_assembly).unwrap(); }); forgetest!(can_print_warnings, |prj, cmd| { From 3ff3d0562215bca620e07c5c4c154eec8da0f04b Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Mon, 30 Sep 2024 19:05:58 +0530 Subject: [PATCH 043/100] chore: bump alloy-core deps + revm (#8988) * chore: bump alloy-core deps + revm * bump alloy to 0.4.0 * bump revm-inspectors + type casting to u128 * fix * Revert "fix" This reverts commit 5e0e0d1128b6819acf600b42372156738e666247. * Revert "bump revm-inspectors + type casting to u128" This reverts commit 25aa23cffaadef1d047ce7b359b4d7ad5018704a. * Revert "bump alloy to 0.4.0" This reverts commit f9721e00f5ba726c4fea6651839d65b45faae488. * replace std HashMap with alloy_primitives maps * bump compilers * replace remaining HashMaps * fmt * nit * replace HashSets * fmt --- Cargo.lock | 8 ++-- Cargo.toml | 16 ++++---- crates/anvil/src/config.rs | 3 +- crates/anvil/src/eth/backend/cheats.rs | 8 ++-- crates/anvil/src/eth/backend/fork.rs | 19 ++++++---- crates/anvil/src/eth/backend/mem/storage.rs | 37 +++++++++++-------- crates/anvil/src/eth/sign.rs | 5 +-- crates/anvil/src/filter.rs | 3 +- crates/anvil/tests/it/logs.rs | 4 +- crates/anvil/tests/it/transaction.rs | 6 +-- crates/cheatcodes/src/config.rs | 5 +-- crates/cheatcodes/src/evm/mapping.rs | 17 +++++---- crates/cheatcodes/src/inspector.rs | 12 ++++-- crates/cheatcodes/src/utils.rs | 3 +- crates/chisel/src/runner.rs | 5 +-- crates/chisel/src/session_source.rs | 3 +- crates/common/src/selectors.rs | 3 +- crates/config/src/error.rs | 4 +- crates/config/src/inline/mod.rs | 4 +- crates/config/src/lib.rs | 7 ++-- crates/debugger/src/tui/builder.rs | 6 +-- crates/debugger/src/tui/mod.rs | 7 ++-- crates/doc/src/builder.rs | 2 +- crates/doc/src/document.rs | 2 +- crates/doc/src/parser/comment.rs | 2 +- .../src/preprocessor/contract_inheritance.rs | 3 +- crates/doc/src/preprocessor/inheritdoc.rs | 2 +- crates/doc/src/preprocessor/mod.rs | 3 +- crates/evm/core/src/backend/diagnostic.rs | 5 +-- crates/evm/core/src/fork/multi.rs | 3 +- crates/evm/core/src/state_snapshot.rs | 4 +- crates/evm/evm/src/executors/fuzz/mod.rs | 4 +- crates/evm/evm/src/executors/fuzz/types.rs | 3 +- .../evm/evm/src/executors/invariant/replay.rs | 4 +- crates/evm/evm/src/executors/mod.rs | 9 +++-- crates/evm/evm/src/inspectors/stack.rs | 5 +-- crates/evm/fuzz/src/lib.rs | 9 +++-- crates/evm/fuzz/src/strategies/calldata.rs | 3 +- crates/evm/traces/src/lib.rs | 4 +- crates/forge/bin/cmd/bind.rs | 3 +- crates/forge/bin/cmd/snapshot.rs | 3 +- crates/forge/bin/cmd/watch.rs | 4 +- crates/forge/src/progress.rs | 4 +- crates/forge/src/result.rs | 17 +++++---- crates/linking/src/lib.rs | 3 +- crates/script/src/broadcast.rs | 17 +++++---- crates/script/src/execute.rs | 6 ++- crates/script/src/lib.rs | 9 +++-- crates/script/src/progress.rs | 9 +++-- crates/script/src/sequence.rs | 4 +- crates/verify/src/sourcify.rs | 7 +++- crates/wallets/src/multi_wallet.rs | 10 ++--- 52 files changed, 187 insertions(+), 161 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fd4e2e0c6..7308606c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7029,9 +7029,9 @@ checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" [[package]] name = "redox_syscall" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355ae415ccd3a04315d3f8246e86d67689ea74d88d915576e1589a351062a13b" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags 2.6.0", ] @@ -7622,9 +7622,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.1.17" +version = "2.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c947adb109a8afce5fc9c7bf951f87f146e9147b3a6a58413105628fb1d1e66" +checksum = "215b1103f73e23e9cb6883072c1fb26ae55c09d42054654955c739e5418a7c96" dependencies = [ "sdd", ] diff --git a/Cargo.toml b/Cargo.toml index 228bac290..ae6ee1825 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -172,7 +172,7 @@ foundry-fork-db = "0.3.2" solang-parser = "=0.3.3" ## revm -revm = { version = "14.0.2", default-features = false } +revm = { version = "14.0.3", default-features = false } revm-primitives = { version = "10.0.0", default-features = false } revm-inspectors = { version = "0.7.7", features = ["serde"] } @@ -203,17 +203,17 @@ 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" -alloy-json-abi = "0.8.1" -alloy-primitives = { version = "0.8.1", features = [ +alloy-dyn-abi = "0.8.5" +alloy-json-abi = "0.8.5" +alloy-primitives = { version = "0.8.5", features = [ "getrandom", "rand", "map-fxhash", ] } -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-sol-macro-expander = "0.8.5" +alloy-sol-macro-input = "0.8.5" +alloy-sol-types = "0.8.5" +syn-solidity = "0.8.5" alloy-chains = "0.1" alloy-rlp = "0.3" diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index d907fc200..f3dbf21f6 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -17,7 +17,7 @@ use crate::{ }; use alloy_genesis::Genesis; use alloy_network::AnyNetwork; -use alloy_primitives::{hex, utils::Unit, BlockNumber, TxHash, U256}; +use alloy_primitives::{hex, map::HashMap, utils::Unit, BlockNumber, TxHash, U256}; use alloy_provider::Provider; use alloy_rpc_types::{Block, BlockNumberOrTag, Transaction}; use alloy_serde::WithOtherFields; @@ -46,7 +46,6 @@ use rand::thread_rng; use revm::primitives::BlobExcessGasAndPrice; use serde_json::{json, to_writer, Value}; use std::{ - collections::HashMap, fmt::Write as FmtWrite, fs::File, net::{IpAddr, Ipv4Addr}, diff --git a/crates/anvil/src/eth/backend/cheats.rs b/crates/anvil/src/eth/backend/cheats.rs index 5e941d004..32115cf41 100644 --- a/crates/anvil/src/eth/backend/cheats.rs +++ b/crates/anvil/src/eth/backend/cheats.rs @@ -1,8 +1,8 @@ //! Support for "cheat codes" / bypass functions -use alloy_primitives::Address; +use alloy_primitives::{map::AddressHashSet, Address}; use parking_lot::RwLock; -use std::{collections::HashSet, sync::Arc}; +use std::sync::Arc; /// Manages user modifications that may affect the node's behavior /// @@ -55,7 +55,7 @@ impl CheatsManager { } /// Returns all accounts that are currently being impersonated. - pub fn impersonated_accounts(&self) -> HashSet
{ + pub fn impersonated_accounts(&self) -> AddressHashSet { self.state.read().impersonated_accounts.clone() } } @@ -64,7 +64,7 @@ impl CheatsManager { #[derive(Clone, Debug, Default)] pub struct CheatsState { /// All accounts that are currently impersonated - pub impersonated_accounts: HashSet
, + pub impersonated_accounts: AddressHashSet, /// If set to true will make the `is_impersonated` function always return true pub auto_impersonate_accounts: bool, } diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index c661eeaa8..ea41b85a9 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -4,7 +4,10 @@ use crate::eth::{backend::db::Db, error::BlockchainError, pool::transactions::Po use alloy_consensus::Account; use alloy_eips::eip2930::AccessListResult; use alloy_network::BlockResponse; -use alloy_primitives::{Address, Bytes, StorageValue, B256, U256}; +use alloy_primitives::{ + map::{FbHashMap, HashMap}, + Address, Bytes, StorageValue, B256, U256, +}; use alloy_provider::{ ext::{DebugApi, TraceApi}, Provider, @@ -27,7 +30,7 @@ use parking_lot::{ RawRwLock, RwLock, }; use revm::primitives::BlobExcessGasAndPrice; -use std::{collections::HashMap, sync::Arc, time::Duration}; +use std::{sync::Arc, time::Duration}; use tokio::sync::RwLock as AsyncRwLock; /// Represents a fork of a remote client @@ -675,14 +678,14 @@ 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: FbHashMap<32, Vec>, + pub blocks: FbHashMap<32, AnyNetworkBlock>, pub hashes: HashMap, - pub transactions: HashMap>, - pub transaction_receipts: HashMap, - pub transaction_traces: HashMap>, + pub transactions: FbHashMap<32, WithOtherFields>, + pub transaction_receipts: FbHashMap<32, ReceiptResponse>, + pub transaction_traces: FbHashMap<32, Vec>, pub logs: HashMap>, - pub geth_transaction_traces: HashMap, + pub geth_transaction_traces: FbHashMap<32, GethTrace>, pub block_traces: HashMap>, pub block_receipts: HashMap>, pub code_at: HashMap<(Address, u64), Bytes>, diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 928fb66e4..abc520c3b 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -10,7 +10,10 @@ use crate::eth::{ error::BlockchainError, pool::transactions::PoolTransaction, }; -use alloy_primitives::{Bytes, TxHash, B256, U256, U64}; +use alloy_primitives::{ + map::{B256HashMap, HashMap}, + Bytes, B256, U256, U64, +}; use alloy_rpc_types::{ trace::{ geth::{ @@ -35,12 +38,8 @@ use foundry_evm::{ }, }; use parking_lot::RwLock; -use std::{ - collections::{HashMap, VecDeque}, - fmt, - sync::Arc, - time::Duration, -}; +use std::{collections::VecDeque, fmt, sync::Arc, time::Duration}; +// use yansi::Paint; // === various limits in number of blocks === @@ -52,9 +51,9 @@ const MAX_ON_DISK_HISTORY_LIMIT: usize = 3_600; /// Represents the complete state of single block pub struct InMemoryBlockStates { /// The states at a certain block - states: HashMap, + states: B256HashMap, /// states which data is moved to disk - on_disk_states: HashMap, + on_disk_states: B256HashMap, /// How many states to store at most in_memory_limit: usize, /// minimum amount of states we keep in memory @@ -245,7 +244,7 @@ impl Default for InMemoryBlockStates { #[derive(Clone)] pub struct BlockchainStorage { /// all stored blocks (block hash -> block) - pub blocks: HashMap, + pub blocks: B256HashMap, /// mapping from block number -> block hash pub hashes: HashMap, /// The current best hash @@ -256,7 +255,7 @@ pub struct BlockchainStorage { pub genesis_hash: B256, /// Mapping from the transaction hash to a tuple containing the transaction as well as the /// transaction receipt - pub transactions: HashMap, + pub transactions: B256HashMap, /// The total difficulty of the chain until this block pub total_difficulty: U256, } @@ -280,9 +279,14 @@ impl BlockchainStorage { let best_hash = genesis_hash; let best_number: U64 = U64::from(0u64); + let mut blocks = B256HashMap::default(); + blocks.insert(genesis_hash, block); + + let mut hashes = HashMap::default(); + hashes.insert(best_number, genesis_hash); Self { - blocks: HashMap::from([(genesis_hash, block)]), - hashes: HashMap::from([(best_number, genesis_hash)]), + blocks, + hashes, best_hash, best_number, genesis_hash, @@ -292,9 +296,12 @@ impl BlockchainStorage { } pub fn forked(block_number: u64, block_hash: B256, total_difficulty: U256) -> Self { + let mut hashes = HashMap::default(); + hashes.insert(U64::from(block_number), block_hash); + Self { - blocks: Default::default(), - hashes: HashMap::from([(U64::from(block_number), block_hash)]), + blocks: B256HashMap::default(), + hashes, best_hash: block_hash, best_number: U64::from(block_number), genesis_hash: Default::default(), diff --git a/crates/anvil/src/eth/sign.rs b/crates/anvil/src/eth/sign.rs index d921b18d3..d322e47d2 100644 --- a/crates/anvil/src/eth/sign.rs +++ b/crates/anvil/src/eth/sign.rs @@ -2,14 +2,13 @@ use crate::eth::error::BlockchainError; use alloy_consensus::SignableTransaction; use alloy_dyn_abi::TypedData; use alloy_network::TxSignerSync; -use alloy_primitives::{Address, Signature, B256}; +use alloy_primitives::{map::AddressHashMap, Address, Signature, B256}; use alloy_signer::Signer as AlloySigner; use alloy_signer_local::PrivateKeySigner; use anvil_core::eth::transaction::{ optimism::{DepositTransaction, DepositTransactionRequest}, TypedTransaction, TypedTransactionRequest, }; -use std::collections::HashMap; /// A transaction signer #[async_trait::async_trait] @@ -47,7 +46,7 @@ pub trait Signer: Send + Sync { /// Maintains developer keys pub struct DevSigner { addresses: Vec
, - accounts: HashMap, + accounts: AddressHashMap, } impl DevSigner { diff --git a/crates/anvil/src/filter.rs b/crates/anvil/src/filter.rs index 2144ce27c..c0c8e7aef 100644 --- a/crates/anvil/src/filter.rs +++ b/crates/anvil/src/filter.rs @@ -4,13 +4,12 @@ use crate::{ pubsub::filter_logs, StorageInfo, }; -use alloy_primitives::TxHash; +use alloy_primitives::{map::HashMap, TxHash}; use alloy_rpc_types::{Filter, FilteredParams, Log}; use anvil_core::eth::subscription::SubscriptionId; use anvil_rpc::response::ResponseResult; use futures::{channel::mpsc::Receiver, Stream, StreamExt}; use std::{ - collections::HashMap, pin::Pin, sync::Arc, task::{Context, Poll}, diff --git a/crates/anvil/tests/it/logs.rs b/crates/anvil/tests/it/logs.rs index 1ce4ac64f..3bf09493d 100644 --- a/crates/anvil/tests/it/logs.rs +++ b/crates/anvil/tests/it/logs.rs @@ -5,7 +5,7 @@ use crate::{ utils::{http_provider_with_signer, ws_provider_with_signer}, }; use alloy_network::EthereumWallet; -use alloy_primitives::B256; +use alloy_primitives::{map::B256HashSet, B256}; use alloy_provider::Provider; use alloy_rpc_types::{BlockNumberOrTag, Filter}; use anvil::{spawn, NodeConfig}; @@ -120,7 +120,7 @@ async fn get_all_events() { // test that logs returned from get_logs and get_transaction_receipt have // the same log_index, block_number, and transaction_hash let mut tasks = vec![]; - let mut seen_tx_hashes = std::collections::HashSet::new(); + let mut seen_tx_hashes = B256HashSet::default(); for log in &logs { if seen_tx_hashes.contains(&log.transaction_hash.unwrap()) { continue; diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index b3e82c380..63ebbbcf3 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -3,7 +3,7 @@ use crate::{ utils::{connect_pubsub, http_provider_with_signer}, }; use alloy_network::{EthereumWallet, TransactionBuilder}; -use alloy_primitives::{Address, Bytes, FixedBytes, U256}; +use alloy_primitives::{map::B256HashSet, Address, Bytes, FixedBytes, U256}; use alloy_provider::Provider; use alloy_rpc_types::{ state::{AccountOverride, StateOverride}, @@ -13,7 +13,7 @@ use alloy_serde::WithOtherFields; use anvil::{spawn, EthereumHardfork, NodeConfig}; use eyre::Ok; use futures::{future::join_all, FutureExt, StreamExt}; -use std::{collections::HashSet, str::FromStr, time::Duration}; +use std::{str::FromStr, time::Duration}; use tokio::time::timeout; #[tokio::test(flavor = "multi_thread")] @@ -950,7 +950,7 @@ async fn can_stream_pending_transactions() { if watch_received.len() == num_txs && sub_received.len() == num_txs { if let Some(sent) = &sent { assert_eq!(sent.len(), watch_received.len()); - let sent_txs = sent.iter().map(|tx| tx.transaction_hash).collect::>(); + let sent_txs = sent.iter().map(|tx| tx.transaction_hash).collect::(); assert_eq!(sent_txs, watch_received.iter().copied().collect()); assert_eq!(sent_txs, sub_received.iter().copied().collect()); break diff --git a/crates/cheatcodes/src/config.rs b/crates/cheatcodes/src/config.rs index 715e26dc5..531784e16 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, U256}; +use alloy_primitives::{map::AddressHashMap, U256}; use foundry_common::{fs::normalize_path, ContractsByArtifact}; use foundry_compilers::{utils::canonicalize, ProjectPathsConfig}; use foundry_config::{ @@ -10,7 +10,6 @@ use foundry_config::{ use foundry_evm_core::opts::EvmOpts; use semver::Version; use std::{ - collections::HashMap, path::{Path, PathBuf}, time::Duration, }; @@ -43,7 +42,7 @@ pub struct CheatsConfig { /// How the evm was configured by the user pub evm_opts: EvmOpts, /// Address labels from config - pub labels: HashMap, + pub labels: AddressHashMap, /// Script wallets pub script_wallets: Option, /// Artifacts which are guaranteed to be fresh (either recompiled or cached). diff --git a/crates/cheatcodes/src/evm/mapping.rs b/crates/cheatcodes/src/evm/mapping.rs index 679609274..e8525908e 100644 --- a/crates/cheatcodes/src/evm/mapping.rs +++ b/crates/cheatcodes/src/evm/mapping.rs @@ -1,26 +1,29 @@ use crate::{Cheatcode, Cheatcodes, Result, Vm::*}; -use alloy_primitives::{keccak256, Address, B256, U256}; +use alloy_primitives::{ + keccak256, + map::{AddressHashMap, B256HashMap}, + Address, B256, U256, +}; use alloy_sol_types::SolValue; use revm::interpreter::{opcode, Interpreter}; -use std::collections::HashMap; /// Recorded mapping slots. #[derive(Clone, Debug, Default)] pub struct MappingSlots { /// Holds mapping parent (slots => slots) - pub parent_slots: HashMap, + pub parent_slots: B256HashMap, /// Holds mapping key (slots => key) - pub keys: HashMap, + pub keys: B256HashMap, /// Holds mapping child (slots => slots[]) - pub children: HashMap>, + pub children: B256HashMap>, /// Holds the last sha3 result `sha3_result => (data_low, data_high)`, this would only record /// when sha3 is called with `size == 0x40`, and the lower 256 bits would be stored in /// `data_low`, higher 256 bits in `data_high`. /// This is needed for mapping_key detect if the slot is for some mapping and record that. - pub seen_sha3: HashMap, + pub seen_sha3: B256HashMap<(B256, B256)>, } impl MappingSlots { @@ -113,7 +116,7 @@ fn slot_child<'a>( } #[cold] -pub(crate) fn step(mapping_slots: &mut HashMap, interpreter: &Interpreter) { +pub(crate) fn step(mapping_slots: &mut AddressHashMap, interpreter: &Interpreter) { match interpreter.current_opcode() { opcode::KECCAK256 => { if interpreter.stack.peek(1) == Ok(U256::from(0x40)) { diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 58d8ca5c9..f4d0ed821 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -20,7 +20,11 @@ use crate::{ CheatsConfig, CheatsCtxt, DynCheatcode, Error, Result, Vm, Vm::AccountAccess, }; -use alloy_primitives::{hex, Address, Bytes, Log, TxKind, B256, U256}; +use alloy_primitives::{ + hex, + map::{AddressHashMap, HashMap}, + 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, TransactionMaybeSigned, SELECTOR_LEN}; @@ -48,7 +52,7 @@ use revm::{ use rustc_hash::FxHashMap; use serde_json::Value; use std::{ - collections::{BTreeMap, HashMap, VecDeque}, + collections::{BTreeMap, VecDeque}, fs::File, io::BufReader, ops::Range, @@ -363,7 +367,7 @@ pub struct Cheatcodes { pub gas_price: Option, /// Address labels - pub labels: HashMap, + pub labels: AddressHashMap, /// Prank information pub prank: Option, @@ -432,7 +436,7 @@ pub struct Cheatcodes { pub gas_metering: GasMetering, /// Mapping slots. - pub mapping_slots: Option>, + pub mapping_slots: Option>, /// The current program counter. pub pc: usize, diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index b4e327483..eb4d2f525 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -2,13 +2,12 @@ use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Result, Vm::*}; use alloy_dyn_abi::{DynSolType, DynSolValue}; -use alloy_primitives::U256; +use alloy_primitives::{map::HashMap, U256}; use alloy_sol_types::SolValue; use foundry_common::ens::namehash; use foundry_evm_core::{backend::DatabaseExt, constants::DEFAULT_CREATE2_DEPLOYER}; use proptest::strategy::{Strategy, ValueTree}; use rand::{Rng, RngCore}; -use std::collections::HashMap; /// Contains locations of traces ignored via cheatcodes. /// diff --git a/crates/chisel/src/runner.rs b/crates/chisel/src/runner.rs index e78454ee3..72b083e1f 100644 --- a/crates/chisel/src/runner.rs +++ b/crates/chisel/src/runner.rs @@ -3,14 +3,13 @@ //! This module contains the `ChiselRunner` struct, which assists with deploying //! and calling the REPL contract on a in-memory REVM instance. -use alloy_primitives::{Address, Bytes, Log, U256}; +use alloy_primitives::{map::AddressHashMap, Address, Bytes, Log, U256}; use eyre::Result; use foundry_evm::{ executors::{DeployResult, Executor, RawCallResult}, traces::{TraceKind, Traces}, }; use revm::interpreter::{return_ok, InstructionResult}; -use std::collections::HashMap; /// The function selector of the REPL contract's entrypoint, the `run()` function. static RUN_SELECTOR: [u8; 4] = [0xc0, 0x40, 0x62, 0x26]; @@ -43,7 +42,7 @@ pub struct ChiselResult { /// Amount of gas used in the transaction pub gas_used: u64, /// Map of addresses to their labels - pub labeled_addresses: HashMap, + pub labeled_addresses: AddressHashMap, /// Return data pub returned: Bytes, /// Called address diff --git a/crates/chisel/src/session_source.rs b/crates/chisel/src/session_source.rs index f7db43832..e1b31a972 100644 --- a/crates/chisel/src/session_source.rs +++ b/crates/chisel/src/session_source.rs @@ -4,6 +4,7 @@ //! the REPL contract's source code. It provides simple compilation, parsing, and //! execution helpers. +use alloy_primitives::map::HashMap; use eyre::Result; use forge_fmt::solang_ext::SafeUnwrap; use foundry_compilers::{ @@ -15,7 +16,7 @@ use foundry_evm::{backend::Backend, opts::EvmOpts}; use semver::Version; use serde::{Deserialize, Serialize}; use solang_parser::{diagnostics::Diagnostic, pt}; -use std::{collections::HashMap, fs, path::PathBuf}; +use std::{fs, path::PathBuf}; use yansi::Paint; /// The minimum Solidity version of the `Vm` interface. diff --git a/crates/common/src/selectors.rs b/crates/common/src/selectors.rs index 16565a07e..3b1073916 100644 --- a/crates/common/src/selectors.rs +++ b/crates/common/src/selectors.rs @@ -4,11 +4,11 @@ use crate::abi::abi_decode_calldata; use alloy_json_abi::JsonAbi; +use alloy_primitives::map::HashMap; use eyre::Context; use reqwest::header::{HeaderMap, HeaderName, HeaderValue}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use std::{ - collections::HashMap, fmt, sync::{ atomic::{AtomicBool, AtomicUsize, Ordering}, @@ -16,7 +16,6 @@ use std::{ }, time::Duration, }; - const SELECTOR_LOOKUP_URL: &str = "https://api.openchain.xyz/signature-database/v1/lookup"; const SELECTOR_IMPORT_URL: &str = "https://api.openchain.xyz/signature-database/v1/import"; diff --git a/crates/config/src/error.rs b/crates/config/src/error.rs index 3da1aee09..09f21605d 100644 --- a/crates/config/src/error.rs +++ b/crates/config/src/error.rs @@ -1,8 +1,8 @@ //! error handling and solc error codes +use alloy_primitives::map::HashSet; use figment::providers::{Format, Toml}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::{collections::HashSet, error::Error, fmt, str::FromStr}; - +use std::{error::Error, fmt, str::FromStr}; /// The message shown upon panic if the config could not be extracted from the figment pub const FAILED_TO_EXTRACT_CONFIG_PANIC_MSG: &str = "failed to extract foundry config:"; diff --git a/crates/config/src/inline/mod.rs b/crates/config/src/inline/mod.rs index 36bad2514..8b5616a21 100644 --- a/crates/config/src/inline/mod.rs +++ b/crates/config/src/inline/mod.rs @@ -1,6 +1,6 @@ use crate::Config; -use std::{collections::HashMap, sync::LazyLock}; - +use alloy_primitives::map::HashMap; +use std::sync::LazyLock; mod conf_parser; pub use conf_parser::*; diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 452365ce9..18352aab9 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -38,12 +38,11 @@ use foundry_compilers::{ }; use inflector::Inflector; use regex::Regex; -use revm_primitives::{FixedBytes, SpecId}; +use revm_primitives::{map::AddressHashMap, FixedBytes, SpecId}; use semver::Version; use serde::{Deserialize, Serialize, Serializer}; use std::{ borrow::Cow, - collections::HashMap, fs, path::{Path, PathBuf}, str::FromStr, @@ -418,7 +417,7 @@ pub struct Config { pub disable_block_gas_limit: bool, /// Address labels - pub labels: HashMap, + pub labels: AddressHashMap, /// Whether to enable safety checks for `vm.getCode` and `vm.getDeployedCode` invocations. /// If disabled, it is possible to access artifacts which were not recompiled or cached. @@ -5034,7 +5033,7 @@ mod tests { let config = Config::load(); assert_eq!( config.labels, - HashMap::from_iter(vec![ + AddressHashMap::from_iter(vec![ ( Address::from_str("0x1F98431c8aD98523631AE4a59f267346ea31F984").unwrap(), "Uniswap V3: Factory".to_string() diff --git a/crates/debugger/src/tui/builder.rs b/crates/debugger/src/tui/builder.rs index fd952def5..484611c70 100644 --- a/crates/debugger/src/tui/builder.rs +++ b/crates/debugger/src/tui/builder.rs @@ -1,11 +1,9 @@ //! TUI debugger builder. use crate::{node::flatten_call_trace, DebugNode, Debugger}; -use alloy_primitives::Address; +use alloy_primitives::{map::AddressHashMap, Address}; use foundry_common::{evm::Breakpoints, get_contract_name}; use foundry_evm_traces::{debug::ContractSources, CallTraceArena, CallTraceDecoder, Traces}; -use std::collections::HashMap; - /// Debugger builder. #[derive(Debug, Default)] #[must_use = "builders do nothing unless you call `build` on them"] @@ -13,7 +11,7 @@ pub struct DebuggerBuilder { /// Debug traces returned from the EVM execution. debug_arena: Vec, /// Identified contracts. - identified_contracts: HashMap, + identified_contracts: AddressHashMap, /// Map of source files. sources: ContractSources, /// Map of the debugger breakpoints. diff --git a/crates/debugger/src/tui/mod.rs b/crates/debugger/src/tui/mod.rs index 155973e15..75b747a7e 100644 --- a/crates/debugger/src/tui/mod.rs +++ b/crates/debugger/src/tui/mod.rs @@ -1,6 +1,6 @@ //! The TUI implementation. -use alloy_primitives::Address; +use alloy_primitives::map::AddressHashMap; use crossterm::{ event::{self, DisableMouseCapture, EnableMouseCapture, Event}, execute, @@ -14,7 +14,6 @@ use ratatui::{ Terminal, }; use std::{ - collections::HashMap, io, ops::ControlFlow, sync::{mpsc, Arc}, @@ -44,7 +43,7 @@ pub enum ExitReason { /// The TUI debugger. pub struct Debugger { debug_arena: Vec, - identified_contracts: HashMap, + identified_contracts: AddressHashMap, /// Source map of contract sources contracts_sources: ContractSources, breakpoints: Breakpoints, @@ -60,7 +59,7 @@ impl Debugger { /// Creates a new debugger. pub fn new( debug_arena: Vec, - identified_contracts: HashMap, + identified_contracts: AddressHashMap, contracts_sources: ContractSources, breakpoints: Breakpoints, ) -> Self { diff --git a/crates/doc/src/builder.rs b/crates/doc/src/builder.rs index e21e80c22..3ec433c39 100644 --- a/crates/doc/src/builder.rs +++ b/crates/doc/src/builder.rs @@ -2,6 +2,7 @@ use crate::{ document::DocumentContent, helpers::merge_toml_table, AsDoc, BufWriter, Document, ParseItem, ParseSource, Parser, Preprocessor, }; +use alloy_primitives::map::HashMap; use forge_fmt::{FormatterConfig, Visitable}; use foundry_compilers::{compilers::solc::SOLC_EXTENSIONS, utils::source_files_iter}; use foundry_config::{filter::expand_globs, DocConfig}; @@ -10,7 +11,6 @@ use mdbook::MDBook; use rayon::prelude::*; use std::{ cmp::Ordering, - collections::HashMap, fs, path::{Path, PathBuf}, }; diff --git a/crates/doc/src/document.rs b/crates/doc/src/document.rs index be4c1e647..10f72a672 100644 --- a/crates/doc/src/document.rs +++ b/crates/doc/src/document.rs @@ -1,6 +1,6 @@ use crate::{DocBuilder, ParseItem, PreprocessorId, PreprocessorOutput}; +use alloy_primitives::map::HashMap; use std::{ - collections::HashMap, path::{Path, PathBuf}, slice::IterMut, sync::Mutex, diff --git a/crates/doc/src/parser/comment.rs b/crates/doc/src/parser/comment.rs index 280dcfd0d..13184112e 100644 --- a/crates/doc/src/parser/comment.rs +++ b/crates/doc/src/parser/comment.rs @@ -1,6 +1,6 @@ +use alloy_primitives::map::HashMap; use derive_more::{Deref, DerefMut}; use solang_parser::doccomment::DocCommentTag; -use std::collections::HashMap; /// The natspec comment tag explaining the purpose of the comment. /// See: . diff --git a/crates/doc/src/preprocessor/contract_inheritance.rs b/crates/doc/src/preprocessor/contract_inheritance.rs index 7d74589bd..ac229a434 100644 --- a/crates/doc/src/preprocessor/contract_inheritance.rs +++ b/crates/doc/src/preprocessor/contract_inheritance.rs @@ -1,7 +1,8 @@ use super::{Preprocessor, PreprocessorId}; use crate::{document::DocumentContent, Document, ParseSource, PreprocessorOutput}; +use alloy_primitives::map::HashMap; use forge_fmt::solang_ext::SafeUnwrap; -use std::{collections::HashMap, path::PathBuf}; +use std::path::PathBuf; /// [ContractInheritance] preprocessor id. pub const CONTRACT_INHERITANCE_ID: PreprocessorId = PreprocessorId("contract_inheritance"); diff --git a/crates/doc/src/preprocessor/inheritdoc.rs b/crates/doc/src/preprocessor/inheritdoc.rs index 8d29a64fc..65cbb688c 100644 --- a/crates/doc/src/preprocessor/inheritdoc.rs +++ b/crates/doc/src/preprocessor/inheritdoc.rs @@ -2,8 +2,8 @@ use super::{Preprocessor, PreprocessorId}; use crate::{ document::DocumentContent, Comments, Document, ParseItem, ParseSource, PreprocessorOutput, }; +use alloy_primitives::map::HashMap; use forge_fmt::solang_ext::SafeUnwrap; -use std::collections::HashMap; /// [`Inheritdoc`] preprocessor ID. pub const INHERITDOC_ID: PreprocessorId = PreprocessorId("inheritdoc"); diff --git a/crates/doc/src/preprocessor/mod.rs b/crates/doc/src/preprocessor/mod.rs index 25ed4db23..5011b59a1 100644 --- a/crates/doc/src/preprocessor/mod.rs +++ b/crates/doc/src/preprocessor/mod.rs @@ -1,7 +1,8 @@ //! Module containing documentation preprocessors. use crate::{Comments, Document}; -use std::{collections::HashMap, fmt::Debug, path::PathBuf}; +use alloy_primitives::map::HashMap; +use std::{fmt::Debug, path::PathBuf}; mod contract_inheritance; pub use contract_inheritance::{ContractInheritance, CONTRACT_INHERITANCE_ID}; diff --git a/crates/evm/core/src/backend/diagnostic.rs b/crates/evm/core/src/backend/diagnostic.rs index 109190a8f..df215508d 100644 --- a/crates/evm/core/src/backend/diagnostic.rs +++ b/crates/evm/core/src/backend/diagnostic.rs @@ -1,7 +1,6 @@ use crate::backend::LocalForkId; -use alloy_primitives::Address; +use alloy_primitives::{map::AddressHashMap, Address}; use itertools::Itertools; -use std::collections::HashMap; /// Represents possible diagnostic cases on revert #[derive(Clone, Debug)] @@ -21,7 +20,7 @@ pub enum RevertDiagnostic { impl RevertDiagnostic { /// Converts the diagnostic to a readable error message - pub fn to_error_msg(&self, labels: &HashMap) -> String { + pub fn to_error_msg(&self, labels: &AddressHashMap) -> String { let get_label = |addr: &Address| labels.get(addr).cloned().unwrap_or_else(|| addr.to_string()); diff --git a/crates/evm/core/src/fork/multi.rs b/crates/evm/core/src/fork/multi.rs index a39b543ea..14fa59aaf 100644 --- a/crates/evm/core/src/fork/multi.rs +++ b/crates/evm/core/src/fork/multi.rs @@ -4,7 +4,7 @@ //! concurrently active pairs at once. use super::CreateFork; -use alloy_primitives::U256; +use alloy_primitives::{map::HashMap, U256}; use alloy_provider::network::{BlockResponse, HeaderResponse}; use alloy_transport::layers::RetryBackoffService; use foundry_common::provider::{ @@ -20,7 +20,6 @@ use futures::{ }; use revm::primitives::Env; use std::{ - collections::HashMap, fmt::{self, Write}, pin::Pin, sync::{ diff --git a/crates/evm/core/src/state_snapshot.rs b/crates/evm/core/src/state_snapshot.rs index 0f453b8c6..3be1172ad 100644 --- a/crates/evm/core/src/state_snapshot.rs +++ b/crates/evm/core/src/state_snapshot.rs @@ -1,7 +1,7 @@ //! Support for snapshotting different states -use alloy_primitives::U256; -use std::{collections::HashMap, ops::Add}; +use alloy_primitives::{map::HashMap, U256}; +use std::ops::Add; /// Represents all state snapshots #[derive(Clone, Debug)] diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index 775b7e73e..cb57f2053 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -1,7 +1,7 @@ use crate::executors::{Executor, RawCallResult}; use alloy_dyn_abi::JsonAbiExt; use alloy_json_abi::Function; -use alloy_primitives::{Address, Bytes, Log, U256}; +use alloy_primitives::{map::HashMap, Address, Bytes, Log, U256}; use eyre::Result; use foundry_common::evm::Breakpoints; use foundry_config::FuzzConfig; @@ -17,7 +17,7 @@ use foundry_evm_fuzz::{ use foundry_evm_traces::SparsedTraceArena; use indicatif::ProgressBar; use proptest::test_runner::{TestCaseError, TestError, TestRunner}; -use std::{cell::RefCell, collections::HashMap}; +use std::cell::RefCell; mod types; pub use types::{CaseOutcome, CounterExampleOutcome, FuzzOutcome}; diff --git a/crates/evm/evm/src/executors/fuzz/types.rs b/crates/evm/evm/src/executors/fuzz/types.rs index 081ff9112..f5399c5c3 100644 --- a/crates/evm/evm/src/executors/fuzz/types.rs +++ b/crates/evm/evm/src/executors/fuzz/types.rs @@ -1,11 +1,10 @@ use crate::executors::RawCallResult; -use alloy_primitives::{Bytes, Log}; +use alloy_primitives::{map::HashMap, Bytes, Log}; use foundry_common::evm::Breakpoints; use foundry_evm_coverage::HitMaps; use foundry_evm_fuzz::FuzzCase; use foundry_evm_traces::SparsedTraceArena; use revm::interpreter::InstructionResult; -use std::collections::HashMap; /// Returned by a single fuzz in the case of a successful run #[derive(Debug)] diff --git a/crates/evm/evm/src/executors/invariant/replay.rs b/crates/evm/evm/src/executors/invariant/replay.rs index 177e1b55b..969f1587f 100644 --- a/crates/evm/evm/src/executors/invariant/replay.rs +++ b/crates/evm/evm/src/executors/invariant/replay.rs @@ -4,7 +4,7 @@ use super::{ }; use crate::executors::Executor; use alloy_dyn_abi::JsonAbiExt; -use alloy_primitives::Log; +use alloy_primitives::{map::HashMap, Log}; use eyre::Result; use foundry_common::{ContractsByAddress, ContractsByArtifact}; use foundry_evm_coverage::HitMaps; @@ -17,7 +17,7 @@ use indicatif::ProgressBar; use parking_lot::RwLock; use proptest::test_runner::TestError; use revm::primitives::U256; -use std::{collections::HashMap, sync::Arc}; +use std::sync::Arc; /// Replays a call sequence for collecting logs and traces. /// Returns counterexample to be used when the call sequence is a failed scenario. diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 421880c0d..5e2acc98f 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -11,7 +11,10 @@ use crate::inspectors::{ }; use alloy_dyn_abi::{DynSolValue, FunctionExt, JsonAbiExt}; use alloy_json_abi::Function; -use alloy_primitives::{Address, Bytes, Log, U256}; +use alloy_primitives::{ + map::{AddressHashMap, HashMap}, + Address, Bytes, Log, U256, +}; use alloy_sol_types::{sol, SolCall}; use foundry_evm_core::{ backend::{Backend, BackendError, BackendResult, CowBackend, DatabaseExt, GLOBAL_FAIL_SLOT}, @@ -32,7 +35,7 @@ use revm::{ SpecId, TxEnv, TxKind, }, }; -use std::{borrow::Cow, collections::HashMap}; +use std::borrow::Cow; mod builder; pub use builder::ExecutorBuilder; @@ -723,7 +726,7 @@ pub struct RawCallResult { /// The logs emitted during the call pub logs: Vec, /// The labels assigned to addresses during the call - pub labels: HashMap, + pub labels: AddressHashMap, /// The traces of the call pub traces: Option, /// The coverage info collected during the call diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 30271910b..fc2705641 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -2,7 +2,7 @@ use super::{ Cheatcodes, CheatsConfig, ChiselState, CoverageCollector, Fuzzer, LogCollector, TracingInspector, }; -use alloy_primitives::{Address, Bytes, Log, TxKind, U256}; +use alloy_primitives::{map::AddressHashMap, Address, Bytes, Log, TxKind, U256}; use foundry_cheatcodes::CheatcodesExecutor; use foundry_evm_core::{ backend::{update_state, DatabaseExt}, @@ -22,7 +22,6 @@ use revm::{ EvmContext, Inspector, }; use std::{ - collections::HashMap, ops::{Deref, DerefMut}, sync::Arc, }; @@ -243,7 +242,7 @@ macro_rules! call_inspectors_adjust_depth { /// The collected results of [`InspectorStack`]. pub struct InspectorData { pub logs: Vec, - pub labels: HashMap, + pub labels: AddressHashMap, pub traces: Option, pub coverage: Option, pub cheatcodes: Option, diff --git a/crates/evm/fuzz/src/lib.rs b/crates/evm/fuzz/src/lib.rs index 2930a6aa0..276958ecf 100644 --- a/crates/evm/fuzz/src/lib.rs +++ b/crates/evm/fuzz/src/lib.rs @@ -9,13 +9,16 @@ extern crate tracing; use alloy_dyn_abi::{DynSolValue, JsonAbiExt}; -use alloy_primitives::{Address, Bytes, Log}; +use alloy_primitives::{ + map::{AddressHashMap, HashMap}, + Address, Bytes, Log, +}; use foundry_common::{calc, contracts::ContractsByAddress, evm::Breakpoints}; use foundry_evm_coverage::HitMaps; use foundry_evm_traces::{CallTraceArena, SparsedTraceArena}; use itertools::Itertools; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, fmt, sync::Arc}; +use std::{fmt, sync::Arc}; pub use proptest::test_runner::{Config as FuzzConfig, Reason}; @@ -166,7 +169,7 @@ pub struct FuzzTestResult { pub logs: Vec, /// Labeled addresses - pub labeled_addresses: HashMap, + pub labeled_addresses: AddressHashMap, /// Exemplary traces for a fuzz run of the test function /// diff --git a/crates/evm/fuzz/src/strategies/calldata.rs b/crates/evm/fuzz/src/strategies/calldata.rs index b919c477f..6d9c0340b 100644 --- a/crates/evm/fuzz/src/strategies/calldata.rs +++ b/crates/evm/fuzz/src/strategies/calldata.rs @@ -65,9 +65,8 @@ mod tests { use crate::{strategies::fuzz_calldata, FuzzFixtures}; use alloy_dyn_abi::{DynSolValue, JsonAbiExt}; use alloy_json_abi::Function; - use alloy_primitives::Address; + use alloy_primitives::{map::HashMap, Address}; use proptest::prelude::Strategy; - use std::collections::HashMap; #[test] fn can_fuzz_with_fixtures() { diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index 382e3d3ec..dd4a89664 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -17,10 +17,12 @@ use revm_inspectors::tracing::{ use serde::{Deserialize, Serialize}; use std::{ borrow::Cow, - collections::{BTreeSet, HashMap}, + collections::BTreeSet, ops::{Deref, DerefMut}, }; +use alloy_primitives::map::HashMap; + pub use revm_inspectors::tracing::{ types::{ CallKind, CallLog, CallTrace, CallTraceNode, DecodedCallData, DecodedCallLog, diff --git a/crates/forge/bin/cmd/bind.rs b/crates/forge/bin/cmd/bind.rs index d5d236332..d8739b42d 100644 --- a/crates/forge/bin/cmd/bind.rs +++ b/crates/forge/bin/cmd/bind.rs @@ -1,3 +1,4 @@ +use alloy_primitives::map::HashSet; use clap::{Parser, ValueHint}; use ethers_contract_abigen::{ Abigen, ContractFilter, ExcludeContracts, MultiAbigen, SelectContracts, @@ -244,7 +245,7 @@ impl BindArgs { } fn get_solmacrogen(&self, artifacts: &Path) -> Result { - let mut dup = std::collections::HashSet::::new(); + let mut dup = HashSet::::default(); let instances = self .get_json_files(artifacts)? .filter_map(|(name, path)| { diff --git a/crates/forge/bin/cmd/snapshot.rs b/crates/forge/bin/cmd/snapshot.rs index 20c5013ec..0d7c2843a 100644 --- a/crates/forge/bin/cmd/snapshot.rs +++ b/crates/forge/bin/cmd/snapshot.rs @@ -1,5 +1,5 @@ use super::test; -use alloy_primitives::U256; +use alloy_primitives::{map::HashMap, U256}; use clap::{builder::RangedU64ValueParser, Parser, ValueHint}; use eyre::{Context, Result}; use forge::result::{SuiteTestResult, TestKindReport, TestOutcome}; @@ -7,7 +7,6 @@ use foundry_cli::utils::STATIC_FUZZ_SEED; use regex::Regex; use std::{ cmp::Ordering, - collections::HashMap, fs, io::{self, BufRead}, path::{Path, PathBuf}, diff --git a/crates/forge/bin/cmd/watch.rs b/crates/forge/bin/cmd/watch.rs index 19b37009f..4cf7dbabe 100644 --- a/crates/forge/bin/cmd/watch.rs +++ b/crates/forge/bin/cmd/watch.rs @@ -1,11 +1,11 @@ use super::{build::BuildArgs, doc::DocArgs, snapshot::GasSnapshotArgs, test::TestArgs}; +use alloy_primitives::map::HashSet; use clap::Parser; use eyre::Result; use foundry_cli::utils::{self, FoundryPathExt}; use foundry_config::Config; use parking_lot::Mutex; use std::{ - collections::HashSet, path::PathBuf, sync::{ atomic::{AtomicU8, Ordering}, @@ -265,7 +265,7 @@ pub async fn watch_test(args: TestArgs) -> Result<()> { filter.args().contract_pattern.is_some() || args.watch.run_all; - let last_test_files = Mutex::new(HashSet::::new()); + let last_test_files = Mutex::new(HashSet::::default()); let project_root = config.root.0.to_string_lossy().into_owned(); let config = args.watch.watchexec_config_with_override( || [&config.test, &config.src], diff --git a/crates/forge/src/progress.rs b/crates/forge/src/progress.rs index 8d047c413..2b45d5513 100644 --- a/crates/forge/src/progress.rs +++ b/crates/forge/src/progress.rs @@ -1,7 +1,7 @@ +use alloy_primitives::map::HashMap; use indicatif::{MultiProgress, ProgressBar}; use parking_lot::Mutex; -use std::{collections::HashMap, sync::Arc, time::Duration}; - +use std::{sync::Arc, time::Duration}; /// State of [ProgressBar]s displayed for the given test run. /// Shows progress of all test suites matching filter. /// For each test within the test suite an individual progress bar is displayed. diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index 171a234a5..1ec829f6b 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -4,7 +4,10 @@ use crate::{ fuzz::{BaseCounterExample, FuzzedCases}, gas_report::GasReport, }; -use alloy_primitives::{Address, Log}; +use alloy_primitives::{ + map::{AddressHashMap, HashMap}, + Address, Log, +}; use eyre::Report; use foundry_common::{evm::Breakpoints, get_contract_name, get_file_name, shell}; use foundry_evm::{ @@ -16,7 +19,7 @@ use foundry_evm::{ }; use serde::{Deserialize, Serialize}; use std::{ - collections::{BTreeMap, HashMap}, + collections::BTreeMap, fmt::{self, Write}, time::Duration, }; @@ -402,7 +405,7 @@ pub struct TestResult { pub coverage: Option, /// Labeled addresses - pub labeled_addresses: HashMap, + pub labeled_addresses: AddressHashMap, pub duration: Duration, @@ -738,7 +741,7 @@ pub struct TestSetup { /// Call traces of the setup pub traces: Traces, /// Addresses labeled during setup - pub labeled_addresses: HashMap, + pub labeled_addresses: AddressHashMap, /// The reason the setup failed, if it did pub reason: Option, /// Coverage info during setup @@ -752,7 +755,7 @@ impl TestSetup { error: EvmError, mut logs: Vec, mut traces: Traces, - mut labeled_addresses: HashMap, + mut labeled_addresses: AddressHashMap, ) -> Self { match error { EvmError::Execution(err) => { @@ -775,7 +778,7 @@ impl TestSetup { address: Address, logs: Vec, traces: Traces, - labeled_addresses: HashMap, + labeled_addresses: AddressHashMap, coverage: Option, fuzz_fixtures: FuzzFixtures, ) -> Self { @@ -785,7 +788,7 @@ impl TestSetup { pub fn failed_with( logs: Vec, traces: Traces, - labeled_addresses: HashMap, + labeled_addresses: AddressHashMap, reason: String, ) -> Self { Self { diff --git a/crates/linking/src/lib.rs b/crates/linking/src/lib.rs index c62c29e6c..d0959ddb7 100644 --- a/crates/linking/src/lib.rs +++ b/crates/linking/src/lib.rs @@ -283,9 +283,8 @@ impl<'a> Linker<'a> { #[cfg(test)] mod tests { use super::*; - use alloy_primitives::fixed_bytes; + use alloy_primitives::{fixed_bytes, map::HashMap}; use foundry_compilers::{Project, ProjectCompileOutput, ProjectPathsConfig}; - use std::collections::HashMap; struct LinkerTest { project: Project, diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index ff4e4205e..ac893b19f 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -6,7 +6,11 @@ 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}; +use alloy_primitives::{ + map::{AddressHashMap, AddressHashSet}, + utils::format_units, + Address, TxHash, +}; use alloy_provider::{utils::Eip1559Estimation, Provider}; use alloy_rpc_types::TransactionRequest; use alloy_serde::WithOtherFields; @@ -22,10 +26,7 @@ use foundry_common::{ use foundry_config::Config; use futures::{future::join_all, StreamExt}; use itertools::Itertools; -use std::{ - collections::{HashMap, HashSet}, - sync::Arc, -}; +use std::sync::Arc; pub async fn estimate_gas( tx: &mut WithOtherFields, @@ -115,9 +116,9 @@ pub enum SendTransactionKind<'a> { /// Represents how to send _all_ transactions pub enum SendTransactionsKind { /// Send via `eth_sendTransaction` and rely on the `from` address being unlocked. - Unlocked(HashSet
), + Unlocked(AddressHashSet), /// Send a signed transaction via `eth_sendRawTransaction` - Raw(HashMap), + Raw(AddressHashMap), } impl SendTransactionsKind { @@ -199,7 +200,7 @@ impl BundledState { .sequences() .iter() .flat_map(|sequence| sequence.transactions().map(|tx| tx.from().expect("missing from"))) - .collect::>(); + .collect::(); if required_addresses.contains(&Config::DEFAULT_SENDER) { eyre::bail!( diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index 97324e5cb..a1b3cf613 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -6,7 +6,10 @@ use crate::{ }; use alloy_dyn_abi::FunctionExt; use alloy_json_abi::{Function, InternalType, JsonAbi}; -use alloy_primitives::{Address, Bytes}; +use alloy_primitives::{ + map::{HashMap, HashSet}, + Address, Bytes, +}; use alloy_provider::Provider; use alloy_rpc_types::TransactionInput; use async_recursion::async_recursion; @@ -31,7 +34,6 @@ use foundry_evm::{ }; use futures::future::join_all; use itertools::Itertools; -use std::collections::{HashMap, HashSet}; use yansi::Paint; /// State after linking, contains the linked build data along with library addresses and optional diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index b7d4088d4..8231a5d54 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -11,7 +11,11 @@ extern crate tracing; use self::transaction::AdditionalContract; use crate::runner::ScriptRunner; use alloy_json_abi::{Function, JsonAbi}; -use alloy_primitives::{hex, Address, Bytes, Log, TxKind, U256}; +use alloy_primitives::{ + hex, + map::{AddressHashMap, HashMap}, + Address, Bytes, Log, TxKind, U256, +}; use alloy_signer::Signer; use broadcast::next_nonce; use build::PreprocessedState; @@ -47,7 +51,6 @@ use foundry_evm::{ }; use foundry_wallets::MultiWalletOpts; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; use yansi::Paint; mod broadcast; @@ -476,7 +479,7 @@ pub struct ScriptResult { pub logs: Vec, pub traces: Traces, pub gas_used: u64, - pub labeled_addresses: HashMap, + pub labeled_addresses: AddressHashMap, #[serde(skip)] pub transactions: Option, pub returned: Bytes, diff --git a/crates/script/src/progress.rs b/crates/script/src/progress.rs index 819ebca99..b8bedb6cd 100644 --- a/crates/script/src/progress.rs +++ b/crates/script/src/progress.rs @@ -3,14 +3,17 @@ use crate::{ sequence::ScriptSequence, }; use alloy_chains::Chain; -use alloy_primitives::B256; +use alloy_primitives::{ + map::{B256HashMap, HashMap}, + B256, +}; use eyre::Result; use foundry_cli::utils::init_progress; use foundry_common::provider::RetryProvider; use futures::StreamExt; use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use parking_lot::RwLock; -use std::{collections::HashMap, fmt::Write, sync::Arc, time::Duration}; +use std::{fmt::Write, sync::Arc, time::Duration}; use yansi::Paint; /// State of [ProgressBar]s displayed for the given [ScriptSequence]. @@ -23,7 +26,7 @@ pub struct SequenceProgressState { /// Progress var with the count of confirmed transactions. receipts: ProgressBar, /// Standalone spinners for pending transactions. - tx_spinners: HashMap, + tx_spinners: B256HashMap, /// Copy of the main [MultiProgress] instance. multi: MultiProgress, } diff --git a/crates/script/src/sequence.rs b/crates/script/src/sequence.rs index 8e2101155..3c31773ba 100644 --- a/crates/script/src/sequence.rs +++ b/crates/script/src/sequence.rs @@ -3,7 +3,7 @@ use crate::{ transaction::{AdditionalContract, TransactionWithMetadata}, verify::VerifyBundle, }; -use alloy_primitives::{hex, Address, TxHash}; +use alloy_primitives::{hex, map::HashMap, Address, TxHash}; use alloy_rpc_types::AnyTransactionReceipt; use eyre::{eyre, ContextCompat, Result, WrapErr}; use forge_verify::provider::VerificationProviderType; @@ -13,7 +13,7 @@ use foundry_compilers::ArtifactId; use foundry_config::Config; use serde::{Deserialize, Serialize}; use std::{ - collections::{HashMap, VecDeque}, + collections::VecDeque, io::{BufWriter, Write}, path::{Path, PathBuf}, }; diff --git a/crates/verify/src/sourcify.rs b/crates/verify/src/sourcify.rs index 81fadd09e..bbb5e9f9e 100644 --- a/crates/verify/src/sourcify.rs +++ b/crates/verify/src/sourcify.rs @@ -2,13 +2,15 @@ use crate::{ provider::{VerificationContext, VerificationProvider}, verify::{VerifyArgs, VerifyCheckArgs}, }; +use alloy_primitives::map::HashMap; use async_trait::async_trait; use eyre::Result; use foundry_common::{fs, retry::Retry}; use futures::FutureExt; use reqwest::Url; +use revm_primitives::map::FxBuildHasher; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, str::FromStr}; +use std::str::FromStr; pub static SOURCIFY_URL: &str = "https://sourcify.dev/server/"; @@ -112,7 +114,8 @@ impl SourcifyVerificationProvider { let metadata = context.get_target_metadata()?; let imports = context.get_target_imports()?; - let mut files = HashMap::with_capacity(2 + imports.len()); + let mut files = + HashMap::with_capacity_and_hasher(2 + imports.len(), FxBuildHasher::default()); let metadata = serde_json::to_string_pretty(&metadata)?; files.insert("metadata.json".to_string(), metadata); diff --git a/crates/wallets/src/multi_wallet.rs b/crates/wallets/src/multi_wallet.rs index 459074aaa..28c3e31a1 100644 --- a/crates/wallets/src/multi_wallet.rs +++ b/crates/wallets/src/multi_wallet.rs @@ -2,14 +2,14 @@ use crate::{ utils, wallet_signer::{PendingSigner, WalletSigner}, }; -use alloy_primitives::Address; +use alloy_primitives::{map::AddressHashMap, Address}; use alloy_signer::Signer; use clap::Parser; use derive_builder::Builder; use eyre::Result; use foundry_config::Config; use serde::Serialize; -use std::{collections::HashMap, iter::repeat, path::PathBuf}; +use std::{iter::repeat, path::PathBuf}; /// Container for multiple wallets. #[derive(Debug, Default)] @@ -18,7 +18,7 @@ pub struct MultiWallet { /// Those are lazily unlocked on the first access of the signers. pending_signers: Vec, /// Contains unlocked signers. - signers: HashMap, + signers: AddressHashMap, } impl MultiWallet { @@ -35,12 +35,12 @@ impl MultiWallet { Ok(()) } - pub fn signers(&mut self) -> Result<&HashMap> { + pub fn signers(&mut self) -> Result<&AddressHashMap> { self.maybe_unlock_pending()?; Ok(&self.signers) } - pub fn into_signers(mut self) -> Result> { + pub fn into_signers(mut self) -> Result> { self.maybe_unlock_pending()?; Ok(self.signers) } From 452066e9747a28682a4de069a05b10fe9f381167 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Wed, 2 Oct 2024 10:42:34 +0200 Subject: [PATCH 044/100] fix(ci): flexibly handle forge-std being installed with tag or untagged (#9003) flexible handle forge-std being installed with tag or untagged --- crates/forge/tests/cli/cmd.rs | 20 ++++++++++---------- crates/forge/tests/cli/config.rs | 4 ++-- crates/forge/tests/cli/debug.rs | 2 +- crates/forge/tests/cli/script.rs | 10 +++++----- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 5f0a78f9b..6c8bab928 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -228,7 +228,7 @@ forgetest!(can_init_repo_with_config, |prj, cmd| { 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 [..] + Installed forge-std[..] Initialized forge project "#]]); @@ -276,7 +276,7 @@ forgetest!(can_init_no_git, |prj, cmd| { 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 [..] + Installed forge-std[..] Initialized forge project "#]]); @@ -361,7 +361,7 @@ Run with the `--force` flag to initialize regardless. 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 [..] + Installed forge-std[..] Initialized forge project "#]]); @@ -396,7 +396,7 @@ Run with the `--force` flag to initialize regardless. 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 [..] + Installed forge-std[..] Initialized forge project "#]]); @@ -432,7 +432,7 @@ Run with the `--force` flag to initialize regardless. 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 [..] + Installed forge-std[..] Initialized forge project "#]]); @@ -451,7 +451,7 @@ forgetest!(can_init_vscode, |prj, cmd| { 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 [..] + Installed forge-std[..] Initialized forge project "#]]); @@ -547,7 +547,7 @@ forgetest!(can_clone, |prj, cmd| { 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 [..] + Installed forge-std[..] Initialized forge project Collecting the creation information of 0x044b75f554b886A065b9567891e45c79542d7357 from Etherscan... [COMPILING_FILES] with [SOLC_VERSION] @@ -595,7 +595,7 @@ forgetest!(can_clone_no_remappings_txt, |prj, cmd| { 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 [..] + Installed forge-std[..] Initialized forge project Collecting the creation information of 0x33e690aEa97E4Ef25F0d140F1bf044d663091DAf from Etherscan... [COMPILING_FILES] with [SOLC_VERSION] @@ -1244,7 +1244,7 @@ forgetest!(can_install_and_remove, |prj, cmd| { .assert_success() .stdout_eq(str![[r#" Installing forge-std in [..] (url: Some("https://github.com/foundry-rs/forge-std"), tag: None) - Installed forge-std [..] + Installed forge-std[..] "#]]); @@ -1312,7 +1312,7 @@ forgetest!(can_reinstall_after_manual_remove, |prj, cmd| { .assert_success() .stdout_eq(str![[r#" Installing forge-std in [..] (url: Some("https://github.com/foundry-rs/forge-std"), tag: None) - Installed forge-std [..] + Installed forge-std[..] "#]]); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 42a2b5142..f8b1da523 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -604,7 +604,7 @@ forgetest!(can_update_libs_section, |prj, cmd| { 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 [..] + Installed forge-std[..] "#] ]); @@ -636,7 +636,7 @@ forgetest!(config_emit_warnings, |prj, cmd| { 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 [..] + Installed forge-std[..] "#] ]); diff --git a/crates/forge/tests/cli/debug.rs b/crates/forge/tests/cli/debug.rs index 8ee1eb3f2..bbed7dc72 100644 --- a/crates/forge/tests/cli/debug.rs +++ b/crates/forge/tests/cli/debug.rs @@ -11,7 +11,7 @@ forgetest_async!( 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 [..] + Installed forge-std[..] Initialized forge project "#]]); diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 78e27d653..fb2e8ae8d 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -1012,7 +1012,7 @@ forgetest_async!(can_execute_script_with_arguments, |prj, cmd| { 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 [..] + Installed forge-std[..] Initialized forge project "#]]); @@ -1134,7 +1134,7 @@ forgetest_async!(can_execute_script_with_arguments_nested_deploy, |prj, cmd| { 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 [..] + Installed forge-std[..] Initialized forge project "#]]); @@ -1301,7 +1301,7 @@ forgetest_async!(assert_tx_origin_is_not_overritten, |prj, cmd| { 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 [..] + Installed forge-std[..] Initialized forge project "#]]); @@ -1382,7 +1382,7 @@ forgetest_async!(assert_can_create_multiple_contracts_with_correct_nonce, |prj, 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 [..] + Installed forge-std[..] Initialized forge project "#]]); @@ -1607,7 +1607,7 @@ forgetest_async!(can_decode_custom_errors, |prj, cmd| { 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 [..] + Installed forge-std[..] Initialized forge project "#]]); From 4469a65cdf18eb03648b9f2bde4b597f4c91bd89 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Wed, 2 Oct 2024 12:03:23 +0200 Subject: [PATCH 045/100] docs: clarify keystore path should point to a filename (#9004) clarify that you should point to a keystore by its file but it can be a custom directory --- crates/wallets/src/multi_wallet.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/wallets/src/multi_wallet.rs b/crates/wallets/src/multi_wallet.rs index 28c3e31a1..c593e67e3 100644 --- a/crates/wallets/src/multi_wallet.rs +++ b/crates/wallets/src/multi_wallet.rs @@ -162,7 +162,7 @@ pub struct MultiWalletOpts { )] pub mnemonic_indexes: Option>, - /// Use the keystore in the given folder or file. + /// Use the keystore by its filename in the given folder. #[arg( long = "keystore", visible_alias = "keystores", @@ -173,7 +173,7 @@ pub struct MultiWalletOpts { #[builder(default = "None")] pub keystore_paths: Option>, - /// Use a keystore from the default keystores folder (~/.foundry/keystores) by its filename + /// Use a keystore from the default keystores folder (~/.foundry/keystores) by its filename. #[arg( long = "account", visible_alias = "accounts", From 08a6409ab742f33b398de0fb5bc6c24800677e8c Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Wed, 2 Oct 2024 14:29:15 +0200 Subject: [PATCH 046/100] feat: gas snapshots over arbitrary sections (#8952) * update internal naming * further internals * deprecate cheats * update Solidity tests and add dedicated test for testing deprecated cheatcodes * clarify gas snapshots * fix build * final fixes * fix build * fix repro 6355 rename * add gas snapshot setup from #8755 * fix build + clippy warnings * fix cheatcodes * account for fixed CREATE / CALL gas cost * remove import * add stipend * recalculate after a - b setup * clear call_stipend, update tests * avoid double counting external calls * update cheatcodes, remove debug prints * enable assertions * clean up tests * clean up test names * remove snapshot directory on `forge clean` * do not remove all snapshots by default due to multiple test suites being able to be ran concurrently or sequentially + optimize gas snapshots check - skip if none were captured * handle edge case where we ask to compare but file does not exist, remove snapshot directory at a top level before test suites are ran * fix path issue when attempting removal * Update crates/cheatcodes/src/evm.rs Co-authored-by: Arsenii Kulikov * Update crates/cheatcodes/src/inspector.rs Co-authored-by: Arsenii Kulikov * refactor, apply recommended changes for last_snapshot_group, last_snapshot_name * remove gas snapshots from fuzz tests for now: this is largely due to it conflicting with the FORGE_SNAPSHOT_CHECK where it is highly likely that with different fuzzed input the gas measurement differs as well. In the future it would be an idea to capture the average gas * fix clippy * avoid setting to 0 unnecessarily * use if let Some * improve comments, clarify use of last_gas_used != 0 * fix merge conflict issue * fix arg ordering to address group naming regression * fix import * move snapshot name derivation to helper * only skip initial call w/ overhead, no special handling for call frames * add flare test * style nits + use helper method --------- Co-authored-by: Arsenii Kulikov --- .gitignore | 1 + crates/cheatcodes/assets/cheatcodes.json | 182 +++++++++++- crates/cheatcodes/spec/src/vm.rs | 45 ++- crates/cheatcodes/src/config.rs | 6 + crates/cheatcodes/src/evm.rs | 203 +++++++++++++ crates/cheatcodes/src/inspector.rs | 59 +++- crates/chisel/src/executor.rs | 1 + crates/common/src/fs.rs | 9 + crates/config/src/lib.rs | 10 + crates/evm/evm/src/executors/fuzz/mod.rs | 9 +- crates/forge/bin/cmd/test/mod.rs | 90 ++++++ crates/forge/src/multi_runner.rs | 1 + crates/forge/src/result.rs | 4 + crates/forge/tests/cli/config.rs | 1 + crates/script/src/lib.rs | 1 + testdata/cheats/Vm.sol | 9 + testdata/default/cheats/GasSnapshots.t.sol | 321 +++++++++++++++++++++ 17 files changed, 947 insertions(+), 5 deletions(-) create mode 100644 testdata/default/cheats/GasSnapshots.t.sol diff --git a/.gitignore b/.gitignore index 19f666e45..5b61e3202 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .DS_STORE /target out/ +snapshots/ out.json .idea .vscode diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index fbf33e500..9b6c67ddd 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -5560,7 +5560,7 @@ { "func": { "id": "lastCallGas", - "description": "Gets the gas used in the last call.", + "description": "Gets the gas used in the last call from the callee perspective.", "declaration": "function lastCallGas() external view returns (Gas memory gas);", "visibility": "external", "mutability": "view", @@ -8563,6 +8563,46 @@ }, "safety": "unsafe" }, + { + "func": { + "id": "snapshotGasLastCall_0", + "description": "Snapshot capture the gas usage of the last call by name from the callee perspective.", + "declaration": "function snapshotGasLastCall(string calldata name) external returns (uint256 gasUsed);", + "visibility": "external", + "mutability": "", + "signature": "snapshotGasLastCall(string)", + "selector": "0xdd9fca12", + "selectorBytes": [ + 221, + 159, + 202, + 18 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, + { + "func": { + "id": "snapshotGasLastCall_1", + "description": "Snapshot capture the gas usage of the last call by name in a group from the callee perspective.", + "declaration": "function snapshotGasLastCall(string calldata group, string calldata name) external returns (uint256 gasUsed);", + "visibility": "external", + "mutability": "", + "signature": "snapshotGasLastCall(string,string)", + "selector": "0x200c6772", + "selectorBytes": [ + 32, + 12, + 103, + 114 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, { "func": { "id": "snapshotState", @@ -8583,6 +8623,46 @@ "status": "stable", "safety": "unsafe" }, + { + "func": { + "id": "snapshotValue_0", + "description": "Snapshot capture an arbitrary numerical value by name.\nThe group name is derived from the contract name.", + "declaration": "function snapshotValue(string calldata name, uint256 value) external;", + "visibility": "external", + "mutability": "", + "signature": "snapshotValue(string,uint256)", + "selector": "0x51db805a", + "selectorBytes": [ + 81, + 219, + 128, + 90 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, + { + "func": { + "id": "snapshotValue_1", + "description": "Snapshot capture an arbitrary numerical value by name in a group.", + "declaration": "function snapshotValue(string calldata group, string calldata name, uint256 value) external;", + "visibility": "external", + "mutability": "", + "signature": "snapshotValue(string,string,uint256)", + "selector": "0x6d2b27d8", + "selectorBytes": [ + 109, + 43, + 39, + 216 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, { "func": { "id": "split", @@ -8723,6 +8803,46 @@ "status": "stable", "safety": "unsafe" }, + { + "func": { + "id": "startSnapshotGas_0", + "description": "Start a snapshot capture of the current gas usage by name.\nThe group name is derived from the contract name.", + "declaration": "function startSnapshotGas(string calldata name) external;", + "visibility": "external", + "mutability": "", + "signature": "startSnapshotGas(string)", + "selector": "0x3cad9d7b", + "selectorBytes": [ + 60, + 173, + 157, + 123 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, + { + "func": { + "id": "startSnapshotGas_1", + "description": "Start a snapshot capture of the current gas usage by name in a group.", + "declaration": "function startSnapshotGas(string calldata group, string calldata name) external;", + "visibility": "external", + "mutability": "", + "signature": "startSnapshotGas(string,string)", + "selector": "0x6cd0cc53", + "selectorBytes": [ + 108, + 208, + 204, + 83 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, { "func": { "id": "startStateDiffRecording", @@ -8843,6 +8963,66 @@ "status": "stable", "safety": "unsafe" }, + { + "func": { + "id": "stopSnapshotGas_0", + "description": "Stop the snapshot capture of the current gas by latest snapshot name, capturing the gas used since the start.", + "declaration": "function stopSnapshotGas() external returns (uint256 gasUsed);", + "visibility": "external", + "mutability": "", + "signature": "stopSnapshotGas()", + "selector": "0xf6402eda", + "selectorBytes": [ + 246, + 64, + 46, + 218 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, + { + "func": { + "id": "stopSnapshotGas_1", + "description": "Stop the snapshot capture of the current gas usage by name, capturing the gas used since the start.\nThe group name is derived from the contract name.", + "declaration": "function stopSnapshotGas(string calldata name) external returns (uint256 gasUsed);", + "visibility": "external", + "mutability": "", + "signature": "stopSnapshotGas(string)", + "selector": "0x773b2805", + "selectorBytes": [ + 119, + 59, + 40, + 5 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, + { + "func": { + "id": "stopSnapshotGas_2", + "description": "Stop the snapshot capture of the current gas usage by name in a group, capturing the gas used since the start.", + "declaration": "function stopSnapshotGas(string calldata group, string calldata name) external returns (uint256 gasUsed);", + "visibility": "external", + "mutability": "", + "signature": "stopSnapshotGas(string,string)", + "selector": "0x0c9db707", + "selectorBytes": [ + 12, + 157, + 183, + 7 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, { "func": { "id": "store", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 69293d1b6..7f27e1aa6 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -509,6 +509,49 @@ interface Vm { #[cheatcode(group = Evm, safety = Unsafe)] function readCallers() external returns (CallerMode callerMode, address msgSender, address txOrigin); + // ----- Arbitrary Snapshots ----- + + /// Snapshot capture an arbitrary numerical value by name. + /// The group name is derived from the contract name. + #[cheatcode(group = Evm, safety = Unsafe)] + function snapshotValue(string calldata name, uint256 value) external; + + /// Snapshot capture an arbitrary numerical value by name in a group. + #[cheatcode(group = Evm, safety = Unsafe)] + function snapshotValue(string calldata group, string calldata name, uint256 value) external; + + // -------- Gas Snapshots -------- + + /// Snapshot capture the gas usage of the last call by name from the callee perspective. + #[cheatcode(group = Evm, safety = Unsafe)] + function snapshotGasLastCall(string calldata name) external returns (uint256 gasUsed); + + /// Snapshot capture the gas usage of the last call by name in a group from the callee perspective. + #[cheatcode(group = Evm, safety = Unsafe)] + function snapshotGasLastCall(string calldata group, string calldata name) external returns (uint256 gasUsed); + + /// Start a snapshot capture of the current gas usage by name. + /// The group name is derived from the contract name. + #[cheatcode(group = Evm, safety = Unsafe)] + function startSnapshotGas(string calldata name) external; + + /// Start a snapshot capture of the current gas usage by name in a group. + #[cheatcode(group = Evm, safety = Unsafe)] + function startSnapshotGas(string calldata group, string calldata name) external; + + /// Stop the snapshot capture of the current gas by latest snapshot name, capturing the gas used since the start. + #[cheatcode(group = Evm, safety = Unsafe)] + function stopSnapshotGas() external returns (uint256 gasUsed); + + /// Stop the snapshot capture of the current gas usage by name, capturing the gas used since the start. + /// The group name is derived from the contract name. + #[cheatcode(group = Evm, safety = Unsafe)] + function stopSnapshotGas(string calldata name) external returns (uint256 gasUsed); + + /// Stop the snapshot capture of the current gas usage by name in a group, capturing the gas used since the start. + #[cheatcode(group = Evm, safety = Unsafe)] + function stopSnapshotGas(string calldata group, string calldata name) external returns (uint256 gasUsed); + // -------- State Snapshots -------- /// `snapshot` is being deprecated in favor of `snapshotState`. It will be removed in future versions. @@ -698,7 +741,7 @@ interface Vm { // -------- Gas Measurement -------- - /// Gets the gas used in the last call. + /// Gets the gas used in the last call from the callee perspective. #[cheatcode(group = Evm, safety = Safe)] function lastCallGas() external view returns (Gas memory gas); diff --git a/crates/cheatcodes/src/config.rs b/crates/cheatcodes/src/config.rs index 531784e16..c6a15f45d 100644 --- a/crates/cheatcodes/src/config.rs +++ b/crates/cheatcodes/src/config.rs @@ -49,6 +49,8 @@ pub struct CheatsConfig { /// If Some, `vm.getDeployedCode` invocations are validated to be in scope of this list. /// If None, no validation is performed. pub available_artifacts: Option, + /// Name of the script/test contract which is currently running. + pub running_contract: Option, /// Version of the script/test contract which is currently running. pub running_version: Option, /// Whether to enable legacy (non-reverting) assertions. @@ -64,6 +66,7 @@ impl CheatsConfig { evm_opts: EvmOpts, available_artifacts: Option, script_wallets: Option, + running_contract: Option, running_version: Option, ) -> Self { let mut allowed_paths = vec![config.root.0.clone()]; @@ -92,6 +95,7 @@ impl CheatsConfig { labels: config.labels.clone(), script_wallets, available_artifacts, + running_contract, running_version, assertions_revert: config.assertions_revert, seed: config.fuzz.seed, @@ -221,6 +225,7 @@ impl Default for CheatsConfig { labels: Default::default(), script_wallets: None, available_artifacts: Default::default(), + running_contract: Default::default(), running_version: Default::default(), assertions_revert: true, seed: None, @@ -240,6 +245,7 @@ mod tests { None, None, None, + None, ) } diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 7ed1ce1a4..7d4a23d61 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -52,6 +52,19 @@ impl RecordAccess { } } +/// Records the `snapshotGas*` cheatcodes. +#[derive(Clone, Debug)] +pub struct GasRecord { + /// The group name of the gas snapshot. + pub group: String, + /// The name of the gas snapshot. + pub name: String, + /// The total gas used in the gas snapshot. + pub gas_used: u64, + /// Depth at which the gas snapshot was taken. + pub depth: u64, +} + /// Records `deal` cheatcodes #[derive(Clone, Debug)] pub struct DealRecord { @@ -506,6 +519,80 @@ impl Cheatcode for readCallersCall { } } +impl Cheatcode for snapshotValue_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { name, value } = self; + inner_value_snapshot(ccx, None, Some(name.clone()), value.to_string()) + } +} + +impl Cheatcode for snapshotValue_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { group, name, value } = self; + inner_value_snapshot(ccx, Some(group.clone()), Some(name.clone()), value.to_string()) + } +} + +impl Cheatcode for snapshotGasLastCall_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { name } = self; + let Some(last_call_gas) = &ccx.state.gas_metering.last_call_gas else { + bail!("no external call was made yet"); + }; + inner_last_gas_snapshot(ccx, None, Some(name.clone()), last_call_gas.gasTotalUsed) + } +} + +impl Cheatcode for snapshotGasLastCall_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { name, group } = self; + let Some(last_call_gas) = &ccx.state.gas_metering.last_call_gas else { + bail!("no external call was made yet"); + }; + inner_last_gas_snapshot( + ccx, + Some(group.clone()), + Some(name.clone()), + last_call_gas.gasTotalUsed, + ) + } +} + +impl Cheatcode for startSnapshotGas_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { name } = self; + inner_start_gas_snapshot(ccx, None, Some(name.clone())) + } +} + +impl Cheatcode for startSnapshotGas_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { group, name } = self; + inner_start_gas_snapshot(ccx, Some(group.clone()), Some(name.clone())) + } +} + +impl Cheatcode for stopSnapshotGas_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self {} = self; + inner_stop_gas_snapshot(ccx, None, None) + } +} + +impl Cheatcode for stopSnapshotGas_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { name } = self; + inner_stop_gas_snapshot(ccx, None, Some(name.clone())) + } +} + +impl Cheatcode for stopSnapshotGas_2Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { group, name } = self; + inner_stop_gas_snapshot(ccx, Some(group.clone()), Some(name.clone())) + } +} + // Deprecated in favor of `snapshotStateCall` impl Cheatcode for snapshotCall { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { @@ -695,6 +782,122 @@ fn inner_delete_state_snapshots(ccx: &mut CheatsCtxt) -> Re Ok(Default::default()) } +fn inner_value_snapshot( + ccx: &mut CheatsCtxt, + group: Option, + name: Option, + value: String, +) -> Result { + let (group, name) = derive_snapshot_name(ccx, group, name); + + ccx.state.gas_snapshots.entry(group).or_default().insert(name, value); + + Ok(Default::default()) +} + +fn inner_last_gas_snapshot( + ccx: &mut CheatsCtxt, + group: Option, + name: Option, + value: u64, +) -> Result { + let (group, name) = derive_snapshot_name(ccx, group, name); + + ccx.state.gas_snapshots.entry(group).or_default().insert(name, value.to_string()); + + Ok(value.abi_encode()) +} + +fn inner_start_gas_snapshot( + ccx: &mut CheatsCtxt, + group: Option, + name: Option, +) -> Result { + // Revert if there is an active gas snapshot as we can only have one active snapshot at a time. + if ccx.state.gas_metering.active_gas_snapshot.is_some() { + let (group, name) = ccx.state.gas_metering.active_gas_snapshot.as_ref().unwrap().clone(); + bail!("gas snapshot was already started with group: {group} and name: {name}"); + } + + let (group, name) = derive_snapshot_name(ccx, group, name); + + ccx.state.gas_metering.gas_records.push(GasRecord { + group: group.clone(), + name: name.clone(), + gas_used: 0, + depth: ccx.ecx.journaled_state.depth(), + }); + + ccx.state.gas_metering.active_gas_snapshot = Some((group, name)); + + ccx.state.gas_metering.start(); + + Ok(Default::default()) +} + +fn inner_stop_gas_snapshot( + ccx: &mut CheatsCtxt, + group: Option, + name: Option, +) -> Result { + // If group and name are not provided, use the last snapshot group and name. + let (group, name) = group.zip(name).unwrap_or_else(|| { + let (group, name) = ccx.state.gas_metering.active_gas_snapshot.as_ref().unwrap().clone(); + (group, name) + }); + + if let Some(record) = ccx + .state + .gas_metering + .gas_records + .iter_mut() + .find(|record| record.group == group && record.name == name) + { + // Calculate the gas used since the snapshot was started. + // We subtract 171 from the gas used to account for gas used by the snapshot itself. + let value = record.gas_used.saturating_sub(171); + + ccx.state + .gas_snapshots + .entry(group.clone()) + .or_default() + .insert(name.clone(), value.to_string()); + + // Stop the gas metering. + ccx.state.gas_metering.stop(); + + // Remove the gas record. + ccx.state + .gas_metering + .gas_records + .retain(|record| record.group != group && record.name != name); + + // Clear last snapshot cache if we have an exact match. + if let Some((snapshot_group, snapshot_name)) = &ccx.state.gas_metering.active_gas_snapshot { + if snapshot_group == &group && snapshot_name == &name { + ccx.state.gas_metering.active_gas_snapshot = None; + } + } + + Ok(value.abi_encode()) + } else { + bail!("no gas snapshot was started with the name: {name} in group: {group}"); + } +} + +// Derives the snapshot group and name from the provided group and name or the running contract. +fn derive_snapshot_name( + ccx: &CheatsCtxt, + group: Option, + name: Option, +) -> (String, String) { + let group = group.unwrap_or_else(|| { + ccx.state.config.running_contract.clone().expect("expected running contract") + }); + let name = name.unwrap_or_else(|| "default".to_string()); + (group, name) +} + /// Reads the current caller information and returns the current [CallerMode], `msg.sender` and /// `tx.origin`. /// diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index f4d0ed821..3e80f1eaa 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -5,7 +5,7 @@ use crate::{ mapping::{self, MappingSlots}, mock::{MockCallDataContext, MockCallReturnData}, prank::Prank, - DealRecord, RecordAccess, + DealRecord, GasRecord, RecordAccess, }, inspector::utils::CommonCreateInput, script::{Broadcast, ScriptWallets}, @@ -232,15 +232,35 @@ pub struct GasMetering { pub touched: bool, /// True if gas metering should be reset to frame limit. pub reset: bool, - /// Stores frames paused gas. + /// Stores paused gas frames. pub paused_frames: Vec, + /// The group and name of the active snapshot. + pub active_gas_snapshot: Option<(String, String)>, + /// Cache of the amount of gas used in previous call. /// This is used by the `lastCallGas` cheatcode. pub last_call_gas: Option, + + /// True if gas recording is enabled. + pub recording: bool, + /// The gas used in the last frame. + pub last_gas_used: u64, + /// Gas records for the active snapshots. + pub gas_records: Vec, } impl GasMetering { + /// Start the gas recording. + pub fn start(&mut self) { + self.recording = true; + } + + /// Stop the gas recording. + pub fn stop(&mut self) { + self.recording = false; + } + /// Resume paused gas metering. pub fn resume(&mut self) { if self.paused { @@ -435,6 +455,10 @@ pub struct Cheatcodes { /// Gas metering state. pub gas_metering: GasMetering, + /// Contains gas snapshots made over the course of a test suite. + // **Note**: both must a BTreeMap to ensure the order of the keys is deterministic. + pub gas_snapshots: BTreeMap>, + /// Mapping slots. pub mapping_slots: Option>, @@ -494,6 +518,7 @@ impl Cheatcodes { serialized_jsons: Default::default(), eth_deals: Default::default(), gas_metering: Default::default(), + gas_snapshots: Default::default(), mapping_slots: Default::default(), pc: Default::default(), breakpoints: Default::default(), @@ -1161,6 +1186,11 @@ impl Inspector for Cheatcodes { if let Some(mapping_slots) = &mut self.mapping_slots { mapping::step(mapping_slots, interpreter); } + + // `snapshotGas*`: take a snapshot of the current gas. + if self.gas_metering.recording { + self.meter_gas_record(interpreter, ecx); + } } #[inline] @@ -1569,6 +1599,31 @@ impl Cheatcodes { } } + #[cold] + fn meter_gas_record( + &mut self, + interpreter: &mut Interpreter, + ecx: &mut EvmContext, + ) { + if matches!(interpreter.instruction_result, InstructionResult::Continue) { + self.gas_metering.gas_records.iter_mut().for_each(|record| { + if ecx.journaled_state.depth() == record.depth { + // Skip the first opcode of the first call frame as it includes the gas cost of + // creating the snapshot. + if self.gas_metering.last_gas_used != 0 { + let gas_diff = + interpreter.gas.spent().saturating_sub(self.gas_metering.last_gas_used); + record.gas_used = record.gas_used.saturating_add(gas_diff); + } + + // Update `last_gas_used` to the current spent gas for the next iteration to + // compare against. + self.gas_metering.last_gas_used = interpreter.gas.spent(); + } + }); + } + } + #[cold] fn meter_gas_end(&mut self, interpreter: &mut Interpreter) { // Remove recorded gas if we exit frame. diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index 3d5fce295..5e40862f8 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -308,6 +308,7 @@ impl SessionSource { self.config.evm_opts.clone(), None, None, + None, Some(self.solc.version.clone()), ) .into(), diff --git a/crates/common/src/fs.rs b/crates/common/src/fs.rs index 45c21eba6..71a62d13a 100644 --- a/crates/common/src/fs.rs +++ b/crates/common/src/fs.rs @@ -56,6 +56,15 @@ pub fn write_json_file(path: &Path, obj: &T) -> Result<()> { writer.flush().map_err(|e| FsPathError::write(e, path)) } +/// Writes the object as a pretty JSON object. +pub fn write_pretty_json_file(path: &Path, obj: &T) -> Result<()> { + let file = create_file(path)?; + let mut writer = BufWriter::new(file); + serde_json::to_writer_pretty(&mut writer, obj) + .map_err(|source| FsPathError::WriteJson { source, path: path.into() })?; + writer.flush().map_err(|e| FsPathError::write(e, path)) +} + /// Wrapper for `std::fs::write` pub fn write(path: impl AsRef, contents: impl AsRef<[u8]>) -> Result<()> { let path = path.as_ref(); diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 18352aab9..017341c3d 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -175,6 +175,8 @@ pub struct Config { pub cache: bool, /// where the cache is stored if enabled pub cache_path: PathBuf, + /// where the gas snapshots are stored + pub snapshots: PathBuf, /// where the broadcast logs are stored pub broadcast: PathBuf, /// additional solc allow paths for `--allow-paths` @@ -718,6 +720,7 @@ impl Config { self.out = p(&root, &self.out); self.broadcast = p(&root, &self.broadcast); self.cache_path = p(&root, &self.cache_path); + self.snapshots = p(&root, &self.snapshots); if let Some(build_info_path) = self.build_info_path { self.build_info_path = Some(p(&root, &build_info_path)); @@ -885,6 +888,12 @@ impl Config { remove_test_dir(&self.fuzz.failure_persist_dir); remove_test_dir(&self.invariant.failure_persist_dir); + // Remove snapshot directory. + let snapshot_dir = project.root().join(&self.snapshots); + if snapshot_dir.exists() { + let _ = fs::remove_dir_all(&snapshot_dir); + } + Ok(()) } @@ -2086,6 +2095,7 @@ impl Default for Config { cache: true, cache_path: "cache".into(), broadcast: "broadcast".into(), + snapshots: "snapshots".into(), allow_paths: vec![], include_paths: vec![], force: false, diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index cb57f2053..982e44ea9 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -17,7 +17,7 @@ use foundry_evm_fuzz::{ use foundry_evm_traces::SparsedTraceArena; use indicatif::ProgressBar; use proptest::test_runner::{TestCaseError, TestError, TestRunner}; -use std::cell::RefCell; +use std::{cell::RefCell, collections::BTreeMap}; mod types; pub use types::{CaseOutcome, CounterExampleOutcome, FuzzOutcome}; @@ -39,6 +39,8 @@ pub struct FuzzTestData { pub coverage: Option, // Stores logs for all fuzz cases pub logs: Vec, + // Stores gas snapshots for all fuzz cases + pub gas_snapshots: BTreeMap>, // Deprecated cheatcodes mapped to their replacements. pub deprecated_cheatcodes: HashMap<&'static str, Option<&'static str>>, } @@ -108,9 +110,11 @@ impl FuzzedExecutor { FuzzOutcome::Case(case) => { let mut data = execution_data.borrow_mut(); data.gas_by_case.push((case.case.gas, case.case.stipend)); + if data.first_case.is_none() { data.first_case.replace(case.case); } + if let Some(call_traces) = case.traces { if data.traces.len() == max_traces_to_collect { data.traces.pop(); @@ -118,14 +122,17 @@ impl FuzzedExecutor { data.traces.push(call_traces); data.breakpoints.replace(case.breakpoints); } + if show_logs { data.logs.extend(case.logs); } + // Collect and merge coverage if `forge snapshot` context. match &mut data.coverage { Some(prev) => prev.merge(case.coverage.unwrap()), opt => *opt = case.coverage, } + data.deprecated_cheatcodes = case.deprecated_cheatcodes; Ok(()) diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 6bf1ffd81..6d53a4756 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -310,6 +310,17 @@ impl TestArgs { let toml = config.get_config_path(); let profiles = get_available_profiles(toml)?; + // Remove the snapshots directory if it exists. + // This is to ensure that we don't have any stale snapshots. + // If `FORGE_SNAPSHOT_CHECK` is set, we don't remove the snapshots directory as it is + // required for comparison. + if std::env::var("FORGE_SNAPSHOT_CHECK").is_err() { + let snapshot_dir = project_root.join(&config.snapshots); + if snapshot_dir.exists() { + let _ = fs::remove_dir_all(project_root.join(&config.snapshots)); + } + } + let test_options: TestOptions = TestOptionsBuilder::default() .fuzz(config.fuzz.clone()) .invariant(config.invariant.clone()) @@ -546,6 +557,8 @@ impl TestArgs { .gas_report .then(|| GasReport::new(config.gas_reports.clone(), config.gas_reports_ignore.clone())); + let mut gas_snapshots = BTreeMap::>::new(); + let mut outcome = TestOutcome::empty(self.allow_failure); let mut any_test_failed = false; @@ -655,6 +668,83 @@ impl TestArgs { } } } + + // Collect and merge gas snapshots. + for (group, new_snapshots) in result.gas_snapshots.iter() { + gas_snapshots.entry(group.clone()).or_default().extend(new_snapshots.clone()); + } + } + + // Write gas snapshots to disk if any were collected. + if !gas_snapshots.is_empty() { + // Check for differences in gas snapshots if `FORGE_SNAPSHOT_CHECK` is set. + // Exiting early with code 1 if differences are found. + if std::env::var("FORGE_SNAPSHOT_CHECK").is_ok() { + let differences_found = gas_snapshots.clone().into_iter().fold( + false, + |mut found, (group, snapshots)| { + // If the snapshot file doesn't exist, we can't compare so we skip. + if !&config.snapshots.join(format!("{group}.json")).exists() { + return false; + } + + let previous_snapshots: BTreeMap = + fs::read_json_file(&config.snapshots.join(format!("{group}.json"))) + .expect("Failed to read snapshots from disk"); + + let diff: BTreeMap<_, _> = snapshots + .iter() + .filter_map(|(k, v)| { + previous_snapshots.get(k).and_then(|previous_snapshot| { + if previous_snapshot != v { + Some(( + k.clone(), + (previous_snapshot.clone(), v.clone()), + )) + } else { + None + } + }) + }) + .collect(); + + if !diff.is_empty() { + println!( + "{}", + format!("\n[{group}] Failed to match snapshots:").red().bold() + ); + + for (key, (previous_snapshot, snapshot)) in &diff { + println!( + "{}", + format!("- [{key}] {previous_snapshot} → {snapshot}").red() + ); + } + + found = true; + } + + found + }, + ); + + if differences_found { + println!(); + eyre::bail!("Snapshots differ from previous run"); + } + } + + // Create `snapshots` directory if it doesn't exist. + fs::create_dir_all(&config.snapshots)?; + + // Write gas snapshots to disk per group. + gas_snapshots.clone().into_iter().for_each(|(group, snapshots)| { + fs::write_pretty_json_file( + &config.snapshots.join(format!("{group}.json")), + &snapshots, + ) + .expect("Failed to write gas snapshots to disk"); + }); } // Print suite summary. diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 43aade0ff..802ad3884 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -243,6 +243,7 @@ impl MultiContractRunner { self.evm_opts.clone(), Some(self.known_contracts.clone()), None, + Some(artifact_id.name.clone()), Some(artifact_id.version.clone()), ); diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index 1ec829f6b..0e00bb5e2 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -412,6 +412,9 @@ pub struct TestResult { /// pc breakpoint char map pub breakpoints: Breakpoints, + /// Any captured gas snapshots along the test's execution which should be accumulated. + pub gas_snapshots: BTreeMap>, + /// Deprecated cheatcodes (mapped to their replacements, if any) used in current test. #[serde(skip)] pub deprecated_cheatcodes: HashMap<&'static str, Option<&'static str>>, @@ -531,6 +534,7 @@ impl TestResult { if let Some(cheatcodes) = raw_call_result.cheatcodes { self.breakpoints = cheatcodes.breakpoints; + self.gas_snapshots = cheatcodes.gas_snapshots; self.deprecated_cheatcodes = cheatcodes.deprecated; } diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index f8b1da523..5c0d62a32 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -37,6 +37,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { libs: vec!["lib-test".into()], cache: true, cache_path: "test-cache".into(), + snapshots: "snapshots".into(), broadcast: "broadcast".into(), force: true, evm_version: EvmVersion::Byzantium, diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 8231a5d54..94c028bb9 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -612,6 +612,7 @@ impl ScriptConfig { self.evm_opts.clone(), Some(known_contracts), Some(script_wallets), + Some(target.name), Some(target.version), ) .into(), diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 09099f540..335ce83d0 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -423,7 +423,11 @@ interface Vm { function skip(bool skipTest, string calldata reason) external; function sleep(uint256 duration) external; function snapshot() external returns (uint256 snapshotId); + function snapshotGasLastCall(string calldata name) external returns (uint256 gasUsed); + function snapshotGasLastCall(string calldata group, string calldata name) external returns (uint256 gasUsed); function snapshotState() external returns (uint256 snapshotId); + function snapshotValue(string calldata name, uint256 value) external; + function snapshotValue(string calldata group, string calldata name, uint256 value) external; function split(string calldata input, string calldata delimiter) external pure returns (string[] memory outputs); function startBroadcast() external; function startBroadcast(address signer) external; @@ -431,12 +435,17 @@ interface Vm { function startMappingRecording() external; function startPrank(address msgSender) external; function startPrank(address msgSender, address txOrigin) external; + function startSnapshotGas(string calldata name) external; + function startSnapshotGas(string calldata group, string calldata name) external; function startStateDiffRecording() external; function stopAndReturnStateDiff() external returns (AccountAccess[] memory accountAccesses); function stopBroadcast() external; function stopExpectSafeMemory() external; function stopMappingRecording() external; function stopPrank() external; + function stopSnapshotGas() external returns (uint256 gasUsed); + function stopSnapshotGas(string calldata name) external returns (uint256 gasUsed); + function stopSnapshotGas(string calldata group, string calldata name) external returns (uint256 gasUsed); function store(address target, bytes32 slot, bytes32 value) external; function toBase64URL(bytes calldata data) external pure returns (string memory); function toBase64URL(string calldata data) external pure returns (string memory); diff --git a/testdata/default/cheats/GasSnapshots.t.sol b/testdata/default/cheats/GasSnapshots.t.sol new file mode 100644 index 000000000..1e64a073d --- /dev/null +++ b/testdata/default/cheats/GasSnapshots.t.sol @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract GasSnapshotTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + uint256 public slot0; + Flare public flare; + + function setUp() public { + flare = new Flare(); + } + + function testSnapshotGasSectionExternal() public { + vm.startSnapshotGas("testAssertGasExternal"); + flare.run(1); + uint256 gasUsed = vm.stopSnapshotGas(); + + assertGt(gasUsed, 0); + } + + function testSnapshotGasSectionInternal() public { + vm.startSnapshotGas("testAssertGasInternalA"); + slot0 = 1; + vm.stopSnapshotGas(); + + vm.startSnapshotGas("testAssertGasInternalB"); + slot0 = 2; + vm.stopSnapshotGas(); + + vm.startSnapshotGas("testAssertGasInternalC"); + slot0 = 0; + vm.stopSnapshotGas(); + + vm.startSnapshotGas("testAssertGasInternalD"); + slot0 = 1; + vm.stopSnapshotGas(); + + vm.startSnapshotGas("testAssertGasInternalE"); + slot0 = 2; + vm.stopSnapshotGas(); + } + + // Writes to `GasSnapshotTest` group with custom names. + function testSnapshotValueDefaultGroupA() public { + uint256 a = 123; + uint256 b = 456; + uint256 c = 789; + + vm.snapshotValue("a", a); + vm.snapshotValue("b", b); + vm.snapshotValue("c", c); + } + + // Writes to same `GasSnapshotTest` group with custom names. + function testSnapshotValueDefaultGroupB() public { + uint256 d = 123; + uint256 e = 456; + uint256 f = 789; + + vm.snapshotValue("d", d); + vm.snapshotValue("e", e); + vm.snapshotValue("f", f); + } + + // Writes to `CustomGroup` group with custom names. + // Asserts that the order of the values is alphabetical. + function testSnapshotValueCustomGroupA() public { + uint256 o = 123; + uint256 i = 456; + uint256 q = 789; + + vm.snapshotValue("CustomGroup", "q", q); + vm.snapshotValue("CustomGroup", "i", i); + vm.snapshotValue("CustomGroup", "o", o); + } + + // Writes to `CustomGroup` group with custom names. + // Asserts that the order of the values is alphabetical. + function testSnapshotValueCustomGroupB() public { + uint256 x = 123; + uint256 e = 456; + uint256 z = 789; + + vm.snapshotValue("CustomGroup", "z", z); + vm.snapshotValue("CustomGroup", "x", x); + vm.snapshotValue("CustomGroup", "e", e); + } + + // Writes to `GasSnapshotTest` group with `testSnapshotGasDefault` name. + function testSnapshotGasSectionDefaultGroupStop() public { + vm.startSnapshotGas("testSnapshotGasSection"); + + flare.run(256); + + // vm.stopSnapshotGas() will use the last snapshot name. + uint256 gasUsed = vm.stopSnapshotGas(); + assertGt(gasUsed, 0); + } + + // Writes to `GasSnapshotTest` group with `testSnapshotGasCustom` name. + function testSnapshotGasSectionCustomGroupStop() public { + vm.startSnapshotGas("CustomGroup", "testSnapshotGasSection"); + + flare.run(256); + + // vm.stopSnapshotGas() will use the last snapshot name, even with custom group. + uint256 gasUsed = vm.stopSnapshotGas(); + assertGt(gasUsed, 0); + } + + // Writes to `GasSnapshotTest` group with `testSnapshotGasSection` name. + function testSnapshotGasSectionName() public { + vm.startSnapshotGas("testSnapshotGasSectionName"); + + flare.run(256); + + uint256 gasUsed = vm.stopSnapshotGas("testSnapshotGasSectionName"); + assertGt(gasUsed, 0); + } + + // Writes to `CustomGroup` group with `testSnapshotGasSection` name. + function testSnapshotGasSectionGroupName() public { + vm.startSnapshotGas("CustomGroup", "testSnapshotGasSectionGroupName"); + + flare.run(256); + + uint256 gasUsed = vm.stopSnapshotGas("CustomGroup", "testSnapshotGasSectionGroupName"); + assertGt(gasUsed, 0); + } + + // Writes to `GasSnapshotTest` group with `testSnapshotGas` name. + function testSnapshotGasLastCallName() public { + flare.run(1); + + uint256 gasUsed = vm.snapshotGasLastCall("testSnapshotGasLastCallName"); + assertGt(gasUsed, 0); + } + + // Writes to `CustomGroup` group with `testSnapshotGas` name. + function testSnapshotGasLastCallGroupName() public { + flare.run(1); + + uint256 gasUsed = vm.snapshotGasLastCall("CustomGroup", "testSnapshotGasLastCallGroupName"); + assertGt(gasUsed, 0); + } +} + +contract GasComparisonTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + uint256 public slot0; + uint256 public slot1; + + uint256 public cachedGas; + + function testGasComparisonEmpty() public { + // Start a cheatcode snapshot. + vm.startSnapshotGas("ComparisonGroup", "testGasComparisonEmptyA"); + uint256 a = vm.stopSnapshotGas(); + + // Start a comparitive Solidity snapshot. + _snapStart(); + uint256 b = _snapEnd(); + vm.snapshotValue("ComparisonGroup", "testGasComparisonEmptyB", b); + + assertEq(a, b); + } + + function testGasComparisonInternalCold() public { + // Start a cheatcode snapshot. + vm.startSnapshotGas("ComparisonGroup", "testGasComparisonInternalColdA"); + slot0 = 1; + uint256 a = vm.stopSnapshotGas(); + + // Start a comparitive Solidity snapshot. + _snapStart(); + slot1 = 1; + uint256 b = _snapEnd(); + vm.snapshotValue("ComparisonGroup", "testGasComparisonInternalColdB", b); + + vm.assertApproxEqAbs(a, b, 6); + } + + function testGasComparisonInternalWarm() public { + // Warm up the cache. + slot0 = 1; + + // Start a cheatcode snapshot. + vm.startSnapshotGas("ComparisonGroup", "testGasComparisonInternalWarmA"); + slot0 = 2; + uint256 a = vm.stopSnapshotGas(); + + // Start a comparitive Solidity snapshot. + _snapStart(); + slot0 = 3; + uint256 b = _snapEnd(); + vm.snapshotValue("ComparisonGroup", "testGasComparisonInternalWarmB", b); + + vm.assertApproxEqAbs(a, b, 6); + } + + function testGasComparisonExternal() public { + // Warm up the cache. + TargetB target = new TargetB(); + target.update(1); + + // Start a cheatcode snapshot. + vm.startSnapshotGas("ComparisonGroup", "testGasComparisonExternalA"); + target.update(2); + uint256 a = vm.stopSnapshotGas(); + + // Start a comparitive Solidity snapshot. + _snapStart(); + target.update(3); + uint256 b = _snapEnd(); + vm.snapshotValue("ComparisonGroup", "testGasComparisonExternalB", b); + + assertEq(a, b); + } + + function testGasComparisonCreate() public { + // Start a cheatcode snapshot. + vm.startSnapshotGas("ComparisonGroup", "testGasComparisonCreateA"); + new TargetC(); + uint256 a = vm.stopSnapshotGas(); + + // Start a comparitive Solidity snapshot. + _snapStart(); + new TargetC(); + uint256 b = _snapEnd(); + vm.snapshotValue("ComparisonGroup", "testGasComparisonCreateB", b); + + assertEq(a, b); + } + + function testGasComparisonNestedCalls() public { + // Warm up the cache. + TargetA target = new TargetA(); + target.update(1); + + // Start a cheatcode snapshot. + vm.startSnapshotGas("ComparisonGroup", "testGasComparisonNestedCallsA"); + target.update(2); + uint256 a = vm.stopSnapshotGas(); + + // Start a comparitive Solidity snapshot. + _snapStart(); + target.update(3); + uint256 b = _snapEnd(); + vm.snapshotValue("ComparisonGroup", "testGasComparisonNestedCallsB", b); + + assertEq(a, b); + } + + function testGasComparisonFlare() public { + // Warm up the cache. + Flare flare = new Flare(); + flare.run(1); + + // Start a cheatcode snapshot. + vm.startSnapshotGas("ComparisonGroup", "testGasComparisonFlareA"); + flare.run(256); + uint256 a = vm.stopSnapshotGas(); + + // Start a comparitive Solidity snapshot. + _snapStart(); + flare.run(256); + uint256 b = _snapEnd(); + vm.snapshotValue("ComparisonGroup", "testGasComparisonFlareB", b); + + assertEq(a, b); + } + + // Internal function to start a Solidity snapshot. + function _snapStart() internal { + cachedGas = 1; + cachedGas = gasleft(); + } + + // Internal function to end a Solidity snapshot. + function _snapEnd() internal returns (uint256 gasUsed) { + gasUsed = cachedGas - gasleft() - 138; + cachedGas = 2; + } +} + +contract Flare { + bytes32[] public data; + + function run(uint256 n_) public { + for (uint256 i = 0; i < n_; i++) { + data.push(keccak256(abi.encodePacked(i))); + } + } +} + +contract TargetA { + TargetB public target; + + constructor() { + target = new TargetB(); + } + + function update(uint256 x_) public { + target.update(x_); + } +} + +contract TargetB { + uint256 public x; + + function update(uint256 x_) public { + x = x_; + } +} + +contract TargetC {} From 57bcac09ef36e14655fc62691a021f588defd6fb Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Wed, 2 Oct 2024 18:59:57 +0530 Subject: [PATCH 047/100] chore(deps): bump alloy to 0.4.2 (#9000) * chore: bump alloy-core deps + revm * bump alloy to 0.4.0 * bump revm-inspectors + type casting to u128 * fix * fix * fix * bump foundry-fork-db * bump alloy * fix * gas related field to u64 * fmt * change gas fields types to u64 in DepositTx, TxEssentials --- Cargo.lock | 396 ++++++++++-------- Cargo.toml | 46 +- crates/anvil/core/src/eth/block.rs | 26 +- crates/anvil/core/src/eth/transaction/mod.rs | 35 +- .../core/src/eth/transaction/optimism.rs | 6 +- crates/anvil/src/cmd.rs | 2 +- crates/anvil/src/config.rs | 22 +- crates/anvil/src/eth/api.rs | 15 +- crates/anvil/src/eth/backend/executor.rs | 34 +- crates/anvil/src/eth/backend/fork.rs | 10 +- crates/anvil/src/eth/backend/mem/mod.rs | 34 +- crates/anvil/src/eth/backend/mem/storage.rs | 10 +- crates/anvil/src/eth/error.rs | 2 +- crates/anvil/src/eth/fees.rs | 24 +- crates/anvil/tests/it/anvil_api.rs | 4 +- crates/anvil/tests/it/api.rs | 41 +- crates/anvil/tests/it/eip4844.rs | 8 +- crates/anvil/tests/it/fork.rs | 12 +- crates/anvil/tests/it/gas.rs | 18 +- crates/anvil/tests/it/optimism.rs | 68 +-- crates/anvil/tests/it/transaction.rs | 22 +- crates/cheatcodes/src/inspector.rs | 12 +- crates/common/src/transactions.rs | 9 +- crates/evm/core/src/backend/mod.rs | 2 +- crates/evm/core/src/utils.rs | 2 +- crates/script/src/broadcast.rs | 2 +- crates/script/src/transaction.rs | 2 +- crates/verify/src/bytecode.rs | 4 +- 28 files changed, 457 insertions(+), 411 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7308606c9..6c48e16b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,28 +89,44 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "629b62e38d471cc15fea534eb7283d2f8a4e8bdb1811bcc5d66dda6cfce6fae1" dependencies = [ - "alloy-eips", + "alloy-eips 0.3.6", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.3.6", "c-kzg", "serde", ] +[[package]] +name = "alloy-consensus" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "705687d5bfd019fee57cf9e206b27b30a9a9617535d5590a02b171e813208f8e" +dependencies = [ + "alloy-eips 0.4.2", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.4.2", + "auto_impl", + "c-kzg", + "derive_more 1.0.0", + "serde", +] + [[package]] name = "alloy-contract" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eefe64fd344cffa9cf9e3435ec4e93e6e9c3481bc37269af988bf497faf4a6a" +checksum = "917f7d12cf3971dc8c11c9972f732b35ccb9aaaf5f28f2f87e9e6523bee3a8ad" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", "alloy-network", - "alloy-network-primitives", + "alloy-network-primitives 0.4.0", "alloy-primitives", "alloy-provider", "alloy-pubsub", - "alloy-rpc-types-eth", + "alloy-rpc-types-eth 0.4.0", "alloy-sol-types", "alloy-transport", "futures", @@ -154,9 +170,9 @@ dependencies = [ [[package]] name = "alloy-eip7702" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d319bb544ca6caeab58c39cea8921c55d924d4f68f2c60f24f914673f9a74a" +checksum = "ea59dc42102bc9a1905dc57901edc6dd48b9f38115df86c7d252acba70d71d04" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -176,7 +192,25 @@ dependencies = [ "alloy-eip7702", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.3.6", + "c-kzg", + "derive_more 1.0.0", + "once_cell", + "serde", + "sha2", +] + +[[package]] +name = "alloy-eips" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ffb906284a1e1f63c4607da2068c8197458a352d0b3e9796e67353d72a9be85" +dependencies = [ + "alloy-eip2930", + "alloy-eip7702", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.4.2", "c-kzg", "derive_more 1.0.0", "once_cell", @@ -186,12 +220,12 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a7a18afb0b318616b6b2b0e2e7ac5529d32a966c673b48091c9919e284e6aca" +checksum = "8429cf4554eed9b40feec7f4451113e76596086447550275e3def933faf47ce3" dependencies = [ "alloy-primitives", - "alloy-serde", + "alloy-serde 0.4.2", "serde", ] @@ -209,9 +243,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3c717b5298fad078cd3a418335b266eba91b511383ca9bd497f742d5975d5ab" +checksum = "f8fa8a1a3c4cbd221f2b8e3693aeb328fca79a757fe556ed08e47bbbc2a70db7" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -223,17 +257,17 @@ dependencies = [ [[package]] name = "alloy-network" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb3705ce7d8602132bcf5ac7a1dd293a42adc2f183abf5907c30ac535ceca049" +checksum = "85fa23a6a9d612b52e402c995f2d582c25165ec03ac6edf64c861a76bc5b87cd" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 0.4.2", + "alloy-eips 0.4.2", "alloy-json-rpc", - "alloy-network-primitives", + "alloy-network-primitives 0.4.0", "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-serde", + "alloy-rpc-types-eth 0.4.0", + "alloy-serde 0.4.2", "alloy-signer", "alloy-sol-types", "async-trait", @@ -248,9 +282,22 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94ad40869867ed2d9cd3842b1e800889e5b49e6b92da346e93862b4a741bedf3" dependencies = [ - "alloy-eips", + "alloy-eips 0.3.6", + "alloy-primitives", + "alloy-serde 0.3.6", + "serde", +] + +[[package]] +name = "alloy-network-primitives" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8416e4e9ceee8014d2f89fc3dde331da392b26d14226a0d5cbc207ae7799fb2f" +dependencies = [ + "alloy-consensus 0.4.2", + "alloy-eips 0.4.2", "alloy-primitives", - "alloy-serde", + "alloy-serde 0.4.2", "serde", ] @@ -287,20 +334,20 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927f708dd457ed63420400ee5f06945df9632d5d101851952056840426a10dc5" +checksum = "fcfaa4ffec0af04e3555686b8aacbcdf7d13638133a0672749209069750f78a6" dependencies = [ "alloy-chains", - "alloy-consensus", - "alloy-eips", + "alloy-consensus 0.4.2", + "alloy-eips 0.4.2", "alloy-json-rpc", "alloy-network", - "alloy-network-primitives", + "alloy-network-primitives 0.4.0", "alloy-primitives", "alloy-pubsub", "alloy-rpc-client", - "alloy-rpc-types-eth", + "alloy-rpc-types-eth 0.4.0", "alloy-rpc-types-trace", "alloy-rpc-types-txpool", "alloy-transport", @@ -326,9 +373,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d05f63677e210d758cd5d6d1ce10f20c980c3560ccfbe79ba1997791862a04f" +checksum = "f32cef487122ae75c91eb50154c70801d71fabdb976fec6c49e0af5e6486ab15" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -367,9 +414,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d82952dca71173813d4e5733e2c986d8b04aea9e0f3b0a576664c232ad050a5" +checksum = "370143ed581aace6e663342d21d209c6b2e34ee6142f7d6675adb518deeaf0dc" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -392,45 +439,47 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64333d639f2a0cf73491813c629a405744e16343a4bc5640931be707c345ecc5" +checksum = "9ffc534b7919e18f35e3aa1f507b6f3d9d92ec298463a9f6beaac112809d8d06" dependencies = [ + "alloy-primitives", "alloy-rpc-types-anvil", "alloy-rpc-types-engine", - "alloy-rpc-types-eth", + "alloy-rpc-types-eth 0.4.0", "alloy-rpc-types-trace", "alloy-rpc-types-txpool", - "alloy-serde", + "alloy-serde 0.4.2", "serde", ] [[package]] name = "alloy-rpc-types-anvil" -version = "0.3.6" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25cb45ad7c0930dd62eecf164d2afe4c3d2dd2c82af85680ad1f118e1e5cb83" +checksum = "142f6fb21ef1857b3d175dc16b73d67f4b70914e6898610da3c0b65a1281fe7b" dependencies = [ "alloy-primitives", - "alloy-serde", + "alloy-serde 0.4.2", "serde", ] [[package]] name = "alloy-rpc-types-engine" -version = "0.3.6" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1464c4dd646e1bdfde86ae65ce5ba168dbb29180b478011fe87117ae46b1629b" +checksum = "c032e9b725a990be03cc0ddd9fa73c21f61d1449b328083aa22fbfafb03eda1b" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 0.4.2", + "alloy-eips 0.4.2", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.4.2", "derive_more 1.0.0", "jsonwebtoken", "rand", "serde", + "strum", ] [[package]] @@ -439,12 +488,12 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83aa984386deda02482660aa31cb8ca1e63d533f1c31a52d7d181ac5ec68e9b8" dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-network-primitives", + "alloy-consensus 0.3.6", + "alloy-eips 0.3.6", + "alloy-network-primitives 0.3.6", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.3.6", "alloy-sol-types", "cfg-if", "derive_more 1.0.0", @@ -454,15 +503,34 @@ dependencies = [ "serde_json", ] +[[package]] +name = "alloy-rpc-types-eth" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e1f655dcd5e9ccf215cbffb69272698ef6b3ec76907e8937345f2a82ae04ed4" +dependencies = [ + "alloy-consensus 0.4.2", + "alloy-eips 0.4.2", + "alloy-network-primitives 0.4.0", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.4.2", + "alloy-sol-types", + "derive_more 1.0.0", + "itertools 0.13.0", + "serde", + "serde_json", +] + [[package]] name = "alloy-rpc-types-trace" -version = "0.3.6" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98db35cd42c90b484377e6bc44d95377a7a38a5ebee996e67754ac0446d542ab" +checksum = "6900c7d94528217465f6b619f03adb2eecc9682f9083d49ad7d40ec6eda0ed04" dependencies = [ "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-serde", + "alloy-rpc-types-eth 0.4.0", + "alloy-serde 0.4.2", "serde", "serde_json", "thiserror", @@ -470,13 +538,13 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "0.3.6" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bac37082c3b21283b3faf5cc0e08974272aee2f756ce1adeb26db56a5fce0d5" +checksum = "954781be5ca2e15db08d753712f494504a04771ee4296de1e834e65c105b8ec3" dependencies = [ "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-serde", + "alloy-rpc-types-eth 0.4.0", + "alloy-serde 0.4.2", "serde", ] @@ -491,11 +559,22 @@ dependencies = [ "serde_json", ] +[[package]] +name = "alloy-serde" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dff0ab1cdd43ca001e324dc27ee0e8606bd2161d6623c63e0e0b8c4dfc13600" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", +] + [[package]] name = "alloy-signer" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307324cca94354cd654d6713629f0383ec037e1ff9e3e3d547212471209860c0" +checksum = "2fd4e0ad79c81a27ca659be5d176ca12399141659fef2bcbfdc848da478f4504" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -509,11 +588,11 @@ dependencies = [ [[package]] name = "alloy-signer-aws" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076be69aa26a4c500919f1ad3847662aa6d1e9bc2995e263ed826b1546d1b990" +checksum = "417e19d9844350d11f7426d4920a5df777f8c2abbb7a70d9de6f1faf284db15b" dependencies = [ - "alloy-consensus", + "alloy-consensus 0.4.2", "alloy-network", "alloy-primitives", "alloy-signer", @@ -527,11 +606,11 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cabd79d4eb954a8c2ae7889a18e2466af186ae68376251cf58525239c156ec54" +checksum = "b6fd12ae28e8330766821058ed9a6a06ae4f9b12c776e8a7cfb16e3a954f508e" dependencies = [ - "alloy-consensus", + "alloy-consensus 0.4.2", "alloy-network", "alloy-primitives", "alloy-signer", @@ -545,11 +624,11 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3df66f5ddcc32d2070485dc702f5f5fb97cfbfa817f6e2e6bac16a4e32ed44c" +checksum = "d3a02400dea370022151f07581b73a836115c88ce4df350206653493bec024e2" dependencies = [ - "alloy-consensus", + "alloy-consensus 0.4.2", "alloy-dyn-abi", "alloy-network", "alloy-primitives", @@ -565,18 +644,17 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fabe917ab1778e760b4701628d1cae8e028ee9d52ac6307de4e1e9286ab6b5f" +checksum = "494e0a256f3e99f2426f994bcd1be312c02cb8f88260088dacb33a8b8936475f" dependencies = [ - "alloy-consensus", + "alloy-consensus 0.4.2", "alloy-network", "alloy-primitives", "alloy-signer", "async-trait", "coins-bip32", "coins-bip39", - "elliptic-curve", "eth-keystore", "k256", "rand", @@ -585,11 +663,11 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1068949eda889b2c052b29a6e8c7ea2ba16be6d1af83ad165fff2a4e4ad19fcd" +checksum = "e0d91ddee2390b002148128e47902893deba353eb1b818a3afb6c960ebf4e42c" dependencies = [ - "alloy-consensus", + "alloy-consensus 0.4.2", "alloy-network", "alloy-primitives", "alloy-signer", @@ -675,9 +753,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33616b2edf7454302a1d48084db185e52c309f73f6c10be99b0fe39354b3f1e9" +checksum = "2ac3e97dad3d31770db0fc89bd6a63b789fbae78963086733f960cf32c483904" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -694,9 +772,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a944f5310c690b62bbb3e7e5ce34527cbd36b2d18532a797af123271ce595a49" +checksum = "b367dcccada5b28987c2296717ee04b9a5637aacd78eacb1726ef211678b5212" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -709,9 +787,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fd8491249f74d16ec979b1f5672377b12ebb818e6056478ffa386954dbd350" +checksum = "b90cf9cde7f2fce617da52768ee28f522264b282d148384a4ca0ea85af04fa3a" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -730,9 +808,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9704761f6297fe482276bee7f77a93cb42bd541c2bd6c1c560b6f3a9ece672e" +checksum = "7153b88690de6a50bba81c11e1d706bc41dbb90126d607404d60b763f6a3947f" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -741,7 +819,7 @@ dependencies = [ "rustls 0.23.13", "serde_json", "tokio", - "tokio-tungstenite 0.23.1", + "tokio-tungstenite", "tracing", "ws_stream_wasm", ] @@ -849,10 +927,10 @@ name = "anvil" version = "0.2.0" dependencies = [ "alloy-chains", - "alloy-consensus", + "alloy-consensus 0.4.2", "alloy-contract", "alloy-dyn-abi", - "alloy-eips", + "alloy-eips 0.4.2", "alloy-genesis", "alloy-json-abi", "alloy-json-rpc", @@ -863,7 +941,7 @@ dependencies = [ "alloy-rlp", "alloy-rpc-client", "alloy-rpc-types", - "alloy-serde", + "alloy-serde 0.4.2", "alloy-signer", "alloy-signer-local", "alloy-sol-types", @@ -917,13 +995,13 @@ dependencies = [ name = "anvil-core" version = "0.2.0" dependencies = [ - "alloy-consensus", + "alloy-consensus 0.4.2", "alloy-dyn-abi", - "alloy-eips", + "alloy-eips 0.4.2", "alloy-primitives", "alloy-rlp", "alloy-rpc-types", - "alloy-serde", + "alloy-serde 0.4.2", "alloy-trie", "bytes", "foundry-common", @@ -1601,7 +1679,7 @@ dependencies = [ "sha1", "sync_wrapper 1.0.1", "tokio", - "tokio-tungstenite 0.24.0", + "tokio-tungstenite", "tower 0.5.1", "tower-layer", "tower-service", @@ -1916,9 +1994,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.22" +version = "1.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9540e661f81799159abee814118cc139a2004b3a3aa3ea37724a1b66530b90e0" +checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" dependencies = [ "shlex", ] @@ -3247,14 +3325,14 @@ name = "forge" version = "0.2.0" dependencies = [ "alloy-chains", - "alloy-consensus", + "alloy-consensus 0.4.2", "alloy-dyn-abi", "alloy-json-abi", "alloy-network", "alloy-primitives", "alloy-provider", "alloy-rpc-types", - "alloy-serde", + "alloy-serde 0.4.2", "alloy-signer", "alloy-signer-local", "alloy-sol-macro-expander", @@ -3377,15 +3455,15 @@ name = "forge-script" version = "0.2.0" dependencies = [ "alloy-chains", - "alloy-consensus", + "alloy-consensus 0.4.2", "alloy-dyn-abi", - "alloy-eips", + "alloy-eips 0.4.2", "alloy-json-abi", "alloy-network", "alloy-primitives", "alloy-provider", "alloy-rpc-types", - "alloy-serde", + "alloy-serde 0.4.2", "alloy-signer", "alloy-transport", "async-recursion", @@ -3498,7 +3576,7 @@ name = "foundry-cast" version = "0.2.0" dependencies = [ "alloy-chains", - "alloy-consensus", + "alloy-consensus 0.4.2", "alloy-contract", "alloy-dyn-abi", "alloy-json-abi", @@ -3508,7 +3586,7 @@ dependencies = [ "alloy-provider", "alloy-rlp", "alloy-rpc-types", - "alloy-serde", + "alloy-serde 0.4.2", "alloy-signer", "alloy-signer-local", "alloy-sol-types", @@ -3556,7 +3634,7 @@ dependencies = [ name = "foundry-cheatcodes" version = "0.2.0" dependencies = [ - "alloy-consensus", + "alloy-consensus 0.4.2", "alloy-dyn-abi", "alloy-genesis", "alloy-json-abi", @@ -3648,7 +3726,7 @@ dependencies = [ name = "foundry-common" version = "0.2.0" dependencies = [ - "alloy-consensus", + "alloy-consensus 0.4.2", "alloy-contract", "alloy-dyn-abi", "alloy-json-abi", @@ -3658,7 +3736,7 @@ dependencies = [ "alloy-pubsub", "alloy-rpc-client", "alloy-rpc-types", - "alloy-serde", + "alloy-serde 0.4.2", "alloy-sol-types", "alloy-transport", "alloy-transport-http", @@ -3694,11 +3772,11 @@ dependencies = [ name = "foundry-common-fmt" version = "0.2.0" dependencies = [ - "alloy-consensus", + "alloy-consensus 0.4.2", "alloy-dyn-abi", "alloy-primitives", "alloy-rpc-types", - "alloy-serde", + "alloy-serde 0.4.2", "chrono", "comfy-table", "foundry-macros", @@ -3921,7 +3999,7 @@ dependencies = [ "alloy-primitives", "alloy-provider", "alloy-rpc-types", - "alloy-serde", + "alloy-serde 0.4.2", "alloy-sol-types", "alloy-transport", "auto_impl", @@ -4017,14 +4095,14 @@ dependencies = [ [[package]] name = "foundry-fork-db" -version = "0.3.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fb76083f203e341deeb4a03f9cc86df096f09c723c42e44c25052c233ed87b4" +checksum = "2c8bdd63ecf8309c549d41a6167510da5053b13e9ab5456f06aff1070c0b642f" dependencies = [ "alloy-primitives", "alloy-provider", "alloy-rpc-types", - "alloy-serde", + "alloy-serde 0.4.2", "alloy-transport", "eyre", "futures", @@ -4084,7 +4162,7 @@ dependencies = [ name = "foundry-wallets" version = "0.2.0" dependencies = [ - "alloy-consensus", + "alloy-consensus 0.4.2", "alloy-dyn-abi", "alloy-network", "alloy-primitives", @@ -4797,9 +4875,9 @@ checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a" [[package]] name = "httparse" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -6013,11 +6091,11 @@ version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21aad1fbf80d2bcd7406880efc7ba109365f44bbb72896758ddcbfa46bf1592c" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 0.3.6", + "alloy-eips 0.3.6", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.3.6", "derive_more 1.0.0", "serde", "spin", @@ -6029,11 +6107,11 @@ version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e281fbfc2198b7c0c16457d6524f83d192662bc9f3df70f24c3038d4521616df" dependencies = [ - "alloy-eips", - "alloy-network-primitives", + "alloy-eips 0.3.6", + "alloy-network-primitives 0.3.6", "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-serde", + "alloy-rpc-types-eth 0.3.6", + "alloy-serde 0.3.6", "cfg-if", "hashbrown 0.14.5", "op-alloy-consensus", @@ -7099,9 +7177,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.7" +version = "0.12.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" dependencies = [ "async-compression", "base64 0.22.1", @@ -7127,8 +7205,8 @@ dependencies = [ "pin-project-lite", "quinn", "rustls 0.23.13", - "rustls-native-certs 0.7.3", - "rustls-pemfile 2.1.3", + "rustls-native-certs 0.8.0", + "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", "serde_json", @@ -7166,12 +7244,12 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.7.7" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8e3bae0d5c824da0ac883e2521c5e83870d6521eeeccd4ee54266aa3cc1a51" +checksum = "43c44af0bf801f48d25f7baf25cf72aff4c02d610f83b428175228162fef0246" dependencies = [ "alloy-primitives", - "alloy-rpc-types-eth", + "alloy-rpc-types-eth 0.4.0", "alloy-rpc-types-trace", "alloy-sol-types", "anstyle", @@ -7450,19 +7528,6 @@ dependencies = [ "security-framework", ] -[[package]] -name = "rustls-native-certs" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" -dependencies = [ - "openssl-probe", - "rustls-pemfile 2.1.3", - "rustls-pki-types", - "schannel", - "security-framework", -] - [[package]] name = "rustls-native-certs" version = "0.8.0" @@ -7470,7 +7535,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.3", + "rustls-pemfile 2.2.0", "rustls-pki-types", "schannel", "security-framework", @@ -7487,11 +7552,10 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.22.1", "rustls-pki-types", ] @@ -7622,9 +7686,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.1.18" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "215b1103f73e23e9cb6883072c1fb26ae55c09d42054654955c739e5418a7c96" +checksum = "836f1e0f4963ef5288b539b643b35e043e76a32d0f4e47e67febf69576527f50" dependencies = [ "sdd", ] @@ -8650,9 +8714,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.23.1" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" +checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" dependencies = [ "futures-util", "log", @@ -8660,22 +8724,10 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", - "tungstenite 0.23.0", + "tungstenite", "webpki-roots", ] -[[package]] -name = "tokio-tungstenite" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite 0.24.0", -] - [[package]] name = "tokio-util" version = "0.7.12" @@ -8755,7 +8807,7 @@ dependencies = [ "pin-project 1.1.5", "prost", "rustls-native-certs 0.8.0", - "rustls-pemfile 2.1.3", + "rustls-pemfile 2.2.0", "socket2", "tokio", "tokio-rustls 0.26.0", @@ -8980,26 +9032,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "tungstenite" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http 1.1.0", - "httparse", - "log", - "rand", - "rustls 0.23.13", - "rustls-pki-types", - "sha1", - "thiserror", - "utf-8", -] - [[package]] name = "tungstenite" version = "0.24.0" @@ -9013,6 +9045,8 @@ dependencies = [ "httparse", "log", "rand", + "rustls 0.23.13", + "rustls-pki-types", "sha1", "thiserror", "utf-8", diff --git a/Cargo.toml b/Cargo.toml index ae6ee1825..bdc2c8772 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -168,39 +168,39 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.7.3", default-features = false } foundry-compilers = { version = "0.11.3", default-features = false } -foundry-fork-db = "0.3.2" +foundry-fork-db = "0.4.0" solang-parser = "=0.3.3" ## revm revm = { version = "14.0.3", default-features = false } revm-primitives = { version = "10.0.0", default-features = false } -revm-inspectors = { version = "0.7.7", features = ["serde"] } +revm-inspectors = { version = "0.8.0", features = ["serde"] } ## ethers ethers-contract-abigen = { version = "2.0.14", default-features = false } ## alloy -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-consensus = { version = "0.4.2", default-features = false } +alloy-contract = { version = "0.4.2", default-features = false } +alloy-eips = { version = "0.4.2", default-features = false } +alloy-genesis = { version = "0.4.2", default-features = false } +alloy-json-rpc = { version = "0.4.2", default-features = false } +alloy-network = { version = "0.4.2", default-features = false } +alloy-provider = { version = "0.4.2", default-features = false } +alloy-pubsub = { version = "0.4.2", default-features = false } +alloy-rpc-client = { version = "0.4.2", default-features = false } +alloy-rpc-types = { version = "0.4.2", default-features = true } +alloy-serde = { version = "0.4.2", default-features = false } +alloy-signer = { version = "0.4.2", default-features = false } +alloy-signer-aws = { version = "0.4.2", default-features = false } +alloy-signer-gcp = { version = "0.4.2", default-features = false } +alloy-signer-ledger = { version = "0.4.2", default-features = false } +alloy-signer-local = { version = "0.4.2", default-features = false } +alloy-signer-trezor = { version = "0.4.2", default-features = false } +alloy-transport = { version = "0.4.2", default-features = false } +alloy-transport-http = { version = "0.4.2", default-features = false } +alloy-transport-ipc = { version = "0.4.2", default-features = false } +alloy-transport-ws = { version = "0.4.2", default-features = false } ## alloy-core alloy-dyn-abi = "0.8.5" diff --git a/crates/anvil/core/src/eth/block.rs b/crates/anvil/core/src/eth/block.rs index 1b0895dcd..337c5cfd8 100644 --- a/crates/anvil/core/src/eth/block.rs +++ b/crates/anvil/core/src/eth/block.rs @@ -90,16 +90,16 @@ pub struct PartialHeader { pub logs_bloom: Bloom, pub difficulty: U256, pub number: u64, - pub gas_limit: u128, - pub gas_used: u128, + pub gas_limit: u64, + pub gas_used: u64, pub timestamp: u64, pub extra_data: Bytes, pub mix_hash: B256, - pub blob_gas_used: Option, - pub excess_blob_gas: Option, + pub blob_gas_used: Option, + pub excess_blob_gas: Option, pub parent_beacon_block_root: Option, pub nonce: B64, - pub base_fee: Option, + pub base_fee: Option, } impl From
for PartialHeader { @@ -150,7 +150,7 @@ mod tests { difficulty: Default::default(), number: 124u64, gas_limit: Default::default(), - gas_used: 1337u128, + gas_used: 1337u64, timestamp: 0, extra_data: Default::default(), mix_hash: Default::default(), @@ -167,7 +167,7 @@ mod tests { let decoded: Header = Header::decode(&mut encoded.as_ref()).unwrap(); assert_eq!(header, decoded); - header.base_fee_per_gas = Some(12345u128); + header.base_fee_per_gas = Some(12345u64); let encoded = alloy_rlp::encode(&header); let decoded: Header = Header::decode(&mut encoded.as_ref()).unwrap(); @@ -190,8 +190,8 @@ mod tests { logs_bloom: Bloom::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(), difficulty: U256::from(2222), number: 0xd05u64, - gas_limit: 0x115cu128, - gas_used: 0x15b3u128, + gas_limit: 0x115cu64, + gas_used: 0x15b3u64, timestamp: 0x1a0au64, extra_data: hex::decode("7788").unwrap().into(), mix_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), @@ -223,8 +223,8 @@ mod tests { logs_bloom: <[u8; 256]>::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap().into(), difficulty: U256::from(2222), number: 0xd05u64, - gas_limit: 0x115cu128, - gas_used: 0x15b3u128, + gas_limit: 0x115cu64, + gas_used: 0x15b3u64, timestamp: 0x1a0au64, extra_data: hex::decode("7788").unwrap().into(), mix_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), @@ -255,8 +255,8 @@ mod tests { logs_bloom: Bloom::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(), difficulty: U256::from(0x020000), number: 1u64, - gas_limit: U256::from(0x016345785d8a0000u128).to::(), - gas_used: U256::from(0x015534).to::(), + gas_limit: U256::from(0x016345785d8a0000u128).to::(), + gas_used: U256::from(0x015534).to::(), timestamp: 0x079e, extra_data: hex::decode("42").unwrap().into(), mix_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index e03a862b8..2ed75db28 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -498,7 +498,7 @@ impl PendingTransaction { value: (*value), gas_price: U256::from(*gas_price), gas_priority_fee: None, - gas_limit: *gas_limit as u64, + gas_limit: *gas_limit, access_list: vec![], ..Default::default() } @@ -524,7 +524,7 @@ impl PendingTransaction { value: *value, gas_price: U256::from(*gas_price), gas_priority_fee: None, - gas_limit: *gas_limit as u64, + gas_limit: *gas_limit, access_list: access_list.clone().into(), ..Default::default() } @@ -551,7 +551,7 @@ impl PendingTransaction { value: *value, gas_price: U256::from(*max_fee_per_gas), gas_priority_fee: Some(U256::from(*max_priority_fee_per_gas)), - gas_limit: *gas_limit as u64, + gas_limit: *gas_limit, access_list: access_list.clone().into(), ..Default::default() } @@ -582,7 +582,7 @@ impl PendingTransaction { gas_priority_fee: Some(U256::from(*max_priority_fee_per_gas)), max_fee_per_blob_gas: Some(U256::from(*max_fee_per_blob_gas)), blob_hashes: blob_versioned_hashes.clone(), - gas_limit: *gas_limit as u64, + gas_limit: *gas_limit, access_list: access_list.clone().into(), ..Default::default() } @@ -609,7 +609,7 @@ impl PendingTransaction { value: *value, gas_price: U256::from(*max_fee_per_gas), gas_priority_fee: Some(U256::from(*max_priority_fee_per_gas)), - gas_limit: *gas_limit as u64, + gas_limit: *gas_limit, access_list: access_list.clone().into(), authorization_list: Some(authorization_list.clone().into()), ..Default::default() @@ -637,7 +637,7 @@ impl PendingTransaction { value: *value, gas_price: U256::ZERO, gas_priority_fee: None, - gas_limit: *gas_limit as u64, + gas_limit: { *gas_limit }, access_list: vec![], optimism: OptimismFields { source_hash: Some(*source_hash), @@ -716,7 +716,7 @@ impl TypedTransaction { } } - pub fn gas_limit(&self) -> u128 { + pub fn gas_limit(&self) -> u64 { match self { Self::Legacy(tx) => tx.tx().gas_limit, Self::EIP2930(tx) => tx.tx().gas_limit, @@ -766,20 +766,23 @@ impl TypedTransaction { /// and if the transaction is EIP-4844, the result of (total blob gas cost * max fee per blob /// gas) is also added pub fn max_cost(&self) -> u128 { - let mut max_cost = self.gas_limit().saturating_mul(self.gas_price()); + let mut max_cost = (self.gas_limit() as u128).saturating_mul(self.gas_price()); if self.is_eip4844() { max_cost = max_cost.saturating_add( - self.blob_gas().unwrap_or(0).mul(self.max_fee_per_blob_gas().unwrap_or(0)), + self.blob_gas() + .map(|g| g as u128) + .unwrap_or(0) + .mul(self.max_fee_per_blob_gas().unwrap_or(0)), ) } max_cost } - pub fn blob_gas(&self) -> Option { + pub fn blob_gas(&self) -> Option { match self { - Self::EIP4844(tx) => Some(tx.tx().tx().blob_gas() as u128), + Self::EIP4844(tx) => Some(tx.tx().tx().blob_gas()), _ => None, } } @@ -1256,7 +1259,7 @@ pub struct TransactionEssentials { pub kind: TxKind, pub input: Bytes, pub nonce: u64, - pub gas_limit: u128, + pub gas_limit: u64, pub gas_price: Option, pub max_fee_per_gas: Option, pub max_priority_fee_per_gas: Option, @@ -1279,7 +1282,7 @@ pub struct TransactionInfo { pub exit: InstructionResult, pub out: Option, pub nonce: u64, - pub gas_used: u128, + pub gas_used: u64, } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] @@ -1287,9 +1290,9 @@ pub struct TransactionInfo { pub struct DepositReceipt { #[serde(flatten)] pub inner: ReceiptWithBloom, - #[serde(default, with = "alloy_serde::num::u64_opt_via_ruint")] + #[serde(default, with = "alloy_serde::quantity::opt")] pub deposit_nonce: Option, - #[serde(default, with = "alloy_serde::num::u64_opt_via_ruint")] + #[serde(default, with = "alloy_serde::quantity::opt")] pub deposit_receipt_version: Option, } @@ -1690,7 +1693,7 @@ mod tests { let tx = TxLegacy { nonce: 2u64, gas_price: 1000000000u128, - gas_limit: 100000u128, + gas_limit: 100000, to: TxKind::Call(Address::from_slice( &hex::decode("d3e8763675e4c425df46cc3b5c0f6cbdac396046").unwrap()[..], )), diff --git a/crates/anvil/core/src/eth/transaction/optimism.rs b/crates/anvil/core/src/eth/transaction/optimism.rs index 6cc7bfa5a..170d67f1d 100644 --- a/crates/anvil/core/src/eth/transaction/optimism.rs +++ b/crates/anvil/core/src/eth/transaction/optimism.rs @@ -16,7 +16,7 @@ pub struct DepositTransactionRequest { pub kind: TxKind, pub mint: U256, pub value: U256, - pub gas_limit: u128, + pub gas_limit: u64, pub is_system_tx: bool, pub input: Bytes, } @@ -178,7 +178,7 @@ impl Transaction for DepositTransactionRequest { } /// Get `gas_limit`. - fn gas_limit(&self) -> u128 { + fn gas_limit(&self) -> u64 { self.gas_limit } @@ -281,7 +281,7 @@ pub struct DepositTransaction { pub kind: TxKind, pub mint: U256, pub value: U256, - pub gas_limit: u128, + pub gas_limit: u64, pub is_system_tx: bool, pub input: Bytes, } diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 3b7778a72..149b9c863 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -545,7 +545,7 @@ pub struct AnvilEvmArgs { value_name = "FEE", help_heading = "Environment config" )] - pub block_base_fee_per_gas: Option, + pub block_base_fee_per_gas: Option, /// The chain ID. #[arg(long, alias = "chain", help_heading = "Environment config")] diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index f3dbf21f6..189cf5175 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -98,7 +98,7 @@ pub struct NodeConfig { /// Default gas price for all txs pub gas_price: Option, /// Default base fee - pub base_fee: Option, + pub base_fee: Option, /// Default blob excess gas and price pub blob_excess_gas_and_price: Option, /// The hardfork to use @@ -474,9 +474,9 @@ impl NodeConfig { self } /// Returns the base fee to use - pub fn get_base_fee(&self) -> u128 { + pub fn get_base_fee(&self) -> u64 { self.base_fee - .or_else(|| self.genesis.as_ref().and_then(|g| g.base_fee_per_gas)) + .or_else(|| self.genesis.as_ref().and_then(|g| g.base_fee_per_gas.map(|g| g as u64))) .unwrap_or(INITIAL_BASE_FEE) } @@ -618,7 +618,7 @@ impl NodeConfig { /// Sets the base fee #[must_use] - pub fn with_base_fee(mut self, base_fee: Option) -> Self { + pub fn with_base_fee(mut self, base_fee: Option) -> Self { self.base_fee = base_fee; self } @@ -1183,7 +1183,7 @@ latest block number: {latest_block}" // this is the base fee of the current block, but we need the base fee of // the next block let next_block_base_fee = fees.get_next_block_base_fee_per_gas( - block.header.gas_used, + block.header.gas_used as u128, gas_limit, block.header.base_fee_per_gas.unwrap_or_default(), ); @@ -1195,9 +1195,9 @@ latest block number: {latest_block}" (block.header.excess_blob_gas, block.header.blob_gas_used) { env.block.blob_excess_gas_and_price = - Some(BlobExcessGasAndPrice::new(blob_excess_gas as u64)); - let next_block_blob_excess_gas = - fees.get_next_block_blob_excess_gas(blob_excess_gas, blob_gas_used); + Some(BlobExcessGasAndPrice::new(blob_excess_gas)); + let next_block_blob_excess_gas = fees + .get_next_block_blob_excess_gas(blob_excess_gas as u128, blob_gas_used as u128); fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new( next_block_blob_excess_gas, )); @@ -1257,13 +1257,13 @@ latest block number: {latest_block}" chain_id, override_chain_id, timestamp: block.header.timestamp, - base_fee: block.header.base_fee_per_gas, + base_fee: block.header.base_fee_per_gas.map(|g| g as u128), timeout: self.fork_request_timeout, retries: self.fork_request_retries, backoff: self.fork_retry_backoff, compute_units_per_second: self.compute_units_per_second, total_difficulty: block.header.total_difficulty.unwrap_or_default(), - blob_gas_used: block.header.blob_gas_used, + blob_gas_used: block.header.blob_gas_used.map(|g| g as u128), blob_excess_gas_and_price: env.block.blob_excess_gas_and_price.clone(), force_transactions, }; @@ -1284,7 +1284,7 @@ latest block number: {latest_block}" if let Some(gas_limit) = self.gas_limit { return gas_limit; } else if block.header.gas_limit > 0 { - return block.header.gas_limit; + return block.header.gas_limit as u128; } } diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 85d1aceaf..c8fd497a3 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -592,7 +592,7 @@ impl EthApi { /// Returns the current gas price pub fn gas_price(&self) -> u128 { if self.backend.is_eip1559() { - self.backend.base_fee().saturating_add(self.lowest_suggestion_tip()) + (self.backend.base_fee() as u128).saturating_add(self.lowest_suggestion_tip()) } else { self.backend.fees().raw_gas_price() } @@ -1405,7 +1405,7 @@ impl EthApi { // The spec states that `base_fee_per_gas` "[..] includes the next block after the // newest of the returned range, because this value can be derived from the // newest block" - response.base_fee_per_gas.push(self.backend.fees().base_fee()); + response.base_fee_per_gas.push(self.backend.fees().base_fee() as u128); // Same goes for the `base_fee_per_blob_gas`: // > [..] includes the next block after the newest of the returned range, because this @@ -2302,7 +2302,7 @@ impl EthApi { let to = tx.to(); let gas_price = tx.gas_price(); let value = tx.value(); - let gas = tx.gas_limit(); + let gas = tx.gas_limit() as u128; TxpoolInspectSummary { to, value, gas, gas_price } } @@ -2485,7 +2485,8 @@ impl EthApi { // get the highest possible gas limit, either the request's set value or the currently // configured gas limit - let mut highest_gas_limit = request.gas.unwrap_or(block_env.gas_limit.to()); + let mut highest_gas_limit = + request.gas.map_or(block_env.gas_limit.to::(), |g| g as u128); let gas_price = fees.gas_price.unwrap_or_default(); // If we have non-zero gas price, cap gas limit by sender balance @@ -2507,7 +2508,7 @@ impl EthApi { } let mut call_to_estimate = request.clone(); - call_to_estimate.gas = Some(highest_gas_limit); + call_to_estimate.gas = Some(highest_gas_limit as u64); // execute the call without writing to db let ethres = @@ -2541,7 +2542,7 @@ impl EthApi { // Binary search for the ideal gas limit while (highest_gas_limit - lowest_gas_limit) > 1 { - request.gas = Some(mid_gas_limit); + request.gas = Some(mid_gas_limit as u64); let ethres = self.backend.call_with_state( &state, request.clone(), @@ -2688,7 +2689,7 @@ impl EthApi { let max_fee_per_blob_gas = request.max_fee_per_blob_gas; let gas_price = request.gas_price; - let gas_limit = request.gas.unwrap_or(self.backend.gas_limit()); + let gas_limit = request.gas.unwrap_or(self.backend.gas_limit() as u64); let request = match transaction_request_to_typed(request) { Some(TypedTransactionRequest::Legacy(mut m)) => { diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index caa042bf4..d92d60a87 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -38,7 +38,7 @@ pub struct ExecutedTransaction { transaction: Arc, exit_reason: InstructionResult, out: Option, - gas_used: u128, + gas_used: u64, logs: Vec, traces: Vec, nonce: u64, @@ -48,7 +48,7 @@ pub struct ExecutedTransaction { impl ExecutedTransaction { /// Creates the receipt for the transaction - fn create_receipt(&self, cumulative_gas_used: &mut u128) -> TypedReceipt { + fn create_receipt(&self, cumulative_gas_used: &mut u64) -> TypedReceipt { let logs = self.logs.clone(); *cumulative_gas_used = cumulative_gas_used.saturating_add(self.gas_used); @@ -56,7 +56,7 @@ impl ExecutedTransaction { let status_code = u8::from(self.exit_reason as u8 <= InstructionResult::SelfDestruct as u8); let receipt_with_bloom: ReceiptWithBloom = Receipt { status: (status_code == 1).into(), - cumulative_gas_used: *cumulative_gas_used, + cumulative_gas_used: *cumulative_gas_used as u128, logs, } .into(); @@ -101,9 +101,9 @@ pub struct TransactionExecutor<'a, Db: ?Sized, V: TransactionValidator> { pub cfg_env: CfgEnvWithHandlerCfg, pub parent_hash: B256, /// Cumulative gas used by all executed transactions - pub gas_used: u128, + pub gas_used: u64, /// Cumulative blob gas used by all executed transactions - pub blob_gas_used: u128, + pub blob_gas_used: u64, pub enable_steps_tracing: bool, pub alphanet: bool, pub print_logs: bool, @@ -118,24 +118,24 @@ impl<'a, DB: Db + ?Sized, V: TransactionValidator> TransactionExecutor<'a, DB, V let mut transaction_infos = Vec::new(); let mut receipts = Vec::new(); let mut bloom = Bloom::default(); - let mut cumulative_gas_used: u128 = 0; + let mut cumulative_gas_used = 0u64; let mut invalid = Vec::new(); let mut included = Vec::new(); - let gas_limit = self.block_env.gas_limit.to::(); + let gas_limit = self.block_env.gas_limit.to::(); let parent_hash = self.parent_hash; let block_number = self.block_env.number.to::(); let difficulty = self.block_env.difficulty; let beneficiary = self.block_env.coinbase; let timestamp = self.block_env.timestamp.to::(); let base_fee = if self.cfg_env.handler_cfg.spec_id.is_enabled_in(SpecId::LONDON) { - Some(self.block_env.basefee.to::()) + Some(self.block_env.basefee.to::()) } else { None }; let is_cancun = self.cfg_env.handler_cfg.spec_id >= SpecId::CANCUN; let excess_blob_gas = if is_cancun { self.block_env.get_blob_excess_gas() } else { None }; - let mut cumulative_blob_gas_used = if is_cancun { Some(0u128) } else { None }; + let mut cumulative_blob_gas_used = if is_cancun { Some(0u64) } else { None }; for tx in self.into_iter() { let tx = match tx { @@ -172,7 +172,7 @@ impl<'a, DB: Db + ?Sized, V: TransactionValidator> TransactionExecutor<'a, DB, V .blob_gas() .unwrap_or(0); cumulative_blob_gas_used = - Some(cumulative_blob_gas_used.unwrap_or(0u128).saturating_add(tx_blob_gas)); + Some(cumulative_blob_gas_used.unwrap_or(0u64).saturating_add(tx_blob_gas)); } let receipt = tx.create_receipt(&mut cumulative_gas_used); @@ -228,7 +228,7 @@ impl<'a, DB: Db + ?Sized, V: TransactionValidator> TransactionExecutor<'a, DB, V base_fee, parent_beacon_block_root: Default::default(), blob_gas_used: cumulative_blob_gas_used, - excess_blob_gas: excess_blob_gas.map(|g| g as u128), + excess_blob_gas, }; let block = Block::new(partial_header, transactions.clone(), ommers); @@ -277,16 +277,16 @@ impl<'a, 'b, DB: Db + ?Sized, V: TransactionValidator> Iterator let env = self.env_for(&transaction.pending_transaction); // check that we comply with the block's gas limit, if not disabled - let max_gas = self.gas_used.saturating_add(env.tx.gas_limit as u128); - if !env.cfg.disable_block_gas_limit && max_gas > env.block.gas_limit.to::() { + let max_gas = self.gas_used.saturating_add(env.tx.gas_limit); + if !env.cfg.disable_block_gas_limit && max_gas > env.block.gas_limit.to::() { return Some(TransactionExecutionOutcome::Exhausted(transaction)) } // check that we comply with the block's blob gas limit let max_blob_gas = self.blob_gas_used.saturating_add( - transaction.pending_transaction.transaction.transaction.blob_gas().unwrap_or(0u128), + transaction.pending_transaction.transaction.transaction.blob_gas().unwrap_or(0), ); - if max_blob_gas > MAX_BLOB_GAS_PER_BLOCK as u128 { + if max_blob_gas > MAX_BLOB_GAS_PER_BLOCK { return Some(TransactionExecutionOutcome::BlobGasExhausted(transaction)) } @@ -364,7 +364,7 @@ impl<'a, 'b, DB: Db + ?Sized, V: TransactionValidator> Iterator trace!(target: "backend", ?exit_reason, ?gas_used, "[{:?}] executed with out={:?}", transaction.hash(), out); // Track the total gas used for total gas per block checks - self.gas_used = self.gas_used.saturating_add(gas_used as u128); + self.gas_used = self.gas_used.saturating_add(gas_used); // Track the total blob gas used for total blob gas per blob checks if let Some(blob_gas) = transaction.pending_transaction.transaction.transaction.blob_gas() { @@ -377,7 +377,7 @@ impl<'a, 'b, DB: Db + ?Sized, V: TransactionValidator> Iterator transaction, exit_reason, out, - gas_used: gas_used as u128, + gas_used, logs: logs.unwrap_or_default(), traces: inspector.tracer.map(|t| t.into_traces().into_nodes()).unwrap_or_default(), nonce, diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index ea41b85a9..7d887f697 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -92,7 +92,13 @@ impl ClientFork { let total_difficulty = block.header.total_difficulty.unwrap_or_default(); let number = block.header.number; - self.config.write().update_block(number, block_hash, timestamp, base_fee, total_difficulty); + self.config.write().update_block( + number, + block_hash, + timestamp, + base_fee.map(|g| g as u128), + total_difficulty, + ); self.clear_cached_storage(); @@ -202,7 +208,7 @@ impl ClientFork { let block = block.unwrap_or_default(); let res = self.provider().estimate_gas(request).block(block.into()).await?; - Ok(res) + Ok(res as u128) } /// Sends `eth_createAccessList` diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 83d8c1871..ea36696f2 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -460,7 +460,7 @@ impl Backend { // 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_used as u128, gas_limit, fork_block.header.base_fee_per_gas.unwrap_or_default(), ); @@ -665,7 +665,7 @@ impl Backend { } /// Returns the current base fee - pub fn base_fee(&self) -> u128 { + pub fn base_fee(&self) -> u64 { self.fees.base_fee() } @@ -674,7 +674,7 @@ impl Backend { } /// Sets the current basefee - pub fn set_base_fee(&self, basefee: u128) { + pub fn set_base_fee(&self, basefee: u64) { self.fees.set_base_fee(basefee) } @@ -1121,13 +1121,13 @@ impl Backend { (outcome, header, block_hash) }; let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas( - header.gas_used, - header.gas_limit, + header.gas_used as u128, + header.gas_limit as u128, header.base_fee_per_gas.unwrap_or_default(), ); let next_block_excess_blob_gas = self.fees.get_next_block_blob_excess_gas( - header.excess_blob_gas.unwrap_or_default(), - header.blob_gas_used.unwrap_or_default(), + header.excess_blob_gas.map(|g| g as u128).unwrap_or_default(), + header.blob_gas_used.map(|g| g as u128).unwrap_or_default(), ); // update next base fee @@ -1231,7 +1231,7 @@ impl Backend { env.tx = TxEnv { caller, - gas_limit: gas_limit as u64, + gas_limit, gas_price: U256::from(gas_price), gas_priority_fee: max_priority_fee_per_gas.map(U256::from), max_fee_per_blob_gas: max_fee_per_blob_gas @@ -2230,7 +2230,7 @@ impl Backend { // Cancun specific let excess_blob_gas = block.header.excess_blob_gas; - let blob_gas_price = calc_blob_gasprice(excess_blob_gas.map_or(0, |g| g as u64)); + let blob_gas_price = calc_blob_gasprice(excess_blob_gas.unwrap_or_default()); let blob_gas_used = transaction.blob_gas(); let effective_gas_price = match transaction.transaction { @@ -2239,17 +2239,17 @@ impl Backend { TypedTransaction::EIP1559(t) => block .header .base_fee_per_gas - .unwrap_or_else(|| self.base_fee()) + .map_or(self.base_fee() as u128, |g| g as u128) .saturating_add(t.tx().max_priority_fee_per_gas), TypedTransaction::EIP4844(t) => block .header .base_fee_per_gas - .unwrap_or_else(|| self.base_fee()) + .map_or(self.base_fee() as u128, |g| g as u128) .saturating_add(t.tx().tx().max_priority_fee_per_gas), TypedTransaction::EIP7702(t) => block .header .base_fee_per_gas - .unwrap_or_else(|| self.base_fee()) + .map_or(self.base_fee() as u128, |g| g as u128) .saturating_add(t.tx().max_priority_fee_per_gas), TypedTransaction::Deposit(_) => 0_u128, }; @@ -2298,7 +2298,7 @@ impl Backend { transaction_hash: info.transaction_hash, transaction_index: Some(info.transaction_index), block_number: Some(block.header.number), - gas_used: info.gas_used, + gas_used: info.gas_used as u128, contract_address: info.contract_address, effective_gas_price, block_hash: Some(block_hash), @@ -2306,7 +2306,7 @@ impl Backend { to: info.to, state_root: Some(block.header.state_root), blob_gas_price: Some(blob_gas_price), - blob_gas_used, + blob_gas_used: blob_gas_used.map(|g| g as u128), authorization_list: None, }; @@ -2634,7 +2634,7 @@ impl TransactionValidator for Backend { } } - if tx.gas_limit() < MIN_TRANSACTION_GAS { + if tx.gas_limit() < MIN_TRANSACTION_GAS as u64 { warn!(target: "backend", "[{:?}] gas too low", tx.hash()); return Err(InvalidTransactionError::GasTooLow); } @@ -2760,7 +2760,7 @@ pub fn transaction_build( eth_transaction: MaybeImpersonatedTransaction, block: Option<&Block>, info: Option, - base_fee: Option, + base_fee: Option, ) -> WithOtherFields { let mut transaction: Transaction = eth_transaction.clone().into(); if info.is_some() && transaction.transaction_type == Some(0x7E) { @@ -2774,7 +2774,7 @@ pub fn transaction_build( } else { // if transaction is already mined, gas price is considered base fee + priority fee: the // effective gas price. - let base_fee = base_fee.unwrap_or(0u128); + let base_fee = base_fee.map_or(0u128, |g| g as u128); let max_priority_fee_per_gas = transaction.max_priority_fee_per_gas.unwrap_or(0); transaction.gas_price = Some(base_fee.saturating_add(max_priority_fee_per_gas)); } diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index abc520c3b..29ae47207 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -262,16 +262,16 @@ pub struct BlockchainStorage { impl BlockchainStorage { /// Creates a new storage with a genesis block - pub fn new(env: &Env, base_fee: Option, timestamp: u64) -> Self { + pub fn new(env: &Env, base_fee: Option, timestamp: u64) -> Self { // create a dummy genesis block let partial_header = PartialHeader { timestamp, base_fee, - gas_limit: env.block.gas_limit.to::(), + gas_limit: env.block.gas_limit.to::(), beneficiary: env.block.coinbase, difficulty: env.block.difficulty, blob_gas_used: env.block.blob_excess_gas_and_price.as_ref().map(|_| 0), - excess_blob_gas: env.block.get_blob_excess_gas().map(|v| v as u128), + excess_blob_gas: env.block.get_blob_excess_gas(), ..Default::default() }; let block = Block::new::(partial_header, vec![], vec![]); @@ -423,7 +423,7 @@ pub struct Blockchain { impl Blockchain { /// Creates a new storage with a genesis block - pub fn new(env: &Env, base_fee: Option, timestamp: u64) -> Self { + pub fn new(env: &Env, base_fee: Option, timestamp: u64) -> Self { Self { storage: Arc::new(RwLock::new(BlockchainStorage::new(env, base_fee, timestamp))) } } @@ -711,7 +711,7 @@ mod tests { load_storage.load_transactions(serialized_transactions); let loaded_block = load_storage.blocks.get(&block_hash).unwrap(); - assert_eq!(loaded_block.header.gas_limit, partial_header.gas_limit); + assert_eq!(loaded_block.header.gas_limit, { partial_header.gas_limit }); let loaded_tx = loaded_block.transactions.first().unwrap(); assert_eq!(loaded_tx, &tx); } diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index ada9e6c53..31d0521bb 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -388,7 +388,7 @@ impl ToRpcResponseResult for Result { match err { TransportError::ErrorResp(err) => RpcError { code: ErrorCode::from(err.code), - message: err.message.into(), + message: err.message, data: err.data.and_then(|data| serde_json::to_value(data).ok()), }, err => RpcError::internal_error_with(format!("Fork Error: {err:?}")), diff --git a/crates/anvil/src/eth/fees.rs b/crates/anvil/src/eth/fees.rs index cb1b8508f..72d66b113 100644 --- a/crates/anvil/src/eth/fees.rs +++ b/crates/anvil/src/eth/fees.rs @@ -24,7 +24,7 @@ use std::{ pub const MAX_FEE_HISTORY_CACHE_SIZE: u64 = 2048u64; /// Initial base fee for EIP-1559 blocks. -pub const INITIAL_BASE_FEE: u128 = 1_000_000_000; +pub const INITIAL_BASE_FEE: u64 = 1_000_000_000; /// Initial default gas price for the first block pub const INITIAL_GAS_PRICE: u128 = 1_875_000_000; @@ -47,7 +47,7 @@ pub struct FeeManager { /// Tracks the base fee for the next block post London /// /// This value will be updated after a new block was mined - base_fee: Arc>, + base_fee: Arc>, /// Tracks the excess blob gas, and the base fee, for the next block post Cancun /// /// This value will be updated after a new block was mined @@ -62,7 +62,7 @@ pub struct FeeManager { impl FeeManager { pub fn new( spec_id: SpecId, - base_fee: u128, + base_fee: u64, gas_price: u128, blob_excess_gas_and_price: BlobExcessGasAndPrice, ) -> Self { @@ -97,7 +97,7 @@ impl FeeManager { } } - pub fn base_fee(&self) -> u128 { + pub fn base_fee(&self) -> u64 { if self.is_eip1559() { *self.base_fee.read() } else { @@ -133,7 +133,7 @@ impl FeeManager { } /// Returns the current base fee - pub fn set_base_fee(&self, fee: u128) { + pub fn set_base_fee(&self, fee: u64) { trace!(target: "backend::fees", "updated base fee {:?}", fee); let mut base = self.base_fee.write(); *base = fee; @@ -151,8 +151,8 @@ impl FeeManager { &self, gas_used: u128, gas_limit: u128, - last_fee_per_gas: u128, - ) -> u128 { + last_fee_per_gas: u64, + ) -> u64 { // It's naturally impossible for base fee to be 0; // It means it was set by the user deliberately and therefore we treat it as a constant. // Therefore, we skip the base fee calculation altogether and we return 0. @@ -179,8 +179,8 @@ impl FeeManager { } /// Calculate base fee for next block. [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) spec -pub fn calculate_next_block_base_fee(gas_used: u128, gas_limit: u128, base_fee: u128) -> u128 { - calc_next_block_base_fee(gas_used, gas_limit, base_fee, BaseFeeParams::ethereum()) +pub fn calculate_next_block_base_fee(gas_used: u128, gas_limit: u128, base_fee: u64) -> u64 { + calc_next_block_base_fee(gas_used as u64, gas_limit as u64, base_fee, BaseFeeParams::ethereum()) } /// An async service that takes care of the `FeeHistory` cache @@ -235,9 +235,9 @@ impl FeeHistoryService { }; let mut block_number: Option = None; - let base_fee = header.base_fee_per_gas.unwrap_or_default(); - let excess_blob_gas = header.excess_blob_gas; - let blob_gas_used = header.blob_gas_used; + let base_fee = header.base_fee_per_gas.map(|g| g as u128).unwrap_or_default(); + let excess_blob_gas = header.excess_blob_gas.map(|g| g as u128); + let blob_gas_used = header.blob_gas_used.map(|g| g as u128); let base_fee_per_blob_gas = header.blob_fee(); let mut item = FeeHistoryCacheItem { base_fee, diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index 31e37c98b..74728f94b 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -48,7 +48,7 @@ async fn can_set_block_gas_limit() { // Mine a new block, and check the new block gas limit api.mine_one().await; let latest_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); - assert_eq!(block_gas_limit.to::(), latest_block.header.gas_limit); + assert_eq!(block_gas_limit.to::(), latest_block.header.gas_limit); } // Ref @@ -557,7 +557,7 @@ async fn test_get_transaction_receipt() { // the block should have the new base fee let block = provider.get_block(BlockId::default(), false.into()).await.unwrap().unwrap(); - assert_eq!(block.header.base_fee_per_gas.unwrap(), new_base_fee.to::()); + assert_eq!(block.header.base_fee_per_gas.unwrap(), new_base_fee.to::()); // mine blocks api.evm_mine(None).await.unwrap(); diff --git a/crates/anvil/tests/it/api.rs b/crates/anvil/tests/it/api.rs index f9aaf0dba..c4172b265 100644 --- a/crates/anvil/tests/it/api.rs +++ b/crates/anvil/tests/it/api.rs @@ -5,7 +5,10 @@ use crate::{ utils::{connect_pubsub_with_wallet, http_provider_with_signer}, }; use alloy_network::{EthereumWallet, TransactionBuilder}; -use alloy_primitives::{Address, ChainId, B256, U256}; +use alloy_primitives::{ + map::{AddressHashMap, B256HashMap, HashMap}, + Address, ChainId, B256, U256, +}; use alloy_provider::Provider; use alloy_rpc_types::{ request::TransactionRequest, state::AccountOverride, BlockId, BlockNumberOrTag, @@ -14,7 +17,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}; +use std::time::Duration; #[tokio::test(flavor = "multi_thread")] async fn can_get_block_number() { @@ -174,7 +177,7 @@ async fn can_estimate_gas_with_undersized_max_fee_per_gas() { let simple_storage_contract = SimpleStorage::deploy(&provider, init_value.clone()).await.unwrap(); - let undersized_max_fee_per_gas = 1_u128; + let undersized_max_fee_per_gas = 1; 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(); @@ -183,7 +186,7 @@ async fn can_estimate_gas_with_undersized_max_fee_per_gas() { let estimated_gas = simple_storage_contract .setValue("new_value".to_string()) - .max_fee_per_gas(undersized_max_fee_per_gas) + .max_fee_per_gas(undersized_max_fee_per_gas.into()) .from(wallet.address()) .estimate_gas() .await @@ -255,7 +258,7 @@ async fn can_call_on_pending_block() { .call() .await .unwrap(); - assert_eq!(block.header.gas_limit, ret_gas_limit.to::()); + assert_eq!(block.header.gas_limit, ret_gas_limit.to::()); let Multicall::getCurrentBlockCoinbaseReturn { coinbase: ret_coinbase, .. } = contract .getCurrentBlockCoinbase() @@ -284,13 +287,13 @@ async fn can_call_with_undersized_max_fee_per_gas() { 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; + let undersized_max_fee_per_gas = 1; 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) + .max_fee_per_gas(undersized_max_fee_per_gas.into()) .from(wallet.address()) .call() .await @@ -319,23 +322,24 @@ async fn can_call_with_state_override() { // Test the `balance` account override let balance = U256::from(42u64); - let overrides = HashMap::from([( - account, - AccountOverride { balance: Some(balance), ..Default::default() }, - )]); + let mut overrides = AddressHashMap::default(); + overrides.insert(account, AccountOverride { balance: Some(balance), ..Default::default() }); let result = multicall_contract.getEthBalance(account).state(overrides).call().await.unwrap().balance; assert_eq!(result, balance); // Test the `state_diff` account override - let overrides = HashMap::from([( + let mut state_diff = B256HashMap::default(); + state_diff.insert(B256::ZERO, account.into_word()); + let mut overrides = AddressHashMap::default(); + overrides.insert( *simple_storage_contract.address(), AccountOverride { // The `lastSender` is in the first storage slot - state_diff: Some(HashMap::from([(B256::ZERO, account.into_word())])), + state_diff: Some(state_diff), ..Default::default() }, - )]); + ); let last_sender = simple_storage_contract.lastSender().state(HashMap::default()).call().await.unwrap()._0; @@ -352,14 +356,17 @@ async fn can_call_with_state_override() { assert_eq!(value, init_value); // Test the `state` account override - let overrides = HashMap::from([( + let mut state = B256HashMap::default(); + state.insert(B256::ZERO, account.into_word()); + let mut overrides = AddressHashMap::default(); + overrides.insert( *simple_storage_contract.address(), AccountOverride { // The `lastSender` is in the first storage slot - state: Some(HashMap::from([(B256::ZERO, account.into_word())])), + state: Some(state), ..Default::default() }, - )]); + ); let last_sender = simple_storage_contract.lastSender().state(overrides.clone()).call().await.unwrap()._0; diff --git a/crates/anvil/tests/it/eip4844.rs b/crates/anvil/tests/it/eip4844.rs index 71a8bfd84..a4243ce15 100644 --- a/crates/anvil/tests/it/eip4844.rs +++ b/crates/anvil/tests/it/eip4844.rs @@ -138,7 +138,7 @@ async fn can_mine_blobs_when_exceeds_max_blobs() { let first_batch = vec![1u8; DATA_GAS_PER_BLOB as usize * 3]; let sidecar: SidecarBuilder = SidecarBuilder::from_slice(&first_batch); - let num_blobs_first = sidecar.clone().take().len(); + let num_blobs_first = sidecar.clone().take().len() as u64; let sidecar = sidecar.build().unwrap(); @@ -160,7 +160,7 @@ async fn can_mine_blobs_when_exceeds_max_blobs() { let sidecar: SidecarBuilder = SidecarBuilder::from_slice(&second_batch); - let num_blobs_second = sidecar.clone().take().len(); + let num_blobs_second = sidecar.clone().take().len() as u64; let sidecar = sidecar.build().unwrap(); tx.set_blob_sidecar(sidecar); @@ -181,12 +181,12 @@ async fn can_mine_blobs_when_exceeds_max_blobs() { ); assert_eq!( first_block.unwrap().unwrap().header.blob_gas_used, - Some(DATA_GAS_PER_BLOB as u128 * num_blobs_first as u128) + Some(DATA_GAS_PER_BLOB * num_blobs_first) ); assert_eq!( second_block.unwrap().unwrap().header.blob_gas_used, - Some(DATA_GAS_PER_BLOB as u128 * num_blobs_second as u128) + Some(DATA_GAS_PER_BLOB * num_blobs_second) ); // Mined in two different blocks assert_eq!(first_receipt.block_number.unwrap() + 1, second_receipt.block_number.unwrap()); diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index f93fa92a1..e6db8a063 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -781,7 +781,7 @@ async fn test_fork_can_send_opensea_tx() { .value(U256::from(20000000000000000u64)) .with_input(input) .with_gas_price(22180711707u128) - .with_gas_limit(150_000u128); + .with_gas_limit(150_000); let tx = WithOtherFields::new(tx); let tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); @@ -817,7 +817,7 @@ async fn test_fork_init_base_fee() { // assert_eq!(block.header.number, 13184859u64); let init_base_fee = block.header.base_fee_per_gas.unwrap(); - assert_eq!(init_base_fee, 63739886069u128); + assert_eq!(init_base_fee, 63739886069); api.mine_one().await; @@ -1185,7 +1185,7 @@ async fn test_fork_reset_basefee() { let latest = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); // basefee of +1 block: - assert_eq!(latest.header.base_fee_per_gas.unwrap(), 59455969592u128); + assert_eq!(latest.header.base_fee_per_gas.unwrap(), 59455969592u64); // now reset to block 18835000 -1 api.anvil_reset(Some(Forking { json_rpc_url: None, block_number: Some(18835000u64 - 1) })) @@ -1196,7 +1196,7 @@ async fn test_fork_reset_basefee() { let latest = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); // basefee of the forked block: - assert_eq!(latest.header.base_fee_per_gas.unwrap(), 59017001138u128); + assert_eq!(latest.header.base_fee_per_gas.unwrap(), 59017001138); } // @@ -1288,7 +1288,7 @@ async fn test_base_fork_gas_limit() { .unwrap(); assert!(api.gas_limit() >= uint!(132_000_000_U256)); - assert!(block.header.gas_limit >= 132_000_000_u128); + assert!(block.header.gas_limit >= 132_000_000_u64); } // @@ -1443,7 +1443,7 @@ async fn test_reset_dev_account_nonce() { .from(address) .to(address) .nonce(nonce_after) - .gas_limit(21000u128), + .gas_limit(21000), )) .await .unwrap() diff --git a/crates/anvil/tests/it/gas.rs b/crates/anvil/tests/it/gas.rs index e80a52462..55f832199 100644 --- a/crates/anvil/tests/it/gas.rs +++ b/crates/anvil/tests/it/gas.rs @@ -102,7 +102,7 @@ async fn test_basefee_half_block() { .unwrap(); // unchanged, half block - assert_eq!(next_base_fee, INITIAL_BASE_FEE); + assert_eq!(next_base_fee, { INITIAL_BASE_FEE }); } #[tokio::test(flavor = "multi_thread")] @@ -147,7 +147,7 @@ async fn test_basefee_empty_block() { #[tokio::test(flavor = "multi_thread")] async fn test_respect_base_fee() { let base_fee = 50u128; - let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee))).await; + let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee as u64))).await; let provider = handle.http_provider(); @@ -168,7 +168,7 @@ async fn test_respect_base_fee() { #[tokio::test(flavor = "multi_thread")] async fn test_tip_above_fee_cap() { let base_fee = 50u128; - let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee))).await; + let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee as u64))).await; let provider = handle.http_provider(); @@ -190,17 +190,17 @@ async fn test_tip_above_fee_cap() { #[tokio::test(flavor = "multi_thread")] async fn test_can_use_fee_history() { let base_fee = 50u128; - let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee))).await; + let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee as u64))).await; let provider = handle.http_provider(); for _ in 0..10 { let fee_history = provider.get_fee_history(1, Default::default(), &[]).await.unwrap(); - let next_base_fee = fee_history.base_fee_per_gas.last().unwrap(); + let next_base_fee = *fee_history.base_fee_per_gas.last().unwrap(); let tx = TransactionRequest::default() .with_to(Address::random()) .with_value(U256::from(100)) - .with_gas_price(*next_base_fee); + .with_gas_price(next_base_fee); let tx = WithOtherFields::new(tx); let receipt = @@ -208,11 +208,11 @@ async fn test_can_use_fee_history() { assert!(receipt.inner.inner.is_success()); let fee_history_after = provider.get_fee_history(1, Default::default(), &[]).await.unwrap(); - let latest_fee_history_fee = fee_history_after.base_fee_per_gas.first().unwrap(); + let latest_fee_history_fee = *fee_history_after.base_fee_per_gas.first().unwrap() as u64; let latest_block = provider.get_block(BlockId::latest(), false.into()).await.unwrap().unwrap(); - assert_eq!(latest_block.header.base_fee_per_gas.unwrap(), *latest_fee_history_fee); - assert_eq!(latest_fee_history_fee, next_base_fee); + assert_eq!(latest_block.header.base_fee_per_gas.unwrap(), latest_fee_history_fee); + assert_eq!(latest_fee_history_fee, next_base_fee as u64); } } diff --git a/crates/anvil/tests/it/optimism.rs b/crates/anvil/tests/it/optimism.rs index 6446caf9c..8de4eab1d 100644 --- a/crates/anvil/tests/it/optimism.rs +++ b/crates/anvil/tests/it/optimism.rs @@ -25,19 +25,21 @@ async fn test_deposits_not_supported_if_optimism_disabled() { .with_to(to) .with_value(U256::from(1234)) .with_gas_limit(21000); - let tx = WithOtherFields { - inner: tx, - other: OptimismTransactionFields { - source_hash: Some(b256!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - mint: Some(0), - is_system_tx: Some(true), - deposit_receipt_version: None, - } - .into(), + + let op_fields = OptimismTransactionFields { + source_hash: Some(b256!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + mint: Some(0), + is_system_tx: Some(true), + deposit_receipt_version: None, }; + // TODO: Test this + let other = serde_json::to_value(op_fields).unwrap().try_into().unwrap(); + + let tx = WithOtherFields { inner: tx, other }; + let err = provider.send_transaction(tx).await.unwrap_err(); let s = err.to_string(); assert!(s.contains("op-stack deposit tx received but is not supported"), "{s:?}"); @@ -61,23 +63,22 @@ async fn test_send_value_deposit_transaction() { let send_value = U256::from(1234); let before_balance_to = provider.get_balance(to).await.unwrap(); + let op_fields = OptimismTransactionFields { + source_hash: Some(b256!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + mint: Some(0), + is_system_tx: Some(true), + deposit_receipt_version: None, + }; + + let other = serde_json::to_value(op_fields).unwrap().try_into().unwrap(); let tx = TransactionRequest::default() .with_from(from) .with_to(to) .with_value(send_value) .with_gas_limit(21000); - let tx: WithOtherFields = WithOtherFields { - inner: tx, - other: OptimismTransactionFields { - source_hash: Some(b256!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - mint: Some(0), - is_system_tx: Some(true), - deposit_receipt_version: None, - } - .into(), - }; + let tx: WithOtherFields = WithOtherFields { inner: tx, other }; let pending = provider.send_transaction(tx).await.unwrap().register().await.unwrap(); @@ -121,18 +122,17 @@ async fn test_send_value_raw_deposit_transaction() { .with_gas_limit(21_000) .with_max_fee_per_gas(20_000_000_000) .with_max_priority_fee_per_gas(1_000_000_000); - let tx = WithOtherFields { - inner: tx, - other: OptimismTransactionFields { - source_hash: Some(b256!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - mint: Some(0), - is_system_tx: Some(true), - deposit_receipt_version: None, - } - .into(), + + let op_fields = OptimismTransactionFields { + source_hash: Some(b256!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + mint: Some(0), + is_system_tx: Some(true), + deposit_receipt_version: None, }; + let other = serde_json::to_value(op_fields).unwrap().try_into().unwrap(); + let tx = WithOtherFields { inner: tx, other }; let tx_envelope = tx.build(&signer).await.unwrap(); let mut tx_buffer = Vec::with_capacity(tx_envelope.encode_2718_len()); tx_envelope.encode_2718(&mut tx_buffer); diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index 63ebbbcf3..07c120d1c 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -197,7 +197,7 @@ async fn can_reject_too_high_gas_limits() { let from = accounts[0].address(); let to = accounts[1].address(); - let gas_limit = api.gas_limit().to::(); + let gas_limit = api.gas_limit().to::(); let amount = handle.genesis_balance().checked_div(U256::from(3u64)).unwrap(); let tx = @@ -237,11 +237,11 @@ async fn can_mine_large_gas_limit() { let from = accounts[0].address(); let to = accounts[1].address(); - let gas_limit = anvil::DEFAULT_GAS_LIMIT; + let gas_limit = anvil::DEFAULT_GAS_LIMIT as u64; let amount = handle.genesis_balance().checked_div(U256::from(3u64)).unwrap(); let tx = - TransactionRequest::default().to(to).value(amount).from(from).with_gas_limit(gas_limit * 3); + TransactionRequest::default().to(to).value(amount).from(from).with_gas_limit(gas_limit); // send transaction with higher gas limit let pending = provider.send_transaction(WithOtherFields::new(tx)).await.unwrap(); @@ -580,7 +580,7 @@ async fn can_handle_multiple_concurrent_transfers_with_same_nonce() { .value(U256::from(100)) .from(from) .nonce(nonce) - .with_gas_limit(21000u128); + .with_gas_limit(21000); let tx = WithOtherFields::new(tx); @@ -621,7 +621,7 @@ async fn can_handle_multiple_concurrent_deploys_with_same_nonce() { .from(from) .with_input(greeter_calldata.to_owned()) .nonce(nonce) - .with_gas_limit(300_000u128); + .with_gas_limit(300_000); let tx = WithOtherFields::new(tx); @@ -662,7 +662,7 @@ async fn can_handle_multiple_concurrent_transactions_with_same_nonce() { .from(from) .with_input(deploy_calldata.to_owned()) .nonce(nonce) - .with_gas_limit(300_000u128); + .with_gas_limit(300_000); let deploy_tx = WithOtherFields::new(deploy_tx); let set_greeting = greeter_contract.setGreeting("Hello".to_string()); @@ -672,7 +672,7 @@ async fn can_handle_multiple_concurrent_transactions_with_same_nonce() { .from(from) .with_input(set_greeting_calldata.to_owned()) .nonce(nonce) - .with_gas_limit(300_000u128); + .with_gas_limit(300_000); let set_greeting_tx = WithOtherFields::new(set_greeting_tx); for idx in 0..10 { @@ -1127,7 +1127,7 @@ async fn test_estimate_gas() { let addr = recipient; let account_override = AccountOverride { balance: Some(alloy_primitives::U256::from(1e18)), ..Default::default() }; - let mut state_override = StateOverride::new(); + let mut state_override = StateOverride::default(); state_override.insert(addr, account_override); // Estimate gas with state override implying sufficient funds. @@ -1152,7 +1152,7 @@ async fn test_reject_gas_too_low() { .to(Address::random()) .value(U256::from(1337u64)) .from(account) - .with_gas_limit(gas as u128); + .with_gas_limit(gas); let tx = WithOtherFields::new(tx); let resp = provider.send_transaction(tx).await; @@ -1169,7 +1169,7 @@ async fn can_call_with_high_gas_limit() { let greeter_contract = Greeter::deploy(provider, "Hello World!".to_string()).await.unwrap(); - let greeting = greeter_contract.greet().gas(60_000_000u128).call().await.unwrap(); + let greeting = greeter_contract.greet().gas(60_000_000).call().await.unwrap(); assert_eq!("Hello World!", greeting._0); } @@ -1179,7 +1179,7 @@ async fn test_reject_eip1559_pre_london() { spawn(NodeConfig::test().with_hardfork(Some(EthereumHardfork::Berlin.into()))).await; let provider = handle.http_provider(); - let gas_limit = api.gas_limit().to::(); + let gas_limit = api.gas_limit().to::(); let gas_price = api.gas_price(); let unsupported_call_builder = diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 3e80f1eaa..3ef5859ec 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -677,11 +677,7 @@ impl Cheatcodes { value: Some(input.value()), input: TransactionInput::new(input.init_code()), nonce: Some(account.info.nonce), - gas: if is_fixed_gas_limit { - Some(input.gas_limit() as u128) - } else { - None - }, + gas: if is_fixed_gas_limit { Some(input.gas_limit()) } else { None }, ..Default::default() } .into(), @@ -1016,11 +1012,7 @@ impl Cheatcodes { value: call.transfer_value(), input: TransactionInput::new(call.input.clone()), nonce: Some(account.info.nonce), - gas: if is_fixed_gas_limit { - Some(call.gas_limit as u128) - } else { - None - }, + gas: if is_fixed_gas_limit { Some(call.gas_limit) } else { None }, ..Default::default() } .into(), diff --git a/crates/common/src/transactions.rs b/crates/common/src/transactions.rs index 9df8f896f..7335714e5 100644 --- a/crates/common/src/transactions.rs +++ b/crates/common/src/transactions.rs @@ -2,7 +2,10 @@ use alloy_consensus::{Transaction, TxEnvelope}; use alloy_primitives::{Address, TxKind, U256}; -use alloy_provider::{network::AnyNetwork, Provider}; +use alloy_provider::{ + network::{AnyNetwork, TransactionBuilder}, + Provider, +}; use alloy_rpc_types::{AnyTransactionReceipt, BlockId, TransactionRequest}; use alloy_serde::WithOtherFields; use alloy_transport::Transport; @@ -212,8 +215,8 @@ impl TransactionMaybeSigned { pub fn gas(&self) -> Option { match self { - Self::Signed { tx, .. } => Some(tx.gas_limit()), - Self::Unsigned(tx) => tx.gas, + Self::Signed { tx, .. } => Some(tx.gas_limit() as u128), + Self::Unsigned(tx) => tx.gas_limit().map(|g| g as u128), } } diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 49830bc89..07ee70130 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -1279,7 +1279,7 @@ impl DatabaseExt for Backend { 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; + tx.gas.ok_or_else(|| eyre::eyre!("transact_from_tx: No `gas` field found"))?; 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; diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index 11f8bbb31..19782ee9d 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -84,7 +84,7 @@ pub fn get_function<'a>( /// Configures the env for the transaction pub fn configure_tx_env(env: &mut revm::primitives::Env, tx: &Transaction) { env.tx.caller = tx.from; - env.tx.gas_limit = tx.gas as u64; + env.tx.gas_limit = tx.gas; env.tx.gas_price = U256::from(tx.gas_price.unwrap_or_default()); env.tx.gas_priority_fee = tx.max_priority_fee_per_gas.map(U256::from); env.tx.nonce = Some(tx.nonce); diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index ac893b19f..3faaa441e 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -43,7 +43,7 @@ where tx.set_gas_limit( provider.estimate_gas(tx).await.wrap_err("Failed to estimate gas for tx")? * - estimate_multiplier as u128 / + estimate_multiplier / 100, ); Ok(()) diff --git a/crates/script/src/transaction.rs b/crates/script/src/transaction.rs index bbf84ff0f..639c5f022 100644 --- a/crates/script/src/transaction.rs +++ b/crates/script/src/transaction.rs @@ -121,7 +121,7 @@ impl TransactionWithMetadata { 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); + unsigned.gas = Some(result.gas_used * gas_estimate_multiplier / 100); } } diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index 034bb49a6..661eb5c8d 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -253,9 +253,9 @@ impl VerifyBytecodeArgs { 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.max_fee_per_gas = block.header.base_fee_per_gas.map(|g| g as u128); gen_tx.gas = block.header.gas_limit; - gen_tx.gas_price = Some(block.header.base_fee_per_gas.unwrap_or_default()); + gen_tx.gas_price = block.header.base_fee_per_gas.map(|g| g as u128); } configure_tx_env(&mut env, &gen_tx); From 6a1e0b781c772209dcfecf13b2cceb875853d043 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 2 Oct 2024 20:23:18 +0200 Subject: [PATCH 048/100] chore: print parent beacon root (#9006) Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/cast/tests/cli/main.rs | 1 + crates/common/fmt/src/ui.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 8c928b3a8..df566d114 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -54,6 +54,7 @@ mixHash [..] nonce [..] number [..] parentHash [..] +parentBeaconRoot [..] transactionsRoot [..] receiptsRoot [..] sha3Uncles [..] diff --git a/crates/common/fmt/src/ui.rs b/crates/common/fmt/src/ui.rs index 64c520769..a82853145 100644 --- a/crates/common/fmt/src/ui.rs +++ b/crates/common/fmt/src/ui.rs @@ -622,6 +622,7 @@ mixHash {} nonce {} number {} parentHash {} +parentBeaconRoot {} transactionsRoot {} receiptsRoot {} sha3Uncles {} @@ -642,6 +643,7 @@ totalDifficulty {}", block.header.nonce.pretty(), block.header.number.pretty(), block.header.parent_hash.pretty(), + block.header.parent_beacon_block_root.pretty(), block.header.transactions_root.pretty(), block.header.receipts_root.pretty(), block.header.uncles_hash.pretty(), From 471e4ac317858b3419faaee58ade30c0671021e0 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 3 Oct 2024 02:02:55 +0200 Subject: [PATCH 049/100] chore: use dyn DatabaseExt everywhere (#8924) * wip * feat: use `dyn DatabaseExt` (#9010) * wip * clean up * fix * clippy * doc * fix imports * chore: simplify InspectorExt by making it lifetime-generic * fmt * chore: remove unnecessary casts and lifetimes * chore: more unused lifetimes (clippy) --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: Arsenii Kulikov --- crates/anvil/src/eth/backend/executor.rs | 42 ++- crates/anvil/src/eth/backend/mem/inspector.rs | 15 - crates/anvil/src/eth/backend/mem/mod.rs | 14 +- crates/cheatcodes/src/evm.rs | 156 +++++---- crates/cheatcodes/src/evm/fork.rs | 86 ++--- crates/cheatcodes/src/evm/mock.rs | 14 +- crates/cheatcodes/src/evm/prank.rs | 14 +- crates/cheatcodes/src/fs.rs | 13 +- crates/cheatcodes/src/inspector.rs | 299 +++++++----------- crates/cheatcodes/src/inspector/utils.rs | 33 +- crates/cheatcodes/src/lib.rs | 59 ++-- crates/cheatcodes/src/script.rs | 28 +- crates/cheatcodes/src/test.rs | 15 +- crates/cheatcodes/src/test/assert.rs | 20 +- crates/cheatcodes/src/test/assume.rs | 4 +- crates/cheatcodes/src/test/expect.rs | 46 +-- crates/cheatcodes/src/utils.rs | 22 +- crates/evm/core/src/backend/cow.rs | 16 +- crates/evm/core/src/backend/mod.rs | 30 +- crates/evm/core/src/lib.rs | 12 +- crates/evm/core/src/utils.rs | 75 +---- crates/evm/evm/src/inspectors/logs.rs | 4 +- crates/evm/evm/src/inspectors/stack.rs | 111 ++++--- 23 files changed, 499 insertions(+), 629 deletions(-) diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index d92d60a87..3221620ef 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -28,8 +28,9 @@ use foundry_evm::{ }, }, traces::CallTraceNode, + utils::alphanet_handler_register, }; -use revm::primitives::MAX_BLOB_GAS_PER_BLOCK; +use revm::{db::WrapDatabaseRef, primitives::MAX_BLOB_GAS_PER_BLOCK}; use std::sync::Arc; /// Represents an executed transaction (transacted on the DB) @@ -303,7 +304,7 @@ impl<'a, 'b, DB: Db + ?Sized, V: TransactionValidator> Iterator let nonce = account.nonce; // records all call and step traces - let mut inspector = Inspector::default().with_tracing().with_alphanet(self.alphanet); + let mut inspector = Inspector::default().with_tracing(); if self.enable_steps_tracing { inspector = inspector.with_steps_tracing(); } @@ -312,8 +313,7 @@ impl<'a, 'b, DB: Db + ?Sized, V: TransactionValidator> Iterator } let exec_result = { - let mut evm = - foundry_evm::utils::new_evm_with_inspector(&mut *self.db, env, &mut inspector); + let mut evm = new_evm_with_inspector(&mut *self.db, env, &mut inspector, self.alphanet); if let Some(factory) = &self.precompile_factory { inject_precompiles(&mut evm, factory.precompiles()); } @@ -396,3 +396,37 @@ fn build_logs_bloom(logs: Vec, bloom: &mut Bloom) { } } } + +/// Creates a database with given database and inspector, optionally enabling alphanet features. +pub fn new_evm_with_inspector( + db: DB, + env: EnvWithHandlerCfg, + inspector: &mut dyn revm::Inspector, + alphanet: bool, +) -> revm::Evm<'_, &mut dyn revm::Inspector, DB> { + let EnvWithHandlerCfg { env, handler_cfg } = env; + + let mut handler = revm::Handler::new(handler_cfg); + + handler.append_handler_register_plain(revm::inspector_handle_register); + if alphanet { + handler.append_handler_register_plain(alphanet_handler_register); + } + + let context = revm::Context::new(revm::EvmContext::new_with_env(db, env), inspector); + + revm::Evm::new(context, handler) +} + +/// Creates a new EVM with the given inspector and wraps the database in a `WrapDatabaseRef`. +pub fn new_evm_with_inspector_ref<'a, DB>( + db: DB, + env: EnvWithHandlerCfg, + inspector: &mut dyn revm::Inspector>, + alphanet: bool, +) -> revm::Evm<'a, &mut dyn revm::Inspector>, WrapDatabaseRef> +where + DB: revm::DatabaseRef, +{ + new_evm_with_inspector(WrapDatabaseRef(db), env, inspector, alphanet) +} diff --git a/crates/anvil/src/eth/backend/mem/inspector.rs b/crates/anvil/src/eth/backend/mem/inspector.rs index b354a9a5c..e590d57e3 100644 --- a/crates/anvil/src/eth/backend/mem/inspector.rs +++ b/crates/anvil/src/eth/backend/mem/inspector.rs @@ -14,7 +14,6 @@ use foundry_evm::{ EvmContext, }, traces::TracingInspectorConfig, - InspectorExt, }; /// The [`revm::Inspector`] used when transacting in the evm @@ -23,8 +22,6 @@ 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 { @@ -59,12 +56,6 @@ 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 { @@ -176,12 +167,6 @@ impl revm::Inspector for Inspector { } } -impl InspectorExt for Inspector { - fn is_alphanet(&self) -> bool { - self.alphanet - } -} - /// Prints all the logs pub fn print_logs(logs: &[Log]) { for log in decode_console_logs(logs) { diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index ea36696f2..dbf22fbeb 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -1,6 +1,7 @@ //! In-memory blockchain backend. use self::state::trie_storage; +use super::executor::new_evm_with_inspector_ref; use crate::{ config::PruneStateHistoryConfig, eth::{ @@ -32,6 +33,7 @@ use crate::{ revm::{db::DatabaseRef, primitives::AccountInfo}, ForkChoice, NodeConfig, PrecompileFactory, }; +use alloy_chains::NamedChain; use alloy_consensus::{Account, Header, Receipt, ReceiptWithBloom}; use alloy_eips::eip4844::MAX_BLOBS_PER_BLOCK; use alloy_primitives::{keccak256, Address, Bytes, TxHash, TxKind, B256, U256, U64}; @@ -64,8 +66,6 @@ use anvil_core::eth::{ utils::meets_eip155, }; use anvil_rpc::error::RpcError; - -use alloy_chains::NamedChain; use flate2::{read::GzDecoder, write::GzEncoder, Compression}; use foundry_evm::{ backend::{DatabaseError, DatabaseResult, RevertStateSnapshotAction}, @@ -81,8 +81,6 @@ use foundry_evm::{ }, }, traces::TracingInspectorConfig, - utils::new_evm_with_inspector_ref, - InspectorExt, }; use futures::channel::mpsc::{unbounded, UnboundedSender}; use parking_lot::{Mutex, RwLock}; @@ -864,15 +862,15 @@ impl Backend { &self, db: &'db dyn DatabaseRef, env: EnvWithHandlerCfg, - inspector: &'i mut dyn InspectorExt< + inspector: &'i mut dyn revm::Inspector< WrapDatabaseRef<&'db dyn DatabaseRef>, >, ) -> revm::Evm< '_, - &'i mut dyn InspectorExt>>, + &'i mut dyn revm::Inspector>>, WrapDatabaseRef<&'db dyn DatabaseRef>, > { - let mut evm = new_evm_with_inspector_ref(db, env, inspector); + let mut evm = new_evm_with_inspector_ref(db, env, inspector, self.alphanet); if let Some(factory) = &self.precompile_factory { inject_precompiles(&mut evm, factory.precompiles()); } @@ -1269,7 +1267,7 @@ impl Backend { /// Builds [`Inspector`] with the configured options fn build_inspector(&self) -> Inspector { - let mut inspector = Inspector::default().with_alphanet(self.alphanet); + let mut inspector = Inspector::default(); if self.print_logs { inspector = inspector.with_log_collector(); diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 7d4a23d61..f9671430d 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -1,7 +1,8 @@ //! Implementations of [`Evm`](spec::Group::Evm) cheatcodes. use crate::{ - BroadcastableTransaction, Cheatcode, Cheatcodes, CheatcodesExecutor, CheatsCtxt, Result, Vm::*, + inspector::InnerEcx, BroadcastableTransaction, Cheatcode, Cheatcodes, CheatcodesExecutor, + CheatsCtxt, Result, Vm::*, }; use alloy_consensus::TxEnvelope; use alloy_genesis::{Genesis, GenesisAccount}; @@ -14,10 +15,7 @@ use foundry_evm_core::{ constants::{CALLER, CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, TEST_CONTRACT_ADDRESS}, }; use rand::Rng; -use revm::{ - primitives::{Account, Bytecode, SpecId, KECCAK_EMPTY}, - InnerEvmContext, -}; +use revm::primitives::{Account, Bytecode, SpecId, KECCAK_EMPTY}; use std::{ collections::{BTreeMap, HashMap}, path::Path, @@ -85,21 +83,21 @@ impl Cheatcode for addrCall { } impl Cheatcode for getNonce_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account } = self; get_nonce(ccx, account) } } impl Cheatcode for getNonce_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + 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 { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { target, slot } = *self; ensure_not_precompile!(&target, ccx); ccx.ecx.load_account(target)?; @@ -136,7 +134,7 @@ impl Cheatcode for loadCall { } impl Cheatcode for loadAllocsCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { pathToAllocsJson } = self; let path = Path::new(pathToAllocsJson); @@ -162,7 +160,7 @@ impl Cheatcode for loadAllocsCall { } impl Cheatcode for dumpStateCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { pathToStateJson } = self; let path = Path::new(pathToStateJson); @@ -282,7 +280,7 @@ impl Cheatcode for lastCallGasCall { } impl Cheatcode for chainIdCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newChainId } = self; ensure!(*newChainId <= U256::from(u64::MAX), "chain ID must be less than 2^64 - 1"); ccx.ecx.env.cfg.chain_id = newChainId.to(); @@ -291,7 +289,7 @@ impl Cheatcode for chainIdCall { } impl Cheatcode for coinbaseCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newCoinbase } = self; ccx.ecx.env.block.coinbase = *newCoinbase; Ok(Default::default()) @@ -299,7 +297,7 @@ impl Cheatcode for coinbaseCall { } impl Cheatcode for difficultyCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newDifficulty } = self; ensure!( ccx.ecx.spec_id() < SpecId::MERGE, @@ -312,7 +310,7 @@ impl Cheatcode for difficultyCall { } impl Cheatcode for feeCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newBasefee } = self; ccx.ecx.env.block.basefee = *newBasefee; Ok(Default::default()) @@ -320,7 +318,7 @@ impl Cheatcode for feeCall { } impl Cheatcode for prevrandao_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newPrevrandao } = self; ensure!( ccx.ecx.spec_id() >= SpecId::MERGE, @@ -333,7 +331,7 @@ impl Cheatcode for prevrandao_0Call { } impl Cheatcode for prevrandao_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newPrevrandao } = self; ensure!( ccx.ecx.spec_id() >= SpecId::MERGE, @@ -346,7 +344,7 @@ impl Cheatcode for prevrandao_1Call { } impl Cheatcode for blobhashesCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { hashes } = self; ensure!( ccx.ecx.spec_id() >= SpecId::CANCUN, @@ -359,7 +357,7 @@ impl Cheatcode for blobhashesCall { } impl Cheatcode for getBlobhashesCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; ensure!( ccx.ecx.spec_id() >= SpecId::CANCUN, @@ -371,7 +369,7 @@ impl Cheatcode for getBlobhashesCall { } impl Cheatcode for rollCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newHeight } = self; ccx.ecx.env.block.number = *newHeight; Ok(Default::default()) @@ -379,14 +377,14 @@ impl Cheatcode for rollCall { } impl Cheatcode for getBlockNumberCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; Ok(ccx.ecx.env.block.number.abi_encode()) } } impl Cheatcode for txGasPriceCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newGasPrice } = self; ccx.ecx.env.tx.gas_price = *newGasPrice; Ok(Default::default()) @@ -394,7 +392,7 @@ impl Cheatcode for txGasPriceCall { } impl Cheatcode for warpCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newTimestamp } = self; ccx.ecx.env.block.timestamp = *newTimestamp; Ok(Default::default()) @@ -402,14 +400,14 @@ impl Cheatcode for warpCall { } impl Cheatcode for getBlockTimestampCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; Ok(ccx.ecx.env.block.timestamp.abi_encode()) } } impl Cheatcode for blobBaseFeeCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newBlobBaseFee } = self; ensure!( ccx.ecx.spec_id() >= SpecId::CANCUN, @@ -422,14 +420,14 @@ impl Cheatcode for blobBaseFeeCall { } impl Cheatcode for getBlobBaseFeeCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; Ok(ccx.ecx.env.block.get_blob_excess_gas().unwrap_or(0).abi_encode()) } } impl Cheatcode for dealCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account: address, newBalance: new_balance } = *self; let account = journaled_account(ccx.ecx, address)?; let old_balance = std::mem::replace(&mut account.info.balance, new_balance); @@ -440,7 +438,7 @@ impl Cheatcode for dealCall { } impl Cheatcode for etchCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { target, newRuntimeBytecode } = self; ensure_not_precompile!(target, ccx); ccx.ecx.load_account(*target)?; @@ -451,7 +449,7 @@ impl Cheatcode for etchCall { } impl Cheatcode for resetNonceCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account } = self; let account = journaled_account(ccx.ecx, *account)?; // Per EIP-161, EOA nonces start at 0, but contract nonces @@ -466,7 +464,7 @@ impl Cheatcode for resetNonceCall { } impl Cheatcode for setNonceCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account, newNonce } = *self; let account = journaled_account(ccx.ecx, account)?; // nonce must increment only @@ -482,7 +480,7 @@ impl Cheatcode for setNonceCall { } impl Cheatcode for setNonceUnsafeCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account, newNonce } = *self; let account = journaled_account(ccx.ecx, account)?; account.info.nonce = newNonce; @@ -491,7 +489,7 @@ impl Cheatcode for setNonceUnsafeCall { } impl Cheatcode for storeCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { target, slot, value } = *self; ensure_not_precompile!(&target, ccx); // ensure the account is touched @@ -502,7 +500,7 @@ impl Cheatcode for storeCall { } impl Cheatcode for coolCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { target } = self; if let Some(account) = ccx.ecx.journaled_state.state.get_mut(target) { account.unmark_touch(); @@ -513,28 +511,28 @@ impl Cheatcode for coolCall { } impl Cheatcode for readCallersCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; read_callers(ccx.state, &ccx.ecx.env.tx.caller) } } impl Cheatcode for snapshotValue_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { name, value } = self; inner_value_snapshot(ccx, None, Some(name.clone()), value.to_string()) } } impl Cheatcode for snapshotValue_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { group, name, value } = self; inner_value_snapshot(ccx, Some(group.clone()), Some(name.clone()), value.to_string()) } } impl Cheatcode for snapshotGasLastCall_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { name } = self; let Some(last_call_gas) = &ccx.state.gas_metering.last_call_gas else { bail!("no external call was made yet"); @@ -544,7 +542,7 @@ impl Cheatcode for snapshotGasLastCall_0Call { } impl Cheatcode for snapshotGasLastCall_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { name, group } = self; let Some(last_call_gas) = &ccx.state.gas_metering.last_call_gas else { bail!("no external call was made yet"); @@ -559,35 +557,35 @@ impl Cheatcode for snapshotGasLastCall_1Call { } impl Cheatcode for startSnapshotGas_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { name } = self; inner_start_gas_snapshot(ccx, None, Some(name.clone())) } } impl Cheatcode for startSnapshotGas_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { group, name } = self; inner_start_gas_snapshot(ccx, Some(group.clone()), Some(name.clone())) } } impl Cheatcode for stopSnapshotGas_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; inner_stop_gas_snapshot(ccx, None, None) } } impl Cheatcode for stopSnapshotGas_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { name } = self; inner_stop_gas_snapshot(ccx, None, Some(name.clone())) } } impl Cheatcode for stopSnapshotGas_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { group, name } = self; inner_stop_gas_snapshot(ccx, Some(group.clone()), Some(name.clone())) } @@ -595,14 +593,14 @@ impl Cheatcode for stopSnapshotGas_2Call { // Deprecated in favor of `snapshotStateCall` impl Cheatcode for snapshotCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; inner_snapshot_state(ccx) } } impl Cheatcode for snapshotStateCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; inner_snapshot_state(ccx) } @@ -610,14 +608,14 @@ impl Cheatcode for snapshotStateCall { // Deprecated in favor of `revertToStateCall` impl Cheatcode for revertToCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { snapshotId } = self; inner_revert_to_state(ccx, *snapshotId) } } impl Cheatcode for revertToStateCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { snapshotId } = self; inner_revert_to_state(ccx, *snapshotId) } @@ -625,14 +623,14 @@ impl Cheatcode for revertToStateCall { // Deprecated in favor of `revertToStateAndDeleteCall` impl Cheatcode for revertToAndDeleteCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { snapshotId } = self; inner_revert_to_state_and_delete(ccx, *snapshotId) } } impl Cheatcode for revertToStateAndDeleteCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { snapshotId } = self; inner_revert_to_state_and_delete(ccx, *snapshotId) } @@ -640,14 +638,14 @@ impl Cheatcode for revertToStateAndDeleteCall { // Deprecated in favor of `deleteStateSnapshotCall` impl Cheatcode for deleteSnapshotCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { snapshotId } = self; inner_delete_state_snapshot(ccx, *snapshotId) } } impl Cheatcode for deleteStateSnapshotCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { snapshotId } = self; inner_delete_state_snapshot(ccx, *snapshotId) } @@ -655,14 +653,14 @@ impl Cheatcode for deleteStateSnapshotCall { // Deprecated in favor of `deleteStateSnapshotsCall` impl Cheatcode for deleteSnapshotsCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; inner_delete_state_snapshots(ccx) } } impl Cheatcode for deleteStateSnapshotsCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; inner_delete_state_snapshots(ccx) } @@ -684,11 +682,7 @@ impl Cheatcode for stopAndReturnStateDiffCall { } impl Cheatcode for broadcastRawTransactionCall { - fn apply_full( - &self, - ccx: &mut CheatsCtxt, - executor: &mut E, - ) -> Result { + fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { let mut data = self.data.as_ref(); let tx = TxEnvelope::decode(&mut data) .map_err(|err| fmt_err!("failed to decode RLP-encoded transaction: {err}"))?; @@ -697,7 +691,7 @@ impl Cheatcode for broadcastRawTransactionCall { tx.clone().into(), &ccx.ecx.env, &mut ccx.ecx.journaled_state, - &mut executor.get_inspector(ccx.state), + &mut *executor.get_inspector(ccx.state), )?; if ccx.state.broadcast.is_some() { @@ -712,7 +706,7 @@ impl Cheatcode for broadcastRawTransactionCall { } impl Cheatcode for setBlockhashCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { blockNumber, blockHash } = *self; ensure!( blockNumber <= ccx.ecx.env.block.number, @@ -725,16 +719,16 @@ impl Cheatcode for setBlockhashCall { } } -pub(super) fn get_nonce(ccx: &mut CheatsCtxt, address: &Address) -> Result { +pub(super) fn get_nonce(ccx: &mut CheatsCtxt, address: &Address) -> Result { let account = ccx.ecx.journaled_state.load_account(*address, &mut ccx.ecx.db)?; Ok(account.info.nonce.abi_encode()) } -fn inner_snapshot_state(ccx: &mut CheatsCtxt) -> Result { +fn inner_snapshot_state(ccx: &mut CheatsCtxt) -> Result { Ok(ccx.ecx.db.snapshot_state(&ccx.ecx.journaled_state, &ccx.ecx.env).abi_encode()) } -fn inner_revert_to_state(ccx: &mut CheatsCtxt, snapshot_id: U256) -> Result { +fn inner_revert_to_state(ccx: &mut CheatsCtxt, snapshot_id: U256) -> Result { let result = if let Some(journaled_state) = ccx.ecx.db.revert_state( snapshot_id, &ccx.ecx.journaled_state, @@ -750,10 +744,7 @@ fn inner_revert_to_state(ccx: &mut CheatsCtxt, snapshot_id: Ok(result.abi_encode()) } -fn inner_revert_to_state_and_delete( - ccx: &mut CheatsCtxt, - snapshot_id: U256, -) -> Result { +fn inner_revert_to_state_and_delete(ccx: &mut CheatsCtxt, snapshot_id: U256) -> Result { let result = if let Some(journaled_state) = ccx.ecx.db.revert_state( snapshot_id, &ccx.ecx.journaled_state, @@ -769,21 +760,18 @@ fn inner_revert_to_state_and_delete( Ok(result.abi_encode()) } -fn inner_delete_state_snapshot( - ccx: &mut CheatsCtxt, - snapshot_id: U256, -) -> Result { +fn inner_delete_state_snapshot(ccx: &mut CheatsCtxt, snapshot_id: U256) -> Result { let result = ccx.ecx.db.delete_state_snapshot(snapshot_id); Ok(result.abi_encode()) } -fn inner_delete_state_snapshots(ccx: &mut CheatsCtxt) -> Result { +fn inner_delete_state_snapshots(ccx: &mut CheatsCtxt) -> Result { ccx.ecx.db.delete_state_snapshots(); Ok(Default::default()) } -fn inner_value_snapshot( - ccx: &mut CheatsCtxt, +fn inner_value_snapshot( + ccx: &mut CheatsCtxt, group: Option, name: Option, value: String, @@ -795,8 +783,8 @@ fn inner_value_snapshot( Ok(Default::default()) } -fn inner_last_gas_snapshot( - ccx: &mut CheatsCtxt, +fn inner_last_gas_snapshot( + ccx: &mut CheatsCtxt, group: Option, name: Option, value: u64, @@ -808,8 +796,8 @@ fn inner_last_gas_snapshot( Ok(value.abi_encode()) } -fn inner_start_gas_snapshot( - ccx: &mut CheatsCtxt, +fn inner_start_gas_snapshot( + ccx: &mut CheatsCtxt, group: Option, name: Option, ) -> Result { @@ -835,8 +823,8 @@ fn inner_start_gas_snapshot( Ok(Default::default()) } -fn inner_stop_gas_snapshot( - ccx: &mut CheatsCtxt, +fn inner_stop_gas_snapshot( + ccx: &mut CheatsCtxt, group: Option, name: Option, ) -> Result { @@ -886,8 +874,8 @@ fn inner_stop_gas_snapshot( } // Derives the snapshot group and name from the provided group and name or the running contract. -fn derive_snapshot_name( - ccx: &CheatsCtxt, +fn derive_snapshot_name( + ccx: &CheatsCtxt, group: Option, name: Option, ) -> (String, String) { @@ -947,10 +935,10 @@ fn read_callers(state: &Cheatcodes, default_sender: &Address) -> Result { } /// Ensures the `Account` is loaded and touched. -pub(super) fn journaled_account( - ecx: &mut InnerEvmContext, +pub(super) fn journaled_account<'a>( + ecx: InnerEcx<'a, '_, '_>, addr: Address, -) -> Result<&mut Account> { +) -> Result<&'a mut Account> { ecx.load_account(addr)?; ecx.journaled_state.touch(&addr); Ok(ecx.journaled_state.state.get_mut(&addr).expect("account is loaded")) diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs index a8cc83000..84fda7883 100644 --- a/crates/cheatcodes/src/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -1,4 +1,4 @@ -use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*}; +use crate::{Cheatcode, Cheatcodes, CheatcodesExecutor, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_dyn_abi::DynSolValue; use alloy_primitives::{B256, U256}; use alloy_provider::Provider; @@ -8,7 +8,7 @@ use foundry_common::provider::ProviderBuilder; use foundry_evm_core::fork::CreateFork; impl Cheatcode for activeForkCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; ccx.ecx .db @@ -19,49 +19,49 @@ impl Cheatcode for activeForkCall { } impl Cheatcode for createFork_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { urlOrAlias } = self; create_fork(ccx, urlOrAlias, None) } } impl Cheatcode for createFork_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { urlOrAlias, blockNumber } = self; create_fork(ccx, urlOrAlias, Some(blockNumber.saturating_to())) } } impl Cheatcode for createFork_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { urlOrAlias, txHash } = self; create_fork_at_transaction(ccx, urlOrAlias, txHash) } } impl Cheatcode for createSelectFork_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { urlOrAlias } = self; create_select_fork(ccx, urlOrAlias, None) } } impl Cheatcode for createSelectFork_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { urlOrAlias, blockNumber } = self; create_select_fork(ccx, urlOrAlias, Some(blockNumber.saturating_to())) } } impl Cheatcode for createSelectFork_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { urlOrAlias, txHash } = self; create_select_fork_at_transaction(ccx, urlOrAlias, txHash) } } impl Cheatcode for rollFork_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { blockNumber } = self; persist_caller(ccx); ccx.ecx.db.roll_fork( @@ -75,7 +75,7 @@ impl Cheatcode for rollFork_0Call { } impl Cheatcode for rollFork_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { txHash } = self; persist_caller(ccx); ccx.ecx.db.roll_fork_to_transaction( @@ -89,7 +89,7 @@ impl Cheatcode for rollFork_1Call { } impl Cheatcode for rollFork_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { forkId, blockNumber } = self; persist_caller(ccx); ccx.ecx.db.roll_fork( @@ -103,7 +103,7 @@ impl Cheatcode for rollFork_2Call { } impl Cheatcode for rollFork_3Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { forkId, txHash } = self; persist_caller(ccx); ccx.ecx.db.roll_fork_to_transaction( @@ -117,7 +117,7 @@ impl Cheatcode for rollFork_3Call { } impl Cheatcode for selectForkCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { forkId } = self; persist_caller(ccx); check_broadcast(ccx.state)?; @@ -128,11 +128,7 @@ impl Cheatcode for selectForkCall { } impl Cheatcode for transact_0Call { - fn apply_full( - &self, - ccx: &mut CheatsCtxt, - executor: &mut E, - ) -> Result { + fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { let Self { txHash } = *self; ccx.ecx.db.transact( None, @@ -146,25 +142,21 @@ impl Cheatcode for transact_0Call { } impl Cheatcode for transact_1Call { - fn apply_full( - &self, - ccx: &mut CheatsCtxt, - executor: &mut E, - ) -> Result { + fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { let Self { forkId, txHash } = *self; ccx.ecx.db.transact( Some(forkId), txHash, &mut ccx.ecx.env, &mut ccx.ecx.journaled_state, - &mut executor.get_inspector(ccx.state), + &mut *executor.get_inspector(ccx.state), )?; Ok(Default::default()) } } impl Cheatcode for allowCheatcodesCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account } = self; ccx.ecx.db.allow_cheatcode_access(*account); Ok(Default::default()) @@ -172,7 +164,7 @@ impl Cheatcode for allowCheatcodesCall { } impl Cheatcode for makePersistent_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account } = self; ccx.ecx.db.add_persistent_account(*account); Ok(Default::default()) @@ -180,7 +172,7 @@ impl Cheatcode for makePersistent_0Call { } impl Cheatcode for makePersistent_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account0, account1 } = self; ccx.ecx.db.add_persistent_account(*account0); ccx.ecx.db.add_persistent_account(*account1); @@ -189,7 +181,7 @@ impl Cheatcode for makePersistent_1Call { } impl Cheatcode for makePersistent_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account0, account1, account2 } = self; ccx.ecx.db.add_persistent_account(*account0); ccx.ecx.db.add_persistent_account(*account1); @@ -199,7 +191,7 @@ impl Cheatcode for makePersistent_2Call { } impl Cheatcode for makePersistent_3Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { accounts } = self; for account in accounts { ccx.ecx.db.add_persistent_account(*account); @@ -209,7 +201,7 @@ impl Cheatcode for makePersistent_3Call { } impl Cheatcode for revokePersistent_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account } = self; ccx.ecx.db.remove_persistent_account(account); Ok(Default::default()) @@ -217,7 +209,7 @@ impl Cheatcode for revokePersistent_0Call { } impl Cheatcode for revokePersistent_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { accounts } = self; for account in accounts { ccx.ecx.db.remove_persistent_account(account); @@ -227,14 +219,14 @@ impl Cheatcode for revokePersistent_1Call { } impl Cheatcode for isPersistentCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account } = self; Ok(ccx.ecx.db.is_persistent(account).abi_encode()) } } impl Cheatcode for rpc_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { method, params } = self; let url = ccx.ecx.db.active_fork_url().ok_or_else(|| fmt_err!("no active fork URL found"))?; @@ -251,7 +243,7 @@ impl Cheatcode for rpc_1Call { } impl Cheatcode for eth_getLogsCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { fromBlock, toBlock, target, topics } = self; let (Ok(from_block), Ok(to_block)) = (u64::try_from(fromBlock), u64::try_from(toBlock)) else { @@ -293,11 +285,7 @@ impl Cheatcode for eth_getLogsCall { } /// Creates and then also selects the new fork -fn create_select_fork( - ccx: &mut CheatsCtxt, - url_or_alias: &str, - block: Option, -) -> Result { +fn create_select_fork(ccx: &mut CheatsCtxt, url_or_alias: &str, block: Option) -> Result { check_broadcast(ccx.state)?; let fork = create_fork_request(ccx, url_or_alias, block)?; @@ -306,19 +294,15 @@ fn create_select_fork( } /// Creates a new fork -fn create_fork( - ccx: &mut CheatsCtxt, - url_or_alias: &str, - block: Option, -) -> Result { +fn create_fork(ccx: &mut CheatsCtxt, url_or_alias: &str, block: Option) -> Result { let fork = create_fork_request(ccx, url_or_alias, block)?; let id = ccx.ecx.db.create_fork(fork)?; Ok(id.abi_encode()) } /// Creates and then also selects the new fork at the given transaction -fn create_select_fork_at_transaction( - ccx: &mut CheatsCtxt, +fn create_select_fork_at_transaction( + ccx: &mut CheatsCtxt, url_or_alias: &str, transaction: &B256, ) -> Result { @@ -335,8 +319,8 @@ fn create_select_fork_at_transaction( } /// Creates a new fork at the given transaction -fn create_fork_at_transaction( - ccx: &mut CheatsCtxt, +fn create_fork_at_transaction( + ccx: &mut CheatsCtxt, url_or_alias: &str, transaction: &B256, ) -> Result { @@ -346,8 +330,8 @@ fn create_fork_at_transaction( } /// Creates the request object for a new fork request -fn create_fork_request( - ccx: &mut CheatsCtxt, +fn create_fork_request( + ccx: &mut CheatsCtxt, url_or_alias: &str, block: Option, ) -> Result { @@ -380,7 +364,7 @@ fn check_broadcast(state: &Cheatcodes) -> Result<()> { // Applies to create, select and roll forks actions. // https://github.com/foundry-rs/foundry/issues/8004 #[inline] -fn persist_caller(ccx: &mut CheatsCtxt) { +fn persist_caller(ccx: &mut CheatsCtxt) { ccx.ecx.db.add_persistent_account(ccx.caller); } diff --git a/crates/cheatcodes/src/evm/mock.rs b/crates/cheatcodes/src/evm/mock.rs index 01a02c5c2..ab858c612 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 crate::{inspector::InnerEcx, Cheatcode, Cheatcodes, CheatsCtxt, Result, Vm::*}; use alloy_primitives::{Address, Bytes, U256}; -use revm::{interpreter::InstructionResult, primitives::Bytecode, InnerEvmContext}; +use revm::{interpreter::InstructionResult, primitives::Bytecode}; use std::cmp::Ordering; /// Mocked call data. @@ -47,7 +47,7 @@ impl Cheatcode for clearMockedCallsCall { } impl Cheatcode for mockCall_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { callee, data, returnData } = self; let _ = make_acc_non_empty(callee, ccx.ecx)?; @@ -57,7 +57,7 @@ impl Cheatcode for mockCall_0Call { } impl Cheatcode for mockCall_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { callee, msgValue, data, returnData } = self; ccx.ecx.load_account(*callee)?; mock_call(ccx.state, callee, data, Some(msgValue), returnData, InstructionResult::Return); @@ -66,7 +66,7 @@ impl Cheatcode for mockCall_1Call { } impl Cheatcode for mockCallRevert_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { callee, data, revertData } = self; let _ = make_acc_non_empty(callee, ccx.ecx)?; @@ -76,7 +76,7 @@ impl Cheatcode for mockCallRevert_0Call { } impl Cheatcode for mockCallRevert_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { callee, msgValue, data, revertData } = self; let _ = make_acc_non_empty(callee, ccx.ecx)?; @@ -111,7 +111,7 @@ fn mock_call( // 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 { +fn make_acc_non_empty(callee: &Address, ecx: InnerEcx) -> Result { let acc = ecx.load_account(*callee)?; let empty_bytecode = acc.info.code.as_ref().map_or(true, Bytecode::is_empty); diff --git a/crates/cheatcodes/src/evm/prank.rs b/crates/cheatcodes/src/evm/prank.rs index fe5418b31..a310e28e5 100644 --- a/crates/cheatcodes/src/evm/prank.rs +++ b/crates/cheatcodes/src/evm/prank.rs @@ -1,4 +1,4 @@ -use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*}; +use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Result, Vm::*}; use alloy_primitives::Address; /// Prank information. @@ -45,28 +45,28 @@ impl Prank { } impl Cheatcode for prank_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { msgSender } = self; prank(ccx, msgSender, None, true) } } impl Cheatcode for startPrank_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { msgSender } = self; prank(ccx, msgSender, None, false) } } impl Cheatcode for prank_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { msgSender, txOrigin } = self; prank(ccx, msgSender, Some(txOrigin), true) } } impl Cheatcode for startPrank_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { msgSender, txOrigin } = self; prank(ccx, msgSender, Some(txOrigin), false) } @@ -80,8 +80,8 @@ impl Cheatcode for stopPrankCall { } } -fn prank( - ccx: &mut CheatsCtxt, +fn prank( + ccx: &mut CheatsCtxt, new_caller: &Address, new_origin: Option<&Address>, single_call: bool, diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 4185b2d79..f36c8d6fe 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -9,7 +9,6 @@ use alloy_sol_types::SolValue; use dialoguer::{Input, Password}; use foundry_common::fs; use foundry_config::fs_permissions::FsAccessKind; -use foundry_evm_core::backend::DatabaseExt; use revm::interpreter::CreateInputs; use semver::Version; use std::{ @@ -293,11 +292,7 @@ impl Cheatcode for getDeployedCodeCall { } impl Cheatcode for deployCode_0Call { - fn apply_full( - &self, - ccx: &mut CheatsCtxt, - executor: &mut E, - ) -> Result { + fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { let Self { artifactPath: path } = self; let bytecode = get_artifact_code(ccx.state, path, false)?; let address = executor @@ -319,11 +314,7 @@ impl Cheatcode for deployCode_0Call { } impl Cheatcode for deployCode_1Call { - fn apply_full( - &self, - ccx: &mut CheatsCtxt, - executor: &mut E, - ) -> Result { + fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { let Self { artifactPath: path, constructorArgs } = self; let mut bytecode = get_artifact_code(ccx.state, path, false)?.to_vec(); bytecode.extend_from_slice(constructorArgs); diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 3ef5859ec..fe8834d5f 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -17,8 +17,8 @@ use crate::{ }, }, utils::IgnoredTraces, - CheatsConfig, CheatsCtxt, DynCheatcode, Error, Result, Vm, - Vm::AccountAccess, + CheatsConfig, CheatsCtxt, DynCheatcode, Error, Result, + Vm::{self, AccountAccess}, }; use alloy_primitives::{ hex, @@ -31,7 +31,7 @@ use foundry_common::{evm::Breakpoints, TransactionMaybeSigned, SELECTOR_LEN}; use foundry_config::Config; use foundry_evm_core::{ abi::Vm::stopExpectSafeMemoryCall, - backend::{DatabaseExt, RevertDiagnostic}, + backend::{DatabaseError, DatabaseExt, RevertDiagnostic}, constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME}, utils::new_evm_with_existing_context, InspectorExt, @@ -62,6 +62,9 @@ use std::{ mod utils; +pub type Ecx<'a, 'b, 'c> = &'a mut EvmContext<&'b mut (dyn DatabaseExt + 'c)>; +pub type InnerEcx<'a, 'b, 'c> = &'a mut InnerEvmContext<&'b mut (dyn DatabaseExt + 'c)>; + /// Helper trait for obtaining complete [revm::Inspector] instance from mutable reference to /// [Cheatcodes]. /// @@ -70,71 +73,30 @@ mod utils; pub trait CheatcodesExecutor { /// Core trait method accepting mutable reference to [Cheatcodes] and returning /// [revm::Inspector]. - fn get_inspector<'a, DB: DatabaseExt>( - &'a mut self, - cheats: &'a mut Cheatcodes, - ) -> impl InspectorExt + 'a; - - /// Constructs [revm::Evm] and runs a given closure with it. - fn with_evm( - &mut self, - ccx: &mut CheatsCtxt, - f: F, - ) -> Result> - where - F: for<'a, 'b> FnOnce( - &mut revm::Evm< - '_, - &'b mut dyn InspectorExt<&'a mut dyn DatabaseExt>, - &'a mut dyn DatabaseExt, - >, - ) -> Result>, - { - let mut inspector = self.get_inspector(ccx.state); - let error = std::mem::replace(&mut ccx.ecx.error, Ok(())); - let l1_block_info = std::mem::take(&mut ccx.ecx.l1_block_info); - - let inner = revm::InnerEvmContext { - env: ccx.ecx.env.clone(), - journaled_state: std::mem::replace( - &mut ccx.ecx.journaled_state, - revm::JournaledState::new(Default::default(), Default::default()), - ), - db: &mut ccx.ecx.db as &mut dyn DatabaseExt, - error, - l1_block_info, - }; - - let mut evm = new_evm_with_existing_context(inner, &mut inspector as _); - - let res = f(&mut evm)?; - - ccx.ecx.journaled_state = evm.context.evm.inner.journaled_state; - 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; - - Ok(res) - } + fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box; /// Obtains [revm::Evm] instance and executes the given CREATE frame. - fn exec_create( + fn exec_create( &mut self, inputs: CreateInputs, - ccx: &mut CheatsCtxt, - ) -> Result> { - self.with_evm(ccx, |evm| { + ccx: &mut CheatsCtxt, + ) -> Result> { + with_evm(self, ccx, |evm| { evm.context.evm.inner.journaled_state.depth += 1; // 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) + 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, - })), + 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))? @@ -158,8 +120,8 @@ pub trait CheatcodesExecutor { }) } - fn console_log(&mut self, ccx: &mut CheatsCtxt, message: String) { - self.get_inspector::(ccx.state).console_log(message); + 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. @@ -168,17 +130,53 @@ pub trait CheatcodesExecutor { } } +/// Constructs [revm::Evm] and runs a given closure with it. +fn with_evm( + executor: &mut E, + ccx: &mut CheatsCtxt, + f: F, +) -> Result> +where + E: CheatcodesExecutor + ?Sized, + F: for<'a, 'b> FnOnce( + &mut revm::Evm<'_, &'b mut dyn InspectorExt, &'a mut dyn DatabaseExt>, + ) -> Result>, +{ + let mut inspector = executor.get_inspector(ccx.state); + let error = std::mem::replace(&mut ccx.ecx.error, Ok(())); + let l1_block_info = std::mem::take(&mut ccx.ecx.l1_block_info); + + let inner = revm::InnerEvmContext { + env: ccx.ecx.env.clone(), + journaled_state: std::mem::replace( + &mut ccx.ecx.journaled_state, + revm::JournaledState::new(Default::default(), Default::default()), + ), + db: &mut ccx.ecx.db as &mut dyn DatabaseExt, + error, + l1_block_info, + }; + + let mut evm = new_evm_with_existing_context(inner, &mut *inspector); + + let res = f(&mut evm)?; + + ccx.ecx.journaled_state = evm.context.evm.inner.journaled_state; + 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; + + Ok(res) +} + /// Basic implementation of [CheatcodesExecutor] that simply returns the [Cheatcodes] instance as an /// inspector. #[derive(Debug, Default, Clone, Copy)] struct TransparentCheatcodesExecutor; impl CheatcodesExecutor for TransparentCheatcodesExecutor { - fn get_inspector<'a, DB: DatabaseExt>( - &'a mut self, - cheats: &'a mut Cheatcodes, - ) -> impl InspectorExt + 'a { - cheats + fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box { + Box::new(cheats) } } @@ -306,13 +304,7 @@ impl ArbitraryStorage { /// 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, - ) { + pub fn save(&mut self, ecx: InnerEcx, 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)); @@ -324,13 +316,7 @@ impl ArbitraryStorage { /// 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 { + pub fn copy(&mut self, ecx: InnerEcx, 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) { @@ -535,11 +521,11 @@ impl Cheatcodes { } /// Decodes the input data and applies the cheatcode. - fn apply_cheatcode( + fn apply_cheatcode( &mut self, - ecx: &mut EvmContext, + ecx: Ecx, call: &CallInputs, - executor: &mut E, + executor: &mut dyn CheatcodesExecutor, ) -> Result { // decode the cheatcode call let decoded = Vm::VmCalls::abi_decode(&call.input, false).map_err(|e| { @@ -578,12 +564,7 @@ impl Cheatcodes { /// /// There may be cheatcodes in the constructor of the new contract, in order to allow them /// automatically we need to determine the new address. - fn allow_cheatcodes_on_create( - &self, - ecx: &mut InnerEvmContext, - caller: Address, - created_address: Address, - ) { + fn allow_cheatcodes_on_create(&self, ecx: InnerEcx, caller: Address, created_address: Address) { if ecx.journaled_state.depth <= 1 || ecx.db.has_cheatcode_access(&caller) { ecx.db.allow_cheatcode_access(created_address); } @@ -593,7 +574,7 @@ impl Cheatcodes { /// /// Cleanup any previously applied cheatcodes that altered the state in such a way that revm's /// revert would run into issues. - pub fn on_revert(&mut self, ecx: &mut EvmContext) { + pub fn on_revert(&mut self, ecx: Ecx) { trace!(deals=?self.eth_deals.len(), "rolling back deals"); // Delay revert clean up until expected revert is handled, if set. @@ -617,14 +598,9 @@ impl Cheatcodes { } // common create functionality for both legacy and EOF. - fn create_common( - &mut self, - ecx: &mut EvmContext, - mut input: Input, - ) -> Option + fn create_common(&mut self, ecx: Ecx, mut input: Input) -> Option where - DB: DatabaseExt, - Input: CommonCreateInput, + Input: CommonCreateInput, { let ecx = &mut ecx.inner; let gas = Gas::new(input.gas_limit()); @@ -717,14 +693,8 @@ impl Cheatcodes { } // common create_end functionality for both legacy and EOF. - fn create_end_common( - &mut self, - ecx: &mut EvmContext, - mut outcome: CreateOutcome, - ) -> CreateOutcome - where - DB: DatabaseExt, - { + fn create_end_common(&mut self, ecx: Ecx, mut outcome: CreateOutcome) -> CreateOutcome +where { let ecx = &mut ecx.inner; // Clean up pranks @@ -831,9 +801,9 @@ impl Cheatcodes { outcome } - pub fn call_with_executor( + pub fn call_with_executor( &mut self, - ecx: &mut EvmContext, + ecx: Ecx, call: &mut CallInputs, executor: &mut impl CheatcodesExecutor, ) -> Option { @@ -1127,9 +1097,9 @@ impl Cheatcodes { } } -impl Inspector for Cheatcodes { +impl Inspector<&mut dyn DatabaseExt> for Cheatcodes { #[inline] - fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { + fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: Ecx) { // 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() { @@ -1146,7 +1116,7 @@ impl Inspector for Cheatcodes { } #[inline] - fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { + fn step(&mut self, interpreter: &mut Interpreter, ecx: Ecx) { self.pc = interpreter.program_counter(); // `pauseGasMetering`: pause / resume interpreter gas. @@ -1186,7 +1156,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: Ecx) { if self.gas_metering.paused { self.meter_gas_end(interpreter); } @@ -1201,7 +1171,7 @@ impl Inspector for Cheatcodes { } } - fn log(&mut self, interpreter: &mut Interpreter, _ecx: &mut EvmContext, log: &Log) { + fn log(&mut self, interpreter: &mut Interpreter, _ecx: Ecx, log: &Log) { if !self.expected_emits.is_empty() { expect::handle_expect_emit(self, log, interpreter); } @@ -1216,16 +1186,11 @@ impl Inspector for Cheatcodes { } } - fn call(&mut self, ecx: &mut EvmContext, inputs: &mut CallInputs) -> Option { + fn call(&mut self, ecx: Ecx, inputs: &mut CallInputs) -> Option { Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor) } - fn call_end( - &mut self, - ecx: &mut EvmContext, - call: &CallInputs, - mut outcome: CallOutcome, - ) -> CallOutcome { + fn call_end(&mut self, ecx: Ecx, call: &CallInputs, mut outcome: CallOutcome) -> CallOutcome { let ecx = &mut ecx.inner; let cheatcode_call = call.target_address == CHEATCODE_ADDRESS || call.target_address == HARDHAT_CONSOLE_ADDRESS; @@ -1521,34 +1486,26 @@ impl Inspector for Cheatcodes { outcome } - fn create( - &mut self, - ecx: &mut EvmContext, - call: &mut CreateInputs, - ) -> Option { + fn create(&mut self, ecx: Ecx, call: &mut CreateInputs) -> Option { self.create_common(ecx, call) } fn create_end( &mut self, - ecx: &mut EvmContext, + ecx: Ecx, _call: &CreateInputs, outcome: CreateOutcome, ) -> CreateOutcome { self.create_end_common(ecx, outcome) } - fn eofcreate( - &mut self, - ecx: &mut EvmContext, - call: &mut EOFCreateInputs, - ) -> Option { + fn eofcreate(&mut self, ecx: Ecx, call: &mut EOFCreateInputs) -> Option { self.create_common(ecx, call) } fn eofcreate_end( &mut self, - ecx: &mut EvmContext, + ecx: Ecx, _call: &EOFCreateInputs, outcome: CreateOutcome, ) -> CreateOutcome { @@ -1556,12 +1513,8 @@ impl Inspector for Cheatcodes { } } -impl InspectorExt for Cheatcodes { - fn should_use_create2_factory( - &mut self, - ecx: &mut EvmContext, - inputs: &mut CreateInputs, - ) -> bool { +impl InspectorExt for Cheatcodes { + fn should_use_create2_factory(&mut self, ecx: Ecx, inputs: &mut CreateInputs) -> bool { if let CreateScheme::Create2 { .. } = inputs.scheme { let target_depth = if let Some(prank) = &self.prank { prank.depth @@ -1592,11 +1545,7 @@ impl Cheatcodes { } #[cold] - fn meter_gas_record( - &mut self, - interpreter: &mut Interpreter, - ecx: &mut EvmContext, - ) { + fn meter_gas_record(&mut self, interpreter: &mut Interpreter, ecx: Ecx) { if matches!(interpreter.instruction_result, InstructionResult::Continue) { self.gas_metering.gas_records.iter_mut().for_each(|record| { if ecx.journaled_state.depth() == record.depth { @@ -1652,11 +1601,7 @@ impl Cheatcodes { /// 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, - ) { + fn arbitrary_storage_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) { let (key, target_address) = if interpreter.current_opcode() == op::SLOAD { (try_or_return!(interpreter.stack().peek(0)), interpreter.contract().target_address) } else { @@ -1706,11 +1651,7 @@ impl Cheatcodes { } #[cold] - fn record_state_diffs( - &mut self, - interpreter: &mut Interpreter, - ecx: &mut EvmContext, - ) { + fn record_state_diffs(&mut self, interpreter: &mut Interpreter, ecx: Ecx) { let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return }; match interpreter.current_opcode() { op::SELFDESTRUCT => { @@ -2057,10 +1998,7 @@ fn disallowed_mem_write( // Determines if the gas limit on a given call was manually set in the script and should therefore // not be overwritten by later estimations -fn check_if_fixed_gas_limit( - ecx: &InnerEvmContext, - call_gas_limit: u64, -) -> bool { +fn check_if_fixed_gas_limit(ecx: InnerEcx, call_gas_limit: u64) -> bool { // If the gas limit was not set in the source code it is set to the estimated gas left at the // time of the call, which should be rather close to configured gas limit. // TODO: Find a way to reliably make this determination. @@ -2130,44 +2068,25 @@ fn append_storage_access( } /// Dispatches the cheatcode call to the appropriate function. -fn apply_dispatch( +fn apply_dispatch( calls: &Vm::VmCalls, - ccx: &mut CheatsCtxt, - executor: &mut E, + ccx: &mut CheatsCtxt, + executor: &mut dyn CheatcodesExecutor, ) -> Result { - // TODO: Replace with `::apply_full` once it's object-safe. - macro_rules! dispatch { - ($($variant:ident),*) => { - match calls { - $(Vm::VmCalls::$variant(cheat) => crate::Cheatcode::apply_full(cheat, ccx, executor),)* - } - }; - } - let cheat = calls_as_dyn_cheatcode(calls); + + let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheat.id()).entered(); + trace!(target: "cheatcodes", cheat = ?cheat.as_debug(), "applying"); + if let spec::Status::Deprecated(replacement) = *cheat.status() { ccx.state.deprecated.insert(cheat.signature(), replacement); } - let _guard = trace_span_and_call(cheat); - let mut result = vm_calls!(dispatch); - fill_and_trace_return(cheat, &mut result); - result -} -/// Helper function to check if frame execution will exit. -fn will_exit(ir: InstructionResult) -> bool { - !matches!(ir, InstructionResult::Continue | InstructionResult::CallOrCreate) -} + // Apply the cheatcode. + let mut result = cheat.dyn_apply(ccx, executor); -fn trace_span_and_call(cheat: &dyn DynCheatcode) -> tracing::span::EnteredSpan { - let span = debug_span!(target: "cheatcodes", "apply", id = %cheat.id()); - let entered = span.entered(); - trace!(target: "cheatcodes", cheat = ?cheat.as_debug(), "applying"); - entered -} - -fn fill_and_trace_return(cheat: &dyn DynCheatcode, result: &mut Result) { - if let Err(e) = result { + // Format the error message to include the cheatcode name. + if let Err(e) = &mut result { if e.is_str() { let name = cheat.name(); // Skip showing the cheatcode name for: @@ -2178,13 +2097,16 @@ fn fill_and_trace_return(cheat: &dyn DynCheatcode, result: &mut Result) { } } } + trace!( target: "cheatcodes", - return = %match result { + return = %match &result { Ok(b) => hex::encode(b), Err(e) => e.to_string(), } ); + + result } fn calls_as_dyn_cheatcode(calls: &Vm::VmCalls) -> &dyn DynCheatcode { @@ -2197,3 +2119,8 @@ fn calls_as_dyn_cheatcode(calls: &Vm::VmCalls) -> &dyn DynCheatcode { } vm_calls!(as_dyn) } + +/// Helper function to check if frame execution will exit. +fn will_exit(ir: InstructionResult) -> bool { + !matches!(ir, InstructionResult::Continue | InstructionResult::CallOrCreate) +} diff --git a/crates/cheatcodes/src/inspector/utils.rs b/crates/cheatcodes/src/inspector/utils.rs index dfccd4b55..a0d7820aa 100644 --- a/crates/cheatcodes/src/inspector/utils.rs +++ b/crates/cheatcodes/src/inspector/utils.rs @@ -1,13 +1,10 @@ +use super::InnerEcx; use crate::inspector::Cheatcodes; use alloy_primitives::{Address, Bytes, U256}; -use foundry_evm_core::backend::DatabaseExt; -use revm::{ - interpreter::{CreateInputs, CreateScheme, EOFCreateInputs, EOFCreateKind}, - InnerEvmContext, -}; +use revm::interpreter::{CreateInputs, CreateScheme, EOFCreateInputs, EOFCreateKind}; /// Common behaviour of legacy and EOF create inputs. -pub(crate) trait CommonCreateInput { +pub(crate) trait CommonCreateInput { fn caller(&self) -> Address; fn gas_limit(&self) -> u64; fn value(&self) -> U256; @@ -15,15 +12,11 @@ pub(crate) trait CommonCreateInput { fn scheme(&self) -> Option; fn set_caller(&mut self, caller: Address); fn log_debug(&self, cheatcode: &mut Cheatcodes, scheme: &CreateScheme); - fn allow_cheatcodes( - &self, - cheatcodes: &mut Cheatcodes, - ecx: &mut InnerEvmContext, - ) -> Address; + fn allow_cheatcodes(&self, cheatcodes: &mut Cheatcodes, ecx: InnerEcx) -> Address; fn computed_created_address(&self) -> Option
; } -impl CommonCreateInput for &mut CreateInputs { +impl CommonCreateInput for &mut CreateInputs { fn caller(&self) -> Address { self.caller } @@ -49,11 +42,7 @@ impl CommonCreateInput for &mut CreateInputs { }; debug!(target: "cheatcodes", tx=?cheatcode.broadcastable_transactions.back().unwrap(), "broadcastable {kind}"); } - fn allow_cheatcodes( - &self, - cheatcodes: &mut Cheatcodes, - ecx: &mut InnerEvmContext, - ) -> Address { + fn allow_cheatcodes(&self, cheatcodes: &mut Cheatcodes, ecx: InnerEcx) -> Address { let old_nonce = ecx .journaled_state .state @@ -69,7 +58,7 @@ impl CommonCreateInput for &mut CreateInputs { } } -impl CommonCreateInput for &mut EOFCreateInputs { +impl CommonCreateInput for &mut EOFCreateInputs { fn caller(&self) -> Address { self.caller } @@ -94,13 +83,9 @@ impl CommonCreateInput for &mut EOFCreateInputs { fn log_debug(&self, cheatcode: &mut Cheatcodes, _scheme: &CreateScheme) { debug!(target: "cheatcodes", tx=?cheatcode.broadcastable_transactions.back().unwrap(), "broadcastable eofcreate"); } - fn allow_cheatcodes( - &self, - cheatcodes: &mut Cheatcodes, - ecx: &mut InnerEvmContext, - ) -> Address { + fn allow_cheatcodes(&self, cheatcodes: &mut Cheatcodes, ecx: InnerEcx) -> Address { let created_address = - <&mut EOFCreateInputs as CommonCreateInput>::computed_created_address(self) + <&mut EOFCreateInputs as CommonCreateInput>::computed_created_address(self) .unwrap_or_default(); cheatcodes.allow_cheatcodes_on_create(ecx, self.caller, created_address); created_address diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index 50bd54701..a25728991 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -70,7 +70,7 @@ pub(crate) trait Cheatcode: CheatcodeDef + DynCheatcode { /// /// Implement this function if you need access to the EVM data. #[inline(always)] - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { self.apply(ccx.state) } @@ -78,58 +78,69 @@ pub(crate) trait Cheatcode: CheatcodeDef + DynCheatcode { /// /// Implement this function if you need access to the executor. #[inline(always)] - fn apply_full( - &self, - ccx: &mut CheatsCtxt, - executor: &mut E, - ) -> Result { + fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { let _ = executor; self.apply_stateful(ccx) } } pub(crate) trait DynCheatcode { - fn name(&self) -> &'static str; - fn id(&self) -> &'static str; - fn signature(&self) -> &'static str; - fn status(&self) -> &Status<'static>; - fn as_debug(&self) -> &dyn std::fmt::Debug; -} + fn cheatcode(&self) -> &'static spec::Cheatcode<'static>; -impl DynCheatcode for T { fn name(&self) -> &'static str { - T::CHEATCODE.func.signature.split('(').next().unwrap() + self.cheatcode().func.signature.split('(').next().unwrap() } + fn id(&self) -> &'static str { - T::CHEATCODE.func.id + self.cheatcode().func.id } + fn signature(&self) -> &'static str { - T::CHEATCODE.func.signature + self.cheatcode().func.signature } + fn status(&self) -> &Status<'static> { - &T::CHEATCODE.status + &self.cheatcode().status } + + fn as_debug(&self) -> &dyn std::fmt::Debug; + + fn dyn_apply(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result; +} + +impl DynCheatcode for T { + #[inline] + fn cheatcode(&self) -> &'static spec::Cheatcode<'static> { + Self::CHEATCODE + } + + #[inline] fn as_debug(&self) -> &dyn std::fmt::Debug { self } + + #[inline] + fn dyn_apply(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { + self.apply_full(ccx, executor) + } } /// The cheatcode context, used in `Cheatcode`. -pub struct CheatsCtxt<'cheats, 'evm, DB: DatabaseExt> { +pub struct CheatsCtxt<'cheats, 'evm, 'db, 'db2> { /// The cheatcodes inspector state. pub(crate) state: &'cheats mut Cheatcodes, /// The EVM data. - pub(crate) ecx: &'evm mut InnerEvmContext, + pub(crate) ecx: &'evm mut InnerEvmContext<&'db mut (dyn DatabaseExt + 'db2)>, /// The precompiles context. - pub(crate) precompiles: &'evm mut ContextPrecompiles, + pub(crate) precompiles: &'evm mut ContextPrecompiles<&'db mut (dyn DatabaseExt + 'db2)>, /// The original `msg.sender`. pub(crate) caller: Address, /// Gas limit of the current cheatcode call. pub(crate) gas_limit: u64, } -impl<'cheats, 'evm, DB: DatabaseExt> std::ops::Deref for CheatsCtxt<'cheats, 'evm, DB> { - type Target = InnerEvmContext; +impl<'db, 'db2> std::ops::Deref for CheatsCtxt<'_, '_, 'db, 'db2> { + type Target = InnerEvmContext<&'db mut (dyn DatabaseExt + 'db2)>; #[inline(always)] fn deref(&self) -> &Self::Target { @@ -137,14 +148,14 @@ impl<'cheats, 'evm, DB: DatabaseExt> std::ops::Deref for CheatsCtxt<'cheats, 'ev } } -impl<'cheats, 'evm, DB: DatabaseExt> std::ops::DerefMut for CheatsCtxt<'cheats, 'evm, DB> { +impl std::ops::DerefMut for CheatsCtxt<'_, '_, '_, '_> { #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { &mut *self.ecx } } -impl<'cheats, 'evm, DB: DatabaseExt> CheatsCtxt<'cheats, 'evm, DB> { +impl CheatsCtxt<'_, '_, '_, '_> { #[inline] pub(crate) fn is_precompile(&self, address: &Address) -> bool { self.precompiles.contains(address) diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index 82eef2354..93d5aaaf8 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -1,6 +1,6 @@ //! Implementations of [`Scripting`](spec::Group::Scripting) cheatcodes. -use crate::{Cheatcode, CheatsCtxt, DatabaseExt, Result, Vm::*}; +use crate::{Cheatcode, CheatsCtxt, Result, Vm::*}; use alloy_primitives::{Address, B256, U256}; use alloy_signer_local::PrivateKeySigner; use foundry_wallets::{multi_wallet::MultiWallet, WalletSigner}; @@ -8,49 +8,49 @@ use parking_lot::Mutex; use std::sync::Arc; impl Cheatcode for broadcast_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; broadcast(ccx, None, true) } } impl Cheatcode for broadcast_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { signer } = self; broadcast(ccx, Some(signer), true) } } impl Cheatcode for broadcast_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { privateKey } = self; broadcast_key(ccx, privateKey, true) } } impl Cheatcode for startBroadcast_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; broadcast(ccx, None, false) } } impl Cheatcode for startBroadcast_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { signer } = self; broadcast(ccx, Some(signer), false) } } impl Cheatcode for startBroadcast_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { privateKey } = self; broadcast_key(ccx, privateKey, false) } } impl Cheatcode for stopBroadcastCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; let Some(broadcast) = ccx.state.broadcast.take() else { bail!("no broadcast in progress to stop"); @@ -123,11 +123,7 @@ impl ScriptWallets { } /// Sets up broadcasting from a script using `new_origin` as the sender. -fn broadcast( - ccx: &mut CheatsCtxt, - new_origin: Option<&Address>, - single_call: bool, -) -> Result { +fn broadcast(ccx: &mut CheatsCtxt, new_origin: Option<&Address>, single_call: bool) -> Result { ensure!( ccx.state.prank.is_none(), "you have an active prank; broadcasting and pranks are not compatible" @@ -166,11 +162,7 @@ fn broadcast( /// Sets up broadcasting from a script with the sender derived from `private_key`. /// Adds this private key to `state`'s `script_wallets` vector to later be used for signing /// if broadcast is successful. -fn broadcast_key( - ccx: &mut CheatsCtxt, - private_key: &U256, - single_call: bool, -) -> Result { +fn broadcast_key(ccx: &mut CheatsCtxt, private_key: &U256, single_call: bool) -> Result { let wallet = super::crypto::parse_wallet(private_key)?; let new_origin = wallet.address(); diff --git a/crates/cheatcodes/src/test.rs b/crates/cheatcodes/src/test.rs index cc91dba45..bd723c931 100644 --- a/crates/cheatcodes/src/test.rs +++ b/crates/cheatcodes/src/test.rs @@ -1,26 +1,25 @@ //! Implementations of [`Testing`](spec::Group::Testing) cheatcodes. -use chrono::DateTime; -use std::env; - -use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*}; +use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Result, Vm::*}; use alloy_primitives::Address; use alloy_sol_types::SolValue; +use chrono::DateTime; use foundry_evm_core::constants::MAGIC_SKIP; +use std::env; pub(crate) mod assert; pub(crate) mod assume; pub(crate) mod expect; impl Cheatcode for breakpoint_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { char } = self; breakpoint(ccx.state, &ccx.caller, char, true) } } impl Cheatcode for breakpoint_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { char, value } = self; breakpoint(ccx.state, &ccx.caller, char, *value) } @@ -71,14 +70,14 @@ impl Cheatcode for sleepCall { } impl Cheatcode for skip_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { skipTest } = *self; skip_1Call { skipTest, reason: String::new() }.apply_stateful(ccx) } } impl Cheatcode for skip_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + 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. diff --git a/crates/cheatcodes/src/test/assert.rs b/crates/cheatcodes/src/test/assert.rs index 4ab97c031..5161716fa 100644 --- a/crates/cheatcodes/src/test/assert.rs +++ b/crates/cheatcodes/src/test/assert.rs @@ -2,7 +2,7 @@ use crate::{CheatcodesExecutor, CheatsCtxt, Result, Vm::*}; use alloy_primitives::{hex, I256, U256}; use foundry_evm_core::{ abi::{format_units_int, format_units_uint}, - backend::{DatabaseExt, GLOBAL_FAIL_SLOT}, + backend::GLOBAL_FAIL_SLOT, constants::CHEATCODE_ADDRESS, }; use itertools::Itertools; @@ -169,10 +169,10 @@ impl EqRelAssertionError { type ComparisonResult<'a, T> = Result, ComparisonAssertionError<'a, T>>; -fn handle_assertion_result( +fn handle_assertion_result( result: core::result::Result, ERR>, - ccx: &mut CheatsCtxt, - executor: &mut E, + ccx: &mut CheatsCtxt, + executor: &mut dyn CheatcodesExecutor, error_formatter: impl Fn(&ERR) -> String, error_msg: Option<&str>, format_error: bool, @@ -224,10 +224,10 @@ macro_rules! impl_assertions { }; (@impl $no_error:ident, $with_error:ident, ($($arg:ident),*), $body:expr, $error_formatter:expr, $format_error:literal) => { impl crate::Cheatcode for $no_error { - fn apply_full( + fn apply_full( &self, - ccx: &mut CheatsCtxt, - executor: &mut E, + ccx: &mut CheatsCtxt, + executor: &mut dyn CheatcodesExecutor, ) -> Result { let Self { $($arg),* } = self; handle_assertion_result($body, ccx, executor, $error_formatter, None, $format_error) @@ -235,10 +235,10 @@ macro_rules! impl_assertions { } impl crate::Cheatcode for $with_error { - fn apply_full( + fn apply_full( &self, - ccx: &mut CheatsCtxt, - executor: &mut E, + ccx: &mut CheatsCtxt, + executor: &mut dyn CheatcodesExecutor, ) -> Result { let Self { $($arg),*, error} = self; handle_assertion_result($body, ccx, executor, $error_formatter, Some(error), $format_error) diff --git a/crates/cheatcodes/src/test/assume.rs b/crates/cheatcodes/src/test/assume.rs index e100eeb9d..a0321b5a1 100644 --- a/crates/cheatcodes/src/test/assume.rs +++ b/crates/cheatcodes/src/test/assume.rs @@ -1,5 +1,5 @@ use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Error, Result}; -use foundry_evm_core::{backend::DatabaseExt, constants::MAGIC_ASSUME}; +use foundry_evm_core::constants::MAGIC_ASSUME; use spec::Vm::{assumeCall, assumeNoRevertCall}; use std::fmt::Debug; @@ -21,7 +21,7 @@ impl Cheatcode for assumeCall { } impl Cheatcode for assumeNoRevertCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + 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/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs index f38776f94..7a58c7ab8 100644 --- a/crates/cheatcodes/src/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -1,4 +1,4 @@ -use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Error, Result, Vm::*}; +use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Error, Result, Vm::*}; use alloy_primitives::{address, hex, Address, Bytes, LogData as RawLog, U256}; use alloy_sol_types::{SolError, SolValue}; use foundry_common::ContractsByArtifact; @@ -210,7 +210,7 @@ impl Cheatcode for expectCallMinGas_1Call { } impl Cheatcode for expectEmit_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { checkTopic1, checkTopic2, checkTopic3, checkData } = *self; expect_emit( ccx.state, @@ -223,7 +223,7 @@ impl Cheatcode for expectEmit_0Call { } impl Cheatcode for expectEmit_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self; expect_emit( ccx.state, @@ -236,21 +236,21 @@ impl Cheatcode for expectEmit_1Call { } impl Cheatcode for expectEmit_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, false) } } impl Cheatcode for expectEmit_3Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { emitter } = *self; expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], Some(emitter), false) } } impl Cheatcode for expectEmitAnonymous_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData } = *self; expect_emit( ccx.state, @@ -263,7 +263,7 @@ impl Cheatcode for expectEmitAnonymous_0Call { } impl Cheatcode for expectEmitAnonymous_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self; expect_emit( ccx.state, @@ -276,28 +276,28 @@ impl Cheatcode for expectEmitAnonymous_1Call { } impl Cheatcode for expectEmitAnonymous_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, true) } } impl Cheatcode for expectEmitAnonymous_3Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { emitter } = *self; expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], Some(emitter), true) } } impl Cheatcode for expectRevert_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), false, false, None) } } impl Cheatcode for expectRevert_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData } = self; expect_revert( ccx.state, @@ -311,7 +311,7 @@ impl Cheatcode for expectRevert_1Call { } impl Cheatcode for expectRevert_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData } = self; expect_revert( ccx.state, @@ -325,7 +325,7 @@ impl Cheatcode for expectRevert_2Call { } impl Cheatcode for expectRevert_3Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { reverter } = self; expect_revert( ccx.state, @@ -339,7 +339,7 @@ impl Cheatcode for expectRevert_3Call { } impl Cheatcode for expectRevert_4Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData, reverter } = self; expect_revert( ccx.state, @@ -353,7 +353,7 @@ impl Cheatcode for expectRevert_4Call { } impl Cheatcode for expectRevert_5Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData, reverter } = self; expect_revert( ccx.state, @@ -367,7 +367,7 @@ impl Cheatcode for expectRevert_5Call { } impl Cheatcode for expectPartialRevert_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData } = self; expect_revert( ccx.state, @@ -381,7 +381,7 @@ impl Cheatcode for expectPartialRevert_0Call { } impl Cheatcode for expectPartialRevert_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData, reverter } = self; expect_revert( ccx.state, @@ -395,13 +395,13 @@ impl Cheatcode for expectPartialRevert_1Call { } impl Cheatcode for _expectCheatcodeRevert_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), true, false, None) } } impl Cheatcode for _expectCheatcodeRevert_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData } = self; expect_revert( ccx.state, @@ -415,7 +415,7 @@ impl Cheatcode for _expectCheatcodeRevert_1Call { } impl Cheatcode for _expectCheatcodeRevert_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData } = self; expect_revert( ccx.state, @@ -429,14 +429,14 @@ impl Cheatcode for _expectCheatcodeRevert_2Call { } impl Cheatcode for expectSafeMemoryCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { min, max } = *self; expect_safe_memory(ccx.state, min, max, ccx.ecx.journaled_state.depth()) } } impl Cheatcode for stopExpectSafeMemoryCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; ccx.state.allowed_mem_writes.remove(&ccx.ecx.journaled_state.depth()); Ok(Default::default()) @@ -444,7 +444,7 @@ impl Cheatcode for stopExpectSafeMemoryCall { } impl Cheatcode for expectSafeMemoryCallCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { min, max } = *self; expect_safe_memory(ccx.state, min, max, ccx.ecx.journaled_state.depth() + 1) } diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index eb4d2f525..23cc02f7a 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -1,12 +1,12 @@ //! Implementations of [`Utilities`](spec::Group::Utilities) cheatcodes. -use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Result, Vm::*}; +use crate::{Cheatcode, Cheatcodes, CheatcodesExecutor, CheatsCtxt, Result, Vm::*}; use alloy_dyn_abi::{DynSolType, DynSolValue}; use alloy_primitives::{map::HashMap, U256}; use alloy_sol_types::SolValue; use foundry_common::ens::namehash; -use foundry_evm_core::{backend::DatabaseExt, constants::DEFAULT_CREATE2_DEPLOYER}; -use proptest::strategy::{Strategy, ValueTree}; +use foundry_evm_core::constants::DEFAULT_CREATE2_DEPLOYER; +use proptest::prelude::Strategy; use rand::{Rng, RngCore}; /// Contains locations of traces ignored via cheatcodes. @@ -134,10 +134,10 @@ impl Cheatcode for randomBytesCall { } impl Cheatcode for pauseTracingCall { - fn apply_full( + fn apply_full( &self, - ccx: &mut crate::CheatsCtxt, - executor: &mut E, + ccx: &mut crate::CheatsCtxt, + executor: &mut dyn CheatcodesExecutor, ) -> Result { let Some(tracer) = executor.tracing_inspector().and_then(|t| t.as_ref()) else { // No tracer -> nothing to pause @@ -157,10 +157,10 @@ impl Cheatcode for pauseTracingCall { } impl Cheatcode for resumeTracingCall { - fn apply_full( + fn apply_full( &self, - ccx: &mut crate::CheatsCtxt, - executor: &mut E, + ccx: &mut crate::CheatsCtxt, + executor: &mut dyn CheatcodesExecutor, ) -> Result { let Some(tracer) = executor.tracing_inspector().and_then(|t| t.as_ref()) else { // No tracer -> nothing to unpause @@ -180,7 +180,7 @@ impl Cheatcode for resumeTracingCall { } impl Cheatcode for setArbitraryStorageCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { target } = self; ccx.state.arbitrary_storage().mark_arbitrary(target); @@ -189,7 +189,7 @@ impl Cheatcode for setArbitraryStorageCall { } impl Cheatcode for copyStorageCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { from, to } = self; ensure!( diff --git a/crates/evm/core/src/backend/cow.rs b/crates/evm/core/src/backend/cow.rs index 8156d75fb..1589c8ee8 100644 --- a/crates/evm/core/src/backend/cow.rs +++ b/crates/evm/core/src/backend/cow.rs @@ -62,20 +62,16 @@ impl<'a> CowBackend<'a> { /// Note: in case there are any cheatcodes executed that modify the environment, this will /// update the given `env` with the new values. #[instrument(name = "inspect", level = "debug", skip_all)] - pub fn inspect<'b, I: InspectorExt<&'b mut dyn DatabaseExt>>( - &'b mut self, + pub fn inspect( + &mut self, env: &mut EnvWithHandlerCfg, - inspector: I, + inspector: &mut dyn InspectorExt, ) -> eyre::Result { // this is a new call to inspect with a new env, so even if we've cloned the backend // already, we reset the initialized state self.is_initialized = false; self.spec_id = env.handler_cfg.spec_id; - let mut evm = crate::utils::new_evm_with_inspector( - self as &mut dyn DatabaseExt, - env.clone(), - inspector, - ); + let mut evm = crate::utils::new_evm_with_inspector(self, env.clone(), inspector); let res = evm.transact().wrap_err("backend: failed while inspecting")?; @@ -190,7 +186,7 @@ impl<'a> DatabaseExt for CowBackend<'a> { transaction: B256, env: &mut Env, journaled_state: &mut JournaledState, - inspector: &mut dyn InspectorExt, + inspector: &mut dyn InspectorExt, ) -> eyre::Result<()> { self.backend_mut(env).transact(id, transaction, env, journaled_state, inspector) } @@ -200,7 +196,7 @@ impl<'a> DatabaseExt for CowBackend<'a> { transaction: TransactionRequest, env: &Env, journaled_state: &mut JournaledState, - inspector: &mut dyn InspectorExt, + inspector: &mut dyn InspectorExt, ) -> eyre::Result<()> { self.backend_mut(env).transact_from_tx(transaction, env, journaled_state, inspector) } diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 07ee70130..9d26ce287 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -200,7 +200,7 @@ pub trait DatabaseExt: Database + DatabaseCommit { transaction: B256, env: &mut Env, journaled_state: &mut JournaledState, - inspector: &mut dyn InspectorExt, + inspector: &mut dyn InspectorExt, ) -> eyre::Result<()>; /// Executes a given TransactionRequest, commits the new state to the DB @@ -209,7 +209,7 @@ pub trait DatabaseExt: Database + DatabaseCommit { transaction: TransactionRequest, env: &Env, journaled_state: &mut JournaledState, - inspector: &mut dyn InspectorExt, + inspector: &mut dyn InspectorExt, ) -> eyre::Result<()>; /// Returns the `ForkId` that's currently used in the database, if fork mode is on @@ -751,17 +751,13 @@ impl Backend { /// Note: in case there are any cheatcodes executed that modify the environment, this will /// update the given `env` with the new values. #[instrument(name = "inspect", level = "debug", skip_all)] - pub fn inspect<'a, I: InspectorExt<&'a mut dyn DatabaseExt>>( - &'a mut self, + pub fn inspect( + &mut self, env: &mut EnvWithHandlerCfg, - inspector: I, + inspector: &mut dyn InspectorExt, ) -> eyre::Result { self.initialize(env); - let mut evm = crate::utils::new_evm_with_inspector( - self as &mut dyn DatabaseExt, - env.clone(), - inspector, - ); + let mut evm = crate::utils::new_evm_with_inspector(self, env.clone(), inspector); let res = evm.transact().wrap_err("backend: failed while inspecting")?; @@ -1229,7 +1225,7 @@ impl DatabaseExt for Backend { transaction: B256, env: &mut Env, journaled_state: &mut JournaledState, - inspector: &mut dyn InspectorExt, + inspector: &mut dyn InspectorExt, ) -> eyre::Result<()> { trace!(?maybe_id, ?transaction, "execute transaction"); let persistent_accounts = self.inner.persistent_accounts.clone(); @@ -1270,7 +1266,7 @@ impl DatabaseExt for Backend { tx: TransactionRequest, env: &Env, journaled_state: &mut JournaledState, - inspector: &mut dyn InspectorExt, + inspector: &mut dyn InspectorExt, ) -> eyre::Result<()> { trace!(?tx, "execute signed transaction"); @@ -1294,9 +1290,9 @@ impl DatabaseExt for Backend { self.commit(journaled_state.state.clone()); let res = { - let db = self.clone(); + let mut db = self.clone(); let env = self.env_with_handler_cfg(env); - let mut evm = new_evm_with_inspector(db, env, inspector); + let mut evm = new_evm_with_inspector(&mut db, env, inspector); evm.context.evm.journaled_state.depth = journaled_state.depth + 1; evm.transact()? }; @@ -1921,7 +1917,7 @@ fn commit_transaction( fork: &mut Fork, fork_id: &ForkId, persistent_accounts: &HashSet
, - inspector: &mut dyn InspectorExt, + inspector: &mut dyn InspectorExt, ) -> eyre::Result<()> { configure_tx_env(&mut env.env, tx); @@ -1930,9 +1926,9 @@ fn commit_transaction( let fork = fork.clone(); let journaled_state = journaled_state.clone(); let depth = journaled_state.depth; - let db = Backend::new_with_fork(fork_id, fork, journaled_state); + let mut db = Backend::new_with_fork(fork_id, fork, journaled_state); - let mut evm = crate::utils::new_evm_with_inspector(db, env, inspector); + let mut evm = crate::utils::new_evm_with_inspector(&mut db as _, env, inspector); // Adjust inner EVM depth to ensure that inspectors receive accurate data. evm.context.evm.inner.journaled_state.depth = depth + 1; evm.transact().wrap_err("backend: failed committing transaction")? diff --git a/crates/evm/core/src/lib.rs b/crates/evm/core/src/lib.rs index b30a36544..b6da4b49a 100644 --- a/crates/evm/core/src/lib.rs +++ b/crates/evm/core/src/lib.rs @@ -6,7 +6,8 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] use auto_impl::auto_impl; -use revm::{inspectors::NoOpInspector, interpreter::CreateInputs, Database, EvmContext, Inspector}; +use backend::DatabaseExt; +use revm::{inspectors::NoOpInspector, interpreter::CreateInputs, EvmContext, Inspector}; use revm_inspectors::access_list::AccessListInspector; #[macro_use] @@ -32,14 +33,14 @@ pub mod utils; /// An extension trait that allows us to add additional hooks to Inspector for later use in /// handlers. #[auto_impl(&mut, Box)] -pub trait InspectorExt: Inspector { +pub trait InspectorExt: for<'a> Inspector<&'a mut dyn DatabaseExt> { /// Determines whether the `DEFAULT_CREATE2_DEPLOYER` should be used for a CREATE2 frame. /// /// If this function returns true, we'll replace CREATE2 frame with a CALL frame to CREATE2 /// factory. fn should_use_create2_factory( &mut self, - _context: &mut EvmContext, + _context: &mut EvmContext<&mut dyn DatabaseExt>, _inputs: &mut CreateInputs, ) -> bool { false @@ -54,5 +55,6 @@ pub trait InspectorExt: Inspector { } } -impl InspectorExt for NoOpInspector {} -impl InspectorExt for AccessListInspector {} +impl InspectorExt for NoOpInspector {} + +impl InspectorExt for AccessListInspector {} diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index 19782ee9d..d45c4b849 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -1,5 +1,8 @@ pub use crate::ic::*; -use crate::{constants::DEFAULT_CREATE2_DEPLOYER, precompiles::ALPHANET_P256, InspectorExt}; +use crate::{ + backend::DatabaseExt, constants::DEFAULT_CREATE2_DEPLOYER, precompiles::ALPHANET_P256, + InspectorExt, +}; use alloy_json_abi::{Function, JsonAbi}; use alloy_primitives::{Address, Selector, TxKind, U256}; use alloy_provider::{ @@ -8,8 +11,8 @@ use alloy_provider::{ }; use alloy_rpc_types::Transaction; use foundry_config::NamedChain; +use foundry_fork_db::DatabaseError; use revm::{ - db::WrapDatabaseRef, handler::register::EvmHandler, interpreter::{ return_ok, CallInputs, CallOutcome, CallScheme, CallValue, CreateInputs, CreateOutcome, @@ -123,15 +126,15 @@ fn get_create2_factory_call_inputs(salt: U256, inputs: CreateInputs) -> CallInpu /// hook by inserting decoded address directly into interpreter. /// /// Should be installed after [revm::inspector_handle_register] and before any other registers. -pub fn create2_handler_register>( - handler: &mut EvmHandler<'_, I, DB>, +pub fn create2_handler_register( + handler: &mut EvmHandler<'_, I, &mut dyn DatabaseExt>, ) { let create2_overrides = Rc::>>::new(RefCell::new(Vec::new())); let create2_overrides_inner = create2_overrides.clone(); let old_handle = handler.execution.create.clone(); handler.execution.create = - Arc::new(move |ctx, mut inputs| -> Result> { + Arc::new(move |ctx, mut inputs| -> Result> { let CreateScheme::Create2 { salt } = inputs.scheme else { return old_handle(ctx, inputs); }; @@ -219,9 +222,7 @@ 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>, -) { +pub fn alphanet_handler_register(handler: &mut EvmHandler<'_, EXT, DB>) { let prev = handler.pre_execution.load_precompiles.clone(); handler.pre_execution.load_precompiles = Arc::new(move || { let mut loaded_precompiles = prev(); @@ -233,15 +234,11 @@ pub fn alphanet_handler_register>( } /// Creates a new EVM with the given inspector. -pub fn new_evm_with_inspector<'a, DB, I>( - db: DB, +pub fn new_evm_with_inspector<'evm, 'i, 'db>( + db: &'db mut dyn DatabaseExt, env: revm::primitives::EnvWithHandlerCfg, - inspector: I, -) -> revm::Evm<'a, I, DB> -where - DB: revm::Database, - I: InspectorExt, -{ + inspector: &'i mut dyn InspectorExt, +) -> revm::Evm<'evm, &'i mut dyn InspectorExt, &'db mut dyn DatabaseExt> { let revm::primitives::EnvWithHandlerCfg { env, handler_cfg } = env; // NOTE: We could use `revm::Evm::builder()` here, but on the current patch it has some @@ -269,27 +266,10 @@ where revm::Evm::new(context, handler) } -/// Creates a new EVM with the given inspector and wraps the database in a `WrapDatabaseRef`. -pub fn new_evm_with_inspector_ref<'a, DB, I>( - db: DB, - env: revm::primitives::EnvWithHandlerCfg, - inspector: I, -) -> revm::Evm<'a, I, WrapDatabaseRef> -where - DB: revm::DatabaseRef, - I: InspectorExt>, -{ - new_evm_with_inspector(WrapDatabaseRef(db), env, inspector) -} - -pub fn new_evm_with_existing_context<'a, DB, I>( - inner: revm::InnerEvmContext, - inspector: I, -) -> revm::Evm<'a, I, DB> -where - DB: revm::Database, - I: InspectorExt, -{ +pub fn new_evm_with_existing_context<'a>( + inner: revm::InnerEvmContext<&'a mut dyn DatabaseExt>, + inspector: &'a mut dyn InspectorExt, +) -> revm::Evm<'a, &'a mut dyn InspectorExt, &'a mut dyn DatabaseExt> { let handler_cfg = HandlerCfg::new(inner.spec_id()); let mut handler = revm::Handler::new(handler_cfg); @@ -303,24 +283,3 @@ where revm::Context::new(revm::EvmContext { inner, precompiles: Default::default() }, inspector); revm::Evm::new(context, handler) } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn build_evm() { - let mut db = revm::db::EmptyDB::default(); - - let env = Box::::default(); - let spec = SpecId::LATEST; - let handler_cfg = revm::primitives::HandlerCfg::new(spec); - let cfg = revm::primitives::EnvWithHandlerCfg::new(env, handler_cfg); - - let mut inspector = revm::inspectors::NoOpInspector; - - let mut evm = new_evm_with_inspector(&mut db, cfg, &mut inspector); - let result = evm.transact().unwrap(); - assert!(result.result.is_success()); - } -} diff --git a/crates/evm/evm/src/inspectors/logs.rs b/crates/evm/evm/src/inspectors/logs.rs index 03b677fcd..877101a80 100644 --- a/crates/evm/evm/src/inspectors/logs.rs +++ b/crates/evm/evm/src/inspectors/logs.rs @@ -60,7 +60,7 @@ impl Inspector for LogCollector { gas: Gas::new(inputs.gas_limit), }, memory_offset: inputs.return_memory_offset.clone(), - }) + }); } } @@ -68,7 +68,7 @@ impl Inspector for LogCollector { } } -impl InspectorExt for LogCollector { +impl InspectorExt for LogCollector { fn console_log(&mut self, input: String) { self.logs.push(Log::new_unchecked( HARDHAT_CONSOLE_ADDRESS, diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index fc2705641..c8df2c693 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -304,11 +304,8 @@ pub struct InspectorStackRefMut<'a> { } impl CheatcodesExecutor for InspectorStackInner { - fn get_inspector<'a, DB: DatabaseExt>( - &'a mut self, - cheats: &'a mut Cheatcodes, - ) -> impl InspectorExt + 'a { - InspectorStackRefMut { cheatcodes: Some(cheats), inner: self } + fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box { + Box::new(InspectorStackRefMut { cheatcodes: Some(cheats), inner: self }) } fn tracing_inspector(&mut self) -> Option<&mut Option> { @@ -479,15 +476,15 @@ impl<'a> InspectorStackRefMut<'a> { /// Should be called on the top-level call of inner context (depth == 0 && /// self.in_inner_context) Decreases sender nonce for CALLs to keep backwards compatibility /// Updates tx.origin to the value before entering inner context - fn adjust_evm_data_for_inner_context(&mut self, ecx: &mut EvmContext) { + fn adjust_evm_data_for_inner_context(&mut self, ecx: &mut EvmContext<&mut dyn DatabaseExt>) { let inner_context_data = self.inner_context_data.as_ref().expect("should be called in inner context"); ecx.env.tx.caller = inner_context_data.original_origin; } - fn do_call_end( + fn do_call_end( &mut self, - ecx: &mut EvmContext, + ecx: &mut EvmContext<&mut dyn DatabaseExt>, inputs: &CallInputs, outcome: CallOutcome, ) -> CallOutcome { @@ -512,9 +509,9 @@ impl<'a> InspectorStackRefMut<'a> { outcome } - fn transact_inner( + fn transact_inner( &mut self, - ecx: &mut EvmContext, + ecx: &mut EvmContext<&mut dyn DatabaseExt>, transact_to: TransactTo, caller: Address, input: Bytes, @@ -547,11 +544,7 @@ impl<'a> InspectorStackRefMut<'a> { let env = EnvWithHandlerCfg::new_with_spec_id(ecx.env.clone(), ecx.spec_id()); let res = { - let mut evm = crate::utils::new_evm_with_inspector( - &mut ecx.db as &mut dyn DatabaseExt, - env, - &mut *self, - ); + let mut evm = crate::utils::new_evm_with_inspector(&mut ecx.db, env, self); let res = evm.transact(); // need to reset the env in case it was modified via cheatcodes during execution @@ -630,8 +623,12 @@ impl<'a> InspectorStackRefMut<'a> { } } -impl<'a, DB: DatabaseExt> Inspector for InspectorStackRefMut<'a> { - fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { +impl<'a> Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'a> { + fn initialize_interp( + &mut self, + interpreter: &mut Interpreter, + ecx: &mut EvmContext<&mut dyn DatabaseExt>, + ) { call_inspectors_adjust_depth!( [&mut self.coverage, &mut self.tracer, &mut self.cheatcodes, &mut self.printer], |inspector| inspector.initialize_interp(interpreter, ecx), @@ -640,7 +637,7 @@ impl<'a, DB: DatabaseExt> Inspector for InspectorStackRefMut<'a> { ); } - fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { + fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut dyn DatabaseExt>) { call_inspectors_adjust_depth!( [ &mut self.fuzzer, @@ -655,7 +652,11 @@ impl<'a, DB: DatabaseExt> Inspector for InspectorStackRefMut<'a> { ); } - fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { + fn step_end( + &mut self, + interpreter: &mut Interpreter, + ecx: &mut EvmContext<&mut dyn DatabaseExt>, + ) { call_inspectors_adjust_depth!( [&mut self.tracer, &mut self.cheatcodes, &mut self.chisel_state, &mut self.printer], |inspector| inspector.step_end(interpreter, ecx), @@ -664,7 +665,12 @@ impl<'a, DB: DatabaseExt> Inspector for InspectorStackRefMut<'a> { ); } - fn log(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext, log: &Log) { + fn log( + &mut self, + interpreter: &mut Interpreter, + ecx: &mut EvmContext<&mut dyn DatabaseExt>, + log: &Log, + ) { call_inspectors_adjust_depth!( [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer], |inspector| inspector.log(interpreter, ecx, log), @@ -673,7 +679,11 @@ impl<'a, DB: DatabaseExt> Inspector for InspectorStackRefMut<'a> { ); } - fn call(&mut self, ecx: &mut EvmContext, call: &mut CallInputs) -> Option { + fn call( + &mut self, + ecx: &mut EvmContext<&mut dyn DatabaseExt>, + call: &mut CallInputs, + ) -> Option { if self.in_inner_context && ecx.journaled_state.depth == 0 { self.adjust_evm_data_for_inner_context(ecx); return None; @@ -739,7 +749,7 @@ impl<'a, DB: DatabaseExt> Inspector for InspectorStackRefMut<'a> { fn call_end( &mut self, - ecx: &mut EvmContext, + ecx: &mut EvmContext<&mut dyn DatabaseExt>, inputs: &CallInputs, outcome: CallOutcome, ) -> CallOutcome { @@ -764,7 +774,7 @@ impl<'a, DB: DatabaseExt> Inspector for InspectorStackRefMut<'a> { fn create( &mut self, - ecx: &mut EvmContext, + ecx: &mut EvmContext<&mut dyn DatabaseExt>, create: &mut CreateInputs, ) -> Option { if self.in_inner_context && ecx.journaled_state.depth == 0 { @@ -801,7 +811,7 @@ impl<'a, DB: DatabaseExt> Inspector for InspectorStackRefMut<'a> { fn create_end( &mut self, - ecx: &mut EvmContext, + ecx: &mut EvmContext<&mut dyn DatabaseExt>, call: &CreateInputs, outcome: CreateOutcome, ) -> CreateOutcome { @@ -835,7 +845,7 @@ impl<'a, DB: DatabaseExt> Inspector for InspectorStackRefMut<'a> { fn eofcreate( &mut self, - ecx: &mut EvmContext, + ecx: &mut EvmContext<&mut dyn DatabaseExt>, create: &mut EOFCreateInputs, ) -> Option { if self.in_inner_context && ecx.journaled_state.depth == 0 { @@ -877,7 +887,7 @@ impl<'a, DB: DatabaseExt> Inspector for InspectorStackRefMut<'a> { fn eofcreate_end( &mut self, - ecx: &mut EvmContext, + ecx: &mut EvmContext<&mut dyn DatabaseExt>, call: &EOFCreateInputs, outcome: CreateOutcome, ) -> CreateOutcome { @@ -911,15 +921,15 @@ impl<'a, DB: DatabaseExt> Inspector for InspectorStackRefMut<'a> { fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) { call_inspectors!([&mut self.tracer, &mut self.printer], |inspector| { - Inspector::::selfdestruct(inspector, contract, target, value) + Inspector::<&mut dyn DatabaseExt>::selfdestruct(inspector, contract, target, value) }); } } -impl<'a, DB: DatabaseExt> InspectorExt for InspectorStackRefMut<'a> { +impl InspectorExt for InspectorStackRefMut<'_> { fn should_use_create2_factory( &mut self, - ecx: &mut EvmContext, + ecx: &mut EvmContext<&mut dyn DatabaseExt>, inputs: &mut CreateInputs, ) -> bool { call_inspectors_adjust_depth!( @@ -934,7 +944,7 @@ impl<'a, DB: DatabaseExt> InspectorExt for InspectorStackRefMut<'a> { } fn console_log(&mut self, input: String) { - call_inspectors!([&mut self.log_collector], |inspector| InspectorExt::::console_log( + call_inspectors!([&mut self.log_collector], |inspector| InspectorExt::console_log( inspector, input )); } @@ -944,20 +954,24 @@ impl<'a, DB: DatabaseExt> InspectorExt for InspectorStackRefMut<'a> { } } -impl Inspector for InspectorStack { +impl Inspector<&mut dyn DatabaseExt> for InspectorStack { #[inline] - fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { + fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut dyn DatabaseExt>) { self.as_mut().step(interpreter, ecx) } #[inline] - fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { + fn step_end( + &mut self, + interpreter: &mut Interpreter, + ecx: &mut EvmContext<&mut dyn DatabaseExt>, + ) { self.as_mut().step_end(interpreter, ecx) } fn call( &mut self, - context: &mut EvmContext, + context: &mut EvmContext<&mut dyn DatabaseExt>, inputs: &mut CallInputs, ) -> Option { self.as_mut().call(context, inputs) @@ -965,7 +979,7 @@ impl Inspector for InspectorStack { fn call_end( &mut self, - context: &mut EvmContext, + context: &mut EvmContext<&mut dyn DatabaseExt>, inputs: &CallInputs, outcome: CallOutcome, ) -> CallOutcome { @@ -974,7 +988,7 @@ impl Inspector for InspectorStack { fn create( &mut self, - context: &mut EvmContext, + context: &mut EvmContext<&mut dyn DatabaseExt>, create: &mut CreateInputs, ) -> Option { self.as_mut().create(context, create) @@ -982,7 +996,7 @@ impl Inspector for InspectorStack { fn create_end( &mut self, - context: &mut EvmContext, + context: &mut EvmContext<&mut dyn DatabaseExt>, call: &CreateInputs, outcome: CreateOutcome, ) -> CreateOutcome { @@ -991,7 +1005,7 @@ impl Inspector for InspectorStack { fn eofcreate( &mut self, - context: &mut EvmContext, + context: &mut EvmContext<&mut dyn DatabaseExt>, create: &mut EOFCreateInputs, ) -> Option { self.as_mut().eofcreate(context, create) @@ -999,30 +1013,39 @@ impl Inspector for InspectorStack { fn eofcreate_end( &mut self, - context: &mut EvmContext, + context: &mut EvmContext<&mut dyn DatabaseExt>, call: &EOFCreateInputs, outcome: CreateOutcome, ) -> CreateOutcome { self.as_mut().eofcreate_end(context, call, outcome) } - fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { + fn initialize_interp( + &mut self, + interpreter: &mut Interpreter, + ecx: &mut EvmContext<&mut dyn DatabaseExt>, + ) { self.as_mut().initialize_interp(interpreter, ecx) } - fn log(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext, log: &Log) { + fn log( + &mut self, + interpreter: &mut Interpreter, + ecx: &mut EvmContext<&mut dyn DatabaseExt>, + log: &Log, + ) { self.as_mut().log(interpreter, ecx, log) } fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) { - Inspector::::selfdestruct(&mut self.as_mut(), contract, target, value) + Inspector::<&mut dyn DatabaseExt>::selfdestruct(&mut self.as_mut(), contract, target, value) } } -impl InspectorExt for InspectorStack { +impl InspectorExt for InspectorStack { fn should_use_create2_factory( &mut self, - ecx: &mut EvmContext, + ecx: &mut EvmContext<&mut dyn DatabaseExt>, inputs: &mut CreateInputs, ) -> bool { self.as_mut().should_use_create2_factory(ecx, inputs) From ecf37f2f22d8e0700ead0ebae3bd3a27761c1236 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 3 Oct 2024 11:51:09 +0400 Subject: [PATCH 050/100] feat: `--eof` flag and config key (#9002) * feat: --eof flag and config key * not windows --------- Co-authored-by: grandizzy --- Cargo.lock | 21 +++++----- Cargo.toml | 2 +- crates/cli/src/opts/build/core.rs | 13 +++++++ crates/config/Cargo.toml | 1 + crates/config/src/lib.rs | 61 ++++++++++++++++++++++++++++++ crates/forge/tests/cli/alphanet.rs | 19 ++++++++++ crates/forge/tests/cli/config.rs | 1 + crates/forge/tests/cli/main.rs | 1 + 8 files changed, 108 insertions(+), 11 deletions(-) create mode 100644 crates/forge/tests/cli/alphanet.rs diff --git a/Cargo.lock b/Cargo.lock index 6c48e16b2..70ea8bef4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3789,9 +3789,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "588bc1dd21020966a255a578433016d4637c810ed76127997a469bc4a3311e7f" +checksum = "7c6cc925fc9fdd73f1038c528fef17ddbdd7512311809ace7d1860fe3666dbb5" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3827,9 +3827,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b12c7a7ab554fde7521428b040205569c187995b817481720e99adf524bf7f8" +checksum = "0d5c999c80c6d702c51522f5b4a805bec5fcae978637f0c337fa5c7a4b43d863" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3837,9 +3837,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4654933ab983928d8e33e7fdf425bb60553e8a4ac415a6014f1f2e56a5b3741d" +checksum = "3747cfeab1fc8299d70ceae0a28b7e2e005324e8eba78ac7d06729d67be5a1ec" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3861,9 +3861,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2271a6689d27f42ffe1259f587196f56251508c6c9e362c585ccf234f5919f96" +checksum = "dbd5c142355bd4822b8a7ec37268cfafe37b2e36835fa8d067b2b9d5a22c7529" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3876,9 +3876,9 @@ dependencies = [ [[package]] name = "foundry-compilers-core" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb71494112126e7ecb92748913403c35ef949f18734e3ff4566a782ce2c8b986" +checksum = "e1291c05a4c8c3b4558eb1b50f53ee1f1b599ff2490d62cdc519ad5ae4b088d6" dependencies = [ "alloy-primitives", "cfg-if", @@ -3930,6 +3930,7 @@ dependencies = [ "toml_edit", "tracing", "walkdir", + "yansi", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index bdc2c8772..e6ccd75bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -167,7 +167,7 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.7.3", default-features = false } -foundry-compilers = { version = "0.11.3", default-features = false } +foundry-compilers = { version = "0.11.4", default-features = false } foundry-fork-db = "0.4.0" solang-parser = "=0.3.3" diff --git a/crates/cli/src/opts/build/core.rs b/crates/cli/src/opts/build/core.rs index ebb4da9f1..b0d0ccbbd 100644 --- a/crates/cli/src/opts/build/core.rs +++ b/crates/cli/src/opts/build/core.rs @@ -126,6 +126,15 @@ pub struct CoreBuildArgs { #[serde(skip_serializing_if = "Option::is_none")] pub build_info_path: Option, + /// Use EOF-enabled solc binary. Enables via-ir and sets EVM version to Prague. Requires Docker + /// to be installed. + /// + /// Note that this is a temporary solution until the EOF support is merged into the main solc + /// release. + #[arg(long)] + #[serde(skip)] + pub eof: bool, + /// Skip building files whose names contain the given filter. /// /// `test` and `script` are aliases for `.t.sol` and `.s.sol`. @@ -277,6 +286,10 @@ impl Provider for CoreBuildArgs { dict.insert("revert_strings".to_string(), revert.to_string().into()); } + if self.eof { + dict.insert("eof".to_string(), true.into()); + } + Ok(Map::from([(Config::selected_profile(), dict)])) } } diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index 357830166..ec1894fb2 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -43,6 +43,7 @@ toml = { version = "0.8", features = ["preserve_order"] } toml_edit = "0.22.4" tracing.workspace = true walkdir.workspace = true +yansi.workspace = true [target.'cfg(target_os = "windows")'.dependencies] path-slash = "0.2.1" diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 017341c3d..87564fd44 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -46,6 +46,8 @@ use std::{ fs, path::{Path, PathBuf}, str::FromStr, + sync::mpsc::{self, RecvTimeoutError}, + time::Duration, }; mod macros; @@ -466,6 +468,9 @@ pub struct Config { /// Timeout for transactions in seconds. pub transaction_timeout: u64, + /// Use EOF-enabled solc for compilation. + pub eof: bool, + /// Warnings gathered when loading the Config. See [`WarningsProvider`] for more information #[serde(rename = "__warnings", default, skip_serializing)] pub warnings: Vec, @@ -530,6 +535,9 @@ impl Config { /// Default salt for create2 library deployments pub const DEFAULT_CREATE2_LIBRARY_SALT: FixedBytes<32> = FixedBytes::<32>::ZERO; + /// Docker image with eof-enabled solc binary + pub const EOF_SOLC_IMAGE: &'static str = "ghcr.io/paradigmxyz/forge-eof@sha256:46f868ce5264e1190881a3a335d41d7f42d6f26ed20b0c823609c715e38d603f"; + /// Returns the current `Config` /// /// See [`figment`](Self::figment) for more details. @@ -786,6 +794,8 @@ impl Config { config.libs.sort_unstable(); config.libs.dedup(); + config.sanitize_eof_settings(); + config } @@ -803,6 +813,22 @@ impl Config { } } + /// Adjusts settings if EOF compilation is enabled. + /// + /// This includes enabling via_ir, eof_version and ensuring that evm_version is not lower than + /// Prague. + pub fn sanitize_eof_settings(&mut self) { + if self.eof { + self.via_ir = true; + if self.eof_version.is_none() { + self.eof_version = Some(EofVersion::V1); + } + if self.evm_version < EvmVersion::Prague { + self.evm_version = EvmVersion::Prague; + } + } + } + /// Returns the directory in which dependencies should be installed /// /// Returns the first dir from `libs` that is not `node_modules` or `lib` if `libs` is empty @@ -904,6 +930,40 @@ impl Config { /// /// If `solc` is [`SolcReq::Local`] then this will ensure that the path exists. fn ensure_solc(&self) -> Result, SolcError> { + if self.eof { + let (tx, rx) = mpsc::channel(); + let root = self.root.0.clone(); + std::thread::spawn(move || { + tx.send( + Solc::new_with_args( + "docker", + [ + "run", + "--rm", + "-i", + "-v", + &format!("{}:/app/root", root.display()), + Self::EOF_SOLC_IMAGE, + ], + ) + .map(Some), + ) + }); + // If it takes more than 1 second, this likely means we are pulling the image. + return match rx.recv_timeout(Duration::from_secs(1)) { + Ok(res) => res, + Err(RecvTimeoutError::Timeout) => { + eprintln!( + "{}", + yansi::Paint::yellow( + "Pulling Docker image for eof-solc, this might take some time..." + ) + ); + rx.recv().expect("sender dropped") + } + Err(RecvTimeoutError::Disconnected) => panic!("sender dropped"), + } + } if let Some(ref solc) = self.solc { let solc = match solc { SolcReq::Version(version) => { @@ -2191,6 +2251,7 @@ impl Default for Config { eof_version: None, alphanet: false, transaction_timeout: 120, + eof: false, _non_exhaustive: (), } } diff --git a/crates/forge/tests/cli/alphanet.rs b/crates/forge/tests/cli/alphanet.rs new file mode 100644 index 000000000..cec450aca --- /dev/null +++ b/crates/forge/tests/cli/alphanet.rs @@ -0,0 +1,19 @@ +// Ensure we can run basic counter tests with EOF support. +#[cfg(not(windows))] +forgetest_init!(test_eof_flag, |prj, cmd| { + cmd.forge_fuse().args(["test", "--eof"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful with warnings: +Warning (3805): This is a pre-release compiler version, please do not use it in production. + + +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) + +"#]]); +}); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 5c0d62a32..c061af78f 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -152,6 +152,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { eof_version: None, alphanet: false, transaction_timeout: 120, + eof: false, _non_exhaustive: (), }; prj.write_config(input.clone()); diff --git a/crates/forge/tests/cli/main.rs b/crates/forge/tests/cli/main.rs index e8c6f1cc0..53c560457 100644 --- a/crates/forge/tests/cli/main.rs +++ b/crates/forge/tests/cli/main.rs @@ -4,6 +4,7 @@ extern crate foundry_test_utils; pub mod constants; pub mod utils; +mod alphanet; mod bind_json; mod build; mod cache; From df2e91b5e22a9ebce2924f0f56c54508d36f1241 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 3 Oct 2024 15:23:53 +0400 Subject: [PATCH 051/100] perf: reduce dynamic dispatch for inspectors (#9011) --- crates/evm/core/src/backend/cow.rs | 4 ++-- crates/evm/core/src/backend/mod.rs | 4 ++-- crates/evm/core/src/utils.rs | 6 +++--- crates/evm/evm/src/inspectors/stack.rs | 25 ++++++++++++++++++++++--- 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/crates/evm/core/src/backend/cow.rs b/crates/evm/core/src/backend/cow.rs index 1589c8ee8..d5e09efbc 100644 --- a/crates/evm/core/src/backend/cow.rs +++ b/crates/evm/core/src/backend/cow.rs @@ -62,10 +62,10 @@ impl<'a> CowBackend<'a> { /// Note: in case there are any cheatcodes executed that modify the environment, this will /// update the given `env` with the new values. #[instrument(name = "inspect", level = "debug", skip_all)] - pub fn inspect( + pub fn inspect( &mut self, env: &mut EnvWithHandlerCfg, - inspector: &mut dyn InspectorExt, + inspector: &mut I, ) -> eyre::Result { // this is a new call to inspect with a new env, so even if we've cloned the backend // already, we reset the initialized state diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 9d26ce287..b72ca682b 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -751,10 +751,10 @@ impl Backend { /// Note: in case there are any cheatcodes executed that modify the environment, this will /// update the given `env` with the new values. #[instrument(name = "inspect", level = "debug", skip_all)] - pub fn inspect( + pub fn inspect( &mut self, env: &mut EnvWithHandlerCfg, - inspector: &mut dyn InspectorExt, + inspector: &mut I, ) -> eyre::Result { self.initialize(env); let mut evm = crate::utils::new_evm_with_inspector(self, env.clone(), inspector); diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index d45c4b849..7e03bdf74 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -234,11 +234,11 @@ pub fn alphanet_handler_register(handler: &mut EvmHandl } /// Creates a new EVM with the given inspector. -pub fn new_evm_with_inspector<'evm, 'i, 'db>( +pub fn new_evm_with_inspector<'evm, 'i, 'db, I: InspectorExt + ?Sized>( db: &'db mut dyn DatabaseExt, env: revm::primitives::EnvWithHandlerCfg, - inspector: &'i mut dyn InspectorExt, -) -> revm::Evm<'evm, &'i mut dyn InspectorExt, &'db mut dyn DatabaseExt> { + inspector: &'i mut I, +) -> revm::Evm<'evm, &'i mut I, &'db mut dyn DatabaseExt> { let revm::primitives::EnvWithHandlerCfg { env, handler_cfg } = env; // NOTE: We could use `revm::Evm::builder()` here, but on the current patch it has some diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index c8df2c693..ac7c31ff9 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -543,14 +543,14 @@ impl<'a> InspectorStackRefMut<'a> { self.in_inner_context = true; let env = EnvWithHandlerCfg::new_with_spec_id(ecx.env.clone(), ecx.spec_id()); - let res = { - let mut evm = crate::utils::new_evm_with_inspector(&mut ecx.db, env, self); + let res = self.with_stack(|inspector| { + let mut evm = crate::utils::new_evm_with_inspector(&mut ecx.db, env, inspector); let res = evm.transact(); // need to reset the env in case it was modified via cheatcodes during execution ecx.env = evm.context.evm.inner.env; res - }; + }); self.in_inner_context = false; self.inner_context_data = None; @@ -621,6 +621,25 @@ impl<'a> InspectorStackRefMut<'a> { }; (InterpreterResult { result, output, gas }, address) } + + /// Moves out of references, constructs an [`InspectorStack`] and runs the given closure with + /// it. + fn with_stack(&mut self, f: impl FnOnce(&mut InspectorStack) -> O) -> O { + let mut stack = InspectorStack { + cheatcodes: self.cheatcodes.as_deref_mut().map(std::mem::take), + inner: std::mem::take(self.inner), + }; + + let out = f(&mut stack); + + if let Some(cheats) = self.cheatcodes.as_deref_mut() { + *cheats = stack.cheatcodes.take().unwrap(); + } + + *self.inner = stack.inner; + + out + } } impl<'a> Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'a> { From d4649bf5094f5c863a1795f7fbb19cc7efa52b4c Mon Sep 17 00:00:00 2001 From: Abhishek kochar Date: Thu, 3 Oct 2024 20:29:14 +0800 Subject: [PATCH 052/100] feat(randomBytes): adding support to generate different bytes via RngCore (#8996) * feat(randomBytes): adding support to generate different bytes via RngCore Signed-off-by: Abhishekkochar * Added needed changes to util file Signed-off-by: Abhishekkochar * Added support to get random 4 and 8 bytes Signed-off-by: Abhishekkochar * Refractor code to suggested changes Signed-off-by: Abhishekkochar * Fixed import with B32 Signed-off-by: Abhishekkochar * updated cheatcodes.json file Signed-off-by: Abhishekkochar * docs --------- Signed-off-by: Abhishekkochar Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/cheatcodes/assets/cheatcodes.json | 50 ++++++++++++++++++++--- crates/cheatcodes/spec/src/vm.rs | 18 +++++--- crates/cheatcodes/src/utils.rs | 16 +++++++- testdata/cheats/Vm.sol | 2 + testdata/default/cheats/RandomBytes.t.sol | 22 ++++++++++ 5 files changed, 97 insertions(+), 11 deletions(-) create mode 100644 testdata/default/cheats/RandomBytes.t.sol diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 9b6c67ddd..9f0870226 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -7000,7 +7000,7 @@ { "func": { "id": "randomBool", - "description": "Returns an random `bool`.", + "description": "Returns a random `bool`.", "declaration": "function randomBool() external view returns (bool);", "visibility": "external", "mutability": "view", @@ -7020,7 +7020,7 @@ { "func": { "id": "randomBytes", - "description": "Returns an random byte array value of the given length.", + "description": "Returns a random byte array value of the given length.", "declaration": "function randomBytes(uint256 len) external view returns (bytes memory);", "visibility": "external", "mutability": "view", @@ -7037,10 +7037,50 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "randomBytes4", + "description": "Returns a random fixed-size byte array of length 4.", + "declaration": "function randomBytes4() external view returns (bytes4);", + "visibility": "external", + "mutability": "view", + "signature": "randomBytes4()", + "selector": "0x9b7cd579", + "selectorBytes": [ + 155, + 124, + 213, + 121 + ] + }, + "group": "utilities", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "randomBytes8", + "description": "Returns a random fixed-size byte array of length 8.", + "declaration": "function randomBytes8() external view returns (bytes8);", + "visibility": "external", + "mutability": "view", + "signature": "randomBytes8()", + "selector": "0x0497b0a5", + "selectorBytes": [ + 4, + 151, + 176, + 165 + ] + }, + "group": "utilities", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "randomInt_0", - "description": "Returns an random `int256` value.", + "description": "Returns a random `int256` value.", "declaration": "function randomInt() external view returns (int256);", "visibility": "external", "mutability": "view", @@ -7060,7 +7100,7 @@ { "func": { "id": "randomInt_1", - "description": "Returns an random `int256` value of given bits.", + "description": "Returns a random `int256` value of given bits.", "declaration": "function randomInt(uint256 bits) external view returns (int256);", "visibility": "external", "mutability": "view", @@ -7120,7 +7160,7 @@ { "func": { "id": "randomUint_2", - "description": "Returns an random `uint256` value of given bits.", + "description": "Returns a random `uint256` value of given bits.", "declaration": "function randomUint(uint256 bits) external view returns (uint256);", "visibility": "external", "mutability": "view", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 7f27e1aa6..4ec51130a 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -2405,7 +2405,7 @@ interface Vm { #[cheatcode(group = Utilities)] function randomUint(uint256 min, uint256 max) external returns (uint256); - /// Returns an random `uint256` value of given bits. + /// Returns a random `uint256` value of given bits. #[cheatcode(group = Utilities)] function randomUint(uint256 bits) external view returns (uint256); @@ -2413,22 +2413,30 @@ interface Vm { #[cheatcode(group = Utilities)] function randomAddress() external returns (address); - /// Returns an random `int256` value. + /// Returns a random `int256` value. #[cheatcode(group = Utilities)] function randomInt() external view returns (int256); - /// Returns an random `int256` value of given bits. + /// Returns a random `int256` value of given bits. #[cheatcode(group = Utilities)] function randomInt(uint256 bits) external view returns (int256); - /// Returns an random `bool`. + /// Returns a random `bool`. #[cheatcode(group = Utilities)] function randomBool() external view returns (bool); - /// Returns an random byte array value of the given length. + /// Returns a random byte array value of the given length. #[cheatcode(group = Utilities)] function randomBytes(uint256 len) external view returns (bytes memory); + /// Returns a random fixed-size byte array of length 4. + #[cheatcode(group = Utilities)] + function randomBytes4() external view returns (bytes4); + + /// Returns a random fixed-size byte array of length 8. + #[cheatcode(group = Utilities)] + function randomBytes8() external view returns (bytes8); + /// 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)] diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index 23cc02f7a..79299d9fd 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -2,7 +2,7 @@ use crate::{Cheatcode, Cheatcodes, CheatcodesExecutor, CheatsCtxt, Result, Vm::*}; use alloy_dyn_abi::{DynSolType, DynSolValue}; -use alloy_primitives::{map::HashMap, U256}; +use alloy_primitives::{aliases::B32, map::HashMap, B64, U256}; use alloy_sol_types::SolValue; use foundry_common::ens::namehash; use foundry_evm_core::constants::DEFAULT_CREATE2_DEPLOYER; @@ -133,6 +133,20 @@ impl Cheatcode for randomBytesCall { } } +impl Cheatcode for randomBytes4Call { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let rand_u32 = state.rng().next_u32(); + Ok(B32::from(rand_u32).abi_encode()) + } +} + +impl Cheatcode for randomBytes8Call { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let rand_u64 = state.rng().next_u64(); + Ok(B64::from(rand_u64).abi_encode()) + } +} + impl Cheatcode for pauseTracingCall { fn apply_full( &self, diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 335ce83d0..7dc38480a 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -347,6 +347,8 @@ interface Vm { function randomAddress() external returns (address); function randomBool() external view returns (bool); function randomBytes(uint256 len) external view returns (bytes memory); + function randomBytes4() external view returns (bytes4); + function randomBytes8() external view returns (bytes8); function randomInt() external view returns (int256); function randomInt(uint256 bits) external view returns (int256); function randomUint() external returns (uint256); diff --git a/testdata/default/cheats/RandomBytes.t.sol b/testdata/default/cheats/RandomBytes.t.sol new file mode 100644 index 000000000..8d298f43f --- /dev/null +++ b/testdata/default/cheats/RandomBytes.t.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract RandomBytes is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function testRandomBytes4() public { + vm.randomBytes4(); + } + + function testRandomBytes8() public { + vm.randomBytes8(); + } + + function testFillrandomBytes() public view { + uint256 len = 16; + vm.randomBytes(len); + } +} From 086fcca5e6672d2894ba80931888a05a89ea1ae9 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 3 Oct 2024 16:18:17 +0200 Subject: [PATCH 053/100] chore: add more context to sourcemap error (#9015) --- crates/evm/traces/src/debug/sources.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/evm/traces/src/debug/sources.rs b/crates/evm/traces/src/debug/sources.rs index 5087a06f3..6ad335302 100644 --- a/crates/evm/traces/src/debug/sources.rs +++ b/crates/evm/traces/src/debug/sources.rs @@ -83,12 +83,14 @@ pub struct ArtifactData { impl ArtifactData { fn new(bytecode: ContractBytecodeSome, build_id: String, file_id: u32) -> Result { - let parse = |b: &Bytecode| { + let parse = |b: &Bytecode, name: &str| { // Only parse source map if it's not empty. let source_map = if b.source_map.as_ref().map_or(true, |s| s.is_empty()) { Ok(None) } else { - b.source_map().transpose() + b.source_map().transpose().wrap_err_with(|| { + format!("failed to parse {name} source map of file {file_id} in {build_id}") + }) }; // Only parse bytecode if it's not empty. @@ -100,11 +102,11 @@ impl ArtifactData { source_map.map(|source_map| (source_map, pc_ic_map)) }; - let (source_map, pc_ic_map) = parse(&bytecode.bytecode)?; + let (source_map, pc_ic_map) = parse(&bytecode.bytecode, "creation")?; let (source_map_runtime, pc_ic_map_runtime) = bytecode .deployed_bytecode .bytecode - .map(|b| parse(&b)) + .map(|b| parse(&b, "runtime")) .unwrap_or_else(|| Ok((None, None)))?; Ok(Self { source_map, source_map_runtime, pc_ic_map, pc_ic_map_runtime, build_id, file_id }) From c89a08c5b0bee69c8b6072853f0a34babbefc495 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 3 Oct 2024 19:06:15 +0400 Subject: [PATCH 054/100] fix: only test `--eof` on linux (#9016) fix: only test --eof on linux --- crates/forge/tests/cli/alphanet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/forge/tests/cli/alphanet.rs b/crates/forge/tests/cli/alphanet.rs index cec450aca..6e41551ac 100644 --- a/crates/forge/tests/cli/alphanet.rs +++ b/crates/forge/tests/cli/alphanet.rs @@ -1,5 +1,5 @@ // Ensure we can run basic counter tests with EOF support. -#[cfg(not(windows))] +#[cfg(target_os = "linux")] forgetest_init!(test_eof_flag, |prj, cmd| { cmd.forge_fuse().args(["test", "--eof"]).assert_success().stdout_eq(str![[r#" [COMPILING_FILES] with [SOLC_VERSION] From a355af4750c4e12103e9684f99401b5b14cd23f9 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 3 Oct 2024 19:37:56 +0300 Subject: [PATCH 055/100] fix(cheatcodes): handle create2 deployer with broadcastRawTransaction (#9020) fix(cheatcodes): fix deploy create with broadcastRawTransaction --- crates/evm/core/src/backend/mod.rs | 4 +- crates/forge/tests/cli/script.rs | 69 ++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index b72ca682b..f76f60ba1 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -1283,8 +1283,8 @@ impl DatabaseExt for Backend { 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"))?; + // If no `to` field then set create kind: https://eips.ethereum.org/EIPS/eip-2470#deployment-transaction + env.tx.transact_to = tx.to.unwrap_or(TxKind::Create); env.tx.chain_id = tx.chain_id; self.commit(journaled_state.state.clone()); diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index fb2e8ae8d..61c2456fe 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -1986,3 +1986,72 @@ forgetest_async!(can_deploy_library_create2_different_sender, |prj, cmd| { .assert_nonce_increment(&[(2, 2)]) .await; }); + +// +forgetest_async!(test_broadcast_raw_create2_deployer, |prj, cmd| { + let (_api, handle) = + spawn(NodeConfig::test().with_disable_default_create2_deployer(true)).await; + + foundry_test_utils::util::initialize(prj.root()); + prj.add_script( + "Foo", + r#" +import "forge-std/Script.sol"; + +contract SimpleScript is Script { + function run() external { + // send funds to create2 factory deployer + vm.broadcast(); + payable(0x3fAB184622Dc19b6109349B94811493BF2a45362).transfer(10000000 gwei); + // deploy create2 factory + vm.broadcastRawTransaction( + hex"f8a58085174876e800830186a08080b853604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf31ba02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222" + ); + } +} + "#, + ) + .unwrap(); + + cmd.args([ + "script", + "--private-key", + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + "--rpc-url", + &handle.http_endpoint(), + "--broadcast", + "SimpleScript", + ]); + + cmd.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] + +[ESTIMATED_AMOUNT_REQUIRED] + +========================== + + +========================== + +ONCHAIN EXECUTION COMPLETE & SUCCESSFUL. + +[SAVED_TRANSACTIONS] + +[SAVED_SENSITIVE_VALUES] + + +"#]]); +}); From 67018dcf3cc4ee80471a6d8a4d519c1d946b7fbb Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Thu, 3 Oct 2024 22:51:49 +0530 Subject: [PATCH 056/100] fix(`anvil`): set `storage.best_hash` while loading state (#9021) * fix(anvil): set `storage.best_hash` while loading state * clippy nit * test: strengthen can_load_state --- crates/anvil/src/eth/backend/mem/mod.rs | 21 ++++++++++++++++----- crates/anvil/tests/it/state.rs | 15 ++++++++++++++- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index dbf22fbeb..0b002d3db 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -800,14 +800,28 @@ impl Backend { /// Apply [SerializableState] data to the backend storage. pub async fn load_state(&self, state: SerializableState) -> Result { + // load the blocks and transactions into the storage + self.blockchain.storage.write().load_blocks(state.blocks.clone()); + self.blockchain.storage.write().load_transactions(state.transactions.clone()); // reset the block env if let Some(block) = state.block.clone() { self.env.write().block = block.clone(); // Set the current best block number. // Defaults to block number for compatibility with existing state files. - self.blockchain.storage.write().best_number = - state.best_block_number.unwrap_or(block.number.to::()); + + let best_number = state.best_block_number.unwrap_or(block.number.to::()); + self.blockchain.storage.write().best_number = best_number; + + // Set the current best block hash; + let best_hash = + self.blockchain.storage.read().hash(best_number.into()).ok_or_else(|| { + BlockchainError::RpcError(RpcError::internal_error_with(format!( + "Best hash not found for best number {best_number}", + ))) + })?; + + self.blockchain.storage.write().best_hash = best_hash; } if !self.db.write().await.load_state(state.clone())? { @@ -817,9 +831,6 @@ impl Backend { .into()); } - 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); } diff --git a/crates/anvil/tests/it/state.rs b/crates/anvil/tests/it/state.rs index 8227a89f1..cb8f3b9eb 100644 --- a/crates/anvil/tests/it/state.rs +++ b/crates/anvil/tests/it/state.rs @@ -1,7 +1,7 @@ //! general eth api tests use crate::abi::Greeter; -use alloy_primitives::{Bytes, Uint}; +use alloy_primitives::{Bytes, Uint, U256}; use alloy_provider::Provider; use alloy_rpc_types::BlockId; use anvil::{spawn, NodeConfig}; @@ -13,6 +13,7 @@ async fn can_load_state() { let (api, _handle) = spawn(NodeConfig::test()).await; + api.mine_one().await; api.mine_one().await; let num = api.block_number().unwrap(); @@ -23,7 +24,19 @@ async fn can_load_state() { let (api, _handle) = spawn(NodeConfig::test().with_init_state_path(state_file)).await; let num2 = api.block_number().unwrap(); + + // Ref: https://github.com/foundry-rs/foundry/issues/9017 + // Check responses of eth_blockNumber and eth_getBlockByNumber don't deviate after loading state + let num_from_tag = api + .block_by_number(alloy_eips::BlockNumberOrTag::Latest) + .await + .unwrap() + .unwrap() + .header + .number; assert_eq!(num, num2); + + assert_eq!(num, U256::from(num_from_tag)); } #[tokio::test(flavor = "multi_thread")] From e10ab3d7010b2cbe2b76030d6638c49a3cec696d Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 3 Oct 2024 20:04:26 +0200 Subject: [PATCH 057/100] chore: reduce size of DynCheatcode vtable (#9023) --- crates/cheatcodes/src/lib.rs | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index a25728991..106b4c961 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -84,25 +84,9 @@ pub(crate) trait Cheatcode: CheatcodeDef + DynCheatcode { } } -pub(crate) trait DynCheatcode { +pub(crate) trait DynCheatcode: 'static { fn cheatcode(&self) -> &'static spec::Cheatcode<'static>; - fn name(&self) -> &'static str { - self.cheatcode().func.signature.split('(').next().unwrap() - } - - fn id(&self) -> &'static str { - self.cheatcode().func.id - } - - fn signature(&self) -> &'static str { - self.cheatcode().func.signature - } - - fn status(&self) -> &Status<'static> { - &self.cheatcode().status - } - fn as_debug(&self) -> &dyn std::fmt::Debug; fn dyn_apply(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result; @@ -125,6 +109,24 @@ impl DynCheatcode for T { } } +impl dyn DynCheatcode { + pub(crate) fn name(&self) -> &'static str { + self.cheatcode().func.signature.split('(').next().unwrap() + } + + pub(crate) fn id(&self) -> &'static str { + self.cheatcode().func.id + } + + pub(crate) fn signature(&self) -> &'static str { + self.cheatcode().func.signature + } + + pub(crate) fn status(&self) -> &Status<'static> { + &self.cheatcode().status + } +} + /// The cheatcode context, used in `Cheatcode`. pub struct CheatsCtxt<'cheats, 'evm, 'db, 'db2> { /// The cheatcodes inspector state. From ac37bdb260acd21cc95a3a63ee78d6530a1eec87 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 4 Oct 2024 08:04:04 +0200 Subject: [PATCH 058/100] chore: unify tx env filling + add missing members (#9022) --- crates/cheatcodes/src/evm.rs | 7 ++-- crates/cheatcodes/src/evm/fork.rs | 36 ++++++++-------- crates/evm/core/src/backend/cow.rs | 10 ++--- crates/evm/core/src/backend/mod.rs | 36 +++++----------- crates/evm/core/src/utils.rs | 67 +++++++++++++++++++++++++----- 5 files changed, 92 insertions(+), 64 deletions(-) diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index f9671430d..af2026a9e 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -683,13 +683,12 @@ impl Cheatcode for stopAndReturnStateDiffCall { impl Cheatcode for broadcastRawTransactionCall { fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { - let mut data = self.data.as_ref(); - let tx = TxEnvelope::decode(&mut data) + let tx = TxEnvelope::decode(&mut self.data.as_ref()) .map_err(|err| fmt_err!("failed to decode RLP-encoded transaction: {err}"))?; ccx.ecx.db.transact_from_tx( - tx.clone().into(), - &ccx.ecx.env, + &tx.clone().into(), + (*ccx.ecx.env).clone(), &mut ccx.ecx.journaled_state, &mut *executor.get_inspector(ccx.state), )?; diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs index 84fda7883..e1d66538d 100644 --- a/crates/cheatcodes/src/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -130,28 +130,14 @@ impl Cheatcode for selectForkCall { impl Cheatcode for transact_0Call { fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { let Self { txHash } = *self; - ccx.ecx.db.transact( - None, - txHash, - &mut ccx.ecx.env, - &mut ccx.ecx.journaled_state, - &mut executor.get_inspector(ccx.state), - )?; - Ok(Default::default()) + transact(ccx, executor, txHash, None) } } impl Cheatcode for transact_1Call { fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { let Self { forkId, txHash } = *self; - ccx.ecx.db.transact( - Some(forkId), - txHash, - &mut ccx.ecx.env, - &mut ccx.ecx.journaled_state, - &mut *executor.get_inspector(ccx.state), - )?; - Ok(Default::default()) + transact(ccx, executor, txHash, Some(forkId)) } } @@ -350,7 +336,6 @@ fn create_fork_request( Ok(fork) } -#[inline] fn check_broadcast(state: &Cheatcodes) -> Result<()> { if state.broadcast.is_none() { Ok(()) @@ -359,11 +344,26 @@ fn check_broadcast(state: &Cheatcodes) -> Result<()> { } } +fn transact( + ccx: &mut CheatsCtxt, + executor: &mut dyn CheatcodesExecutor, + transaction: B256, + fork_id: Option, +) -> Result { + ccx.ecx.db.transact( + fork_id, + transaction, + (*ccx.ecx.env).clone(), + &mut ccx.ecx.journaled_state, + &mut *executor.get_inspector(ccx.state), + )?; + Ok(Default::default()) +} + // Helper to add the caller of fork cheat code as persistent account (in order to make sure that the // state of caller contract is not lost when fork changes). // Applies to create, select and roll forks actions. // https://github.com/foundry-rs/foundry/issues/8004 -#[inline] fn persist_caller(ccx: &mut CheatsCtxt) { ccx.ecx.db.add_persistent_account(ccx.caller); } diff --git a/crates/evm/core/src/backend/cow.rs b/crates/evm/core/src/backend/cow.rs index d5e09efbc..d52ac6fd0 100644 --- a/crates/evm/core/src/backend/cow.rs +++ b/crates/evm/core/src/backend/cow.rs @@ -184,21 +184,21 @@ impl<'a> DatabaseExt for CowBackend<'a> { &mut self, id: Option, transaction: B256, - env: &mut Env, + env: Env, journaled_state: &mut JournaledState, inspector: &mut dyn InspectorExt, ) -> eyre::Result<()> { - self.backend_mut(env).transact(id, transaction, env, journaled_state, inspector) + self.backend_mut(&env).transact(id, transaction, env, journaled_state, inspector) } fn transact_from_tx( &mut self, - transaction: TransactionRequest, - env: &Env, + 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) + self.backend_mut(&env).transact_from_tx(transaction, env, journaled_state, inspector) } fn active_fork_id(&self) -> Option { diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index f76f60ba1..8cf59d96e 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -4,7 +4,7 @@ use crate::{ constants::{CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, TEST_CONTRACT_ADDRESS}, fork::{CreateFork, ForkId, MultiFork}, state_snapshot::StateSnapshots, - utils::{configure_tx_env, new_evm_with_inspector}, + utils::{configure_tx_env, configure_tx_req_env, new_evm_with_inspector}, InspectorExt, }; use alloy_genesis::GenesisAccount; @@ -198,7 +198,7 @@ pub trait DatabaseExt: Database + DatabaseCommit { &mut self, id: Option, transaction: B256, - env: &mut Env, + env: Env, journaled_state: &mut JournaledState, inspector: &mut dyn InspectorExt, ) -> eyre::Result<()>; @@ -206,8 +206,8 @@ pub trait DatabaseExt: Database + DatabaseCommit { /// Executes a given TransactionRequest, commits the new state to the DB fn transact_from_tx( &mut self, - transaction: TransactionRequest, - env: &Env, + transaction: &TransactionRequest, + env: Env, journaled_state: &mut JournaledState, inspector: &mut dyn InspectorExt, ) -> eyre::Result<()>; @@ -1223,7 +1223,7 @@ impl DatabaseExt for Backend { &mut self, maybe_id: Option, transaction: B256, - env: &mut Env, + mut env: Env, journaled_state: &mut JournaledState, inspector: &mut dyn InspectorExt, ) -> eyre::Result<()> { @@ -1245,7 +1245,6 @@ impl DatabaseExt for Backend { // So we modify the env to match the transaction's 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, &block); let env = self.env_with_handler_cfg(env); @@ -1263,35 +1262,20 @@ impl DatabaseExt for Backend { fn transact_from_tx( &mut self, - tx: TransactionRequest, - env: &Env, + tx: &TransactionRequest, + mut 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"))?; - 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(); - // If no `to` field then set create kind: https://eips.ethereum.org/EIPS/eip-2470#deployment-transaction - env.tx.transact_to = tx.to.unwrap_or(TxKind::Create); - env.tx.chain_id = tx.chain_id; - self.commit(journaled_state.state.clone()); let res = { - let mut db = self.clone(); + configure_tx_req_env(&mut env, tx)?; let env = self.env_with_handler_cfg(env); + + let mut db = self.clone(); let mut evm = new_evm_with_inspector(&mut db, env, inspector); evm.context.evm.journaled_state.depth = journaled_state.depth + 1; evm.transact()? diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index 7e03bdf74..bd17aae44 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -9,7 +9,7 @@ use alloy_provider::{ network::{BlockResponse, HeaderResponse}, Network, }; -use alloy_rpc_types::Transaction; +use alloy_rpc_types::{Transaction, TransactionRequest}; use foundry_config::NamedChain; use foundry_fork_db::DatabaseError; use revm::{ @@ -84,17 +84,62 @@ pub fn get_function<'a>( .ok_or_else(|| eyre::eyre!("{contract_name} does not have the selector {selector}")) } -/// Configures the env for the transaction +/// Configures the env for the given RPC transaction. pub fn configure_tx_env(env: &mut revm::primitives::Env, tx: &Transaction) { - env.tx.caller = tx.from; - env.tx.gas_limit = tx.gas; - env.tx.gas_price = U256::from(tx.gas_price.unwrap_or_default()); - env.tx.gas_priority_fee = tx.max_priority_fee_per_gas.map(U256::from); - env.tx.nonce = Some(tx.nonce); - env.tx.access_list = tx.access_list.clone().unwrap_or_default().0.into_iter().collect(); - env.tx.value = tx.value.to(); - env.tx.data = alloy_primitives::Bytes(tx.input.0.clone()); - env.tx.transact_to = tx.to.map(TxKind::Call).unwrap_or(TxKind::Create) + configure_tx_req_env(env, &tx.clone().into()).expect("cannot fail"); +} + +/// Configures the env for the given RPC transaction request. +pub fn configure_tx_req_env( + env: &mut revm::primitives::Env, + tx: &TransactionRequest, +) -> eyre::Result<()> { + let TransactionRequest { + nonce, + from, + to, + value, + gas_price, + gas, + max_fee_per_gas, + max_priority_fee_per_gas, + max_fee_per_blob_gas, + ref input, + chain_id, + ref blob_versioned_hashes, + ref access_list, + transaction_type: _, + ref authorization_list, + sidecar: _, + } = *tx; + + // If no `to` field then set create kind: https://eips.ethereum.org/EIPS/eip-2470#deployment-transaction + env.tx.transact_to = to.unwrap_or(TxKind::Create); + env.tx.caller = from.ok_or_else(|| eyre::eyre!("missing `from` field"))?; + env.tx.gas_limit = gas.ok_or_else(|| eyre::eyre!("missing `gas` field"))?; + env.tx.nonce = nonce; + env.tx.value = value.unwrap_or_default(); + env.tx.data = input.input().cloned().unwrap_or_default(); + env.tx.chain_id = chain_id; + + // Type 1, EIP-2930 + env.tx.access_list = access_list.clone().unwrap_or_default().0.into_iter().collect(); + + // Type 2, EIP-1559 + env.tx.gas_price = U256::from(gas_price.or(max_fee_per_gas).unwrap_or_default()); + env.tx.gas_priority_fee = max_priority_fee_per_gas.map(U256::from); + + // Type 3, EIP-4844 + env.tx.blob_hashes = blob_versioned_hashes.clone().unwrap_or_default(); + env.tx.max_fee_per_blob_gas = max_fee_per_blob_gas.map(U256::from); + + // Type 4, EIP-7702 + if let Some(authorization_list) = authorization_list { + env.tx.authorization_list = + Some(revm::primitives::AuthorizationList::Signed(authorization_list.clone())); + } + + Ok(()) } /// Get the gas used, accounting for refunds From a970b36d451fe84b8fba456ec99bf822376e56b8 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 4 Oct 2024 10:06:01 +0300 Subject: [PATCH 059/100] chore: fix clippy (#9028) --- crates/anvil/src/eth/backend/executor.rs | 6 ++---- crates/anvil/src/eth/util.rs | 4 ++-- crates/cheatcodes/src/test/assert.rs | 8 ++++---- crates/chisel/src/executor.rs | 2 +- crates/chisel/src/solidity_helper.rs | 2 +- crates/config/src/lib.rs | 2 +- crates/config/src/providers/remappings.rs | 4 ++-- crates/doc/src/preprocessor/infer_hyperlinks.rs | 2 +- crates/doc/src/writer/as_doc.rs | 2 +- crates/doc/src/writer/markdown.rs | 4 ++-- crates/doc/src/writer/traits.rs | 2 +- crates/evm/core/src/backend/cow.rs | 8 ++++---- crates/evm/evm/src/inspectors/stack.rs | 4 ++-- crates/fmt/src/comments.rs | 2 +- crates/fmt/src/formatter.rs | 6 +++--- crates/fmt/src/solang_ext/loc.rs | 6 +++--- crates/fmt/src/string.rs | 6 +++--- crates/forge/bin/cmd/geiger/find.rs | 2 +- crates/forge/src/coverage.rs | 2 +- crates/forge/src/runner.rs | 2 +- 20 files changed, 37 insertions(+), 39 deletions(-) diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index 3221620ef..3ceee8b04 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -112,7 +112,7 @@ pub struct TransactionExecutor<'a, Db: ?Sized, V: TransactionValidator> { pub precompile_factory: Option>, } -impl<'a, DB: Db + ?Sized, V: TransactionValidator> TransactionExecutor<'a, DB, V> { +impl TransactionExecutor<'_, DB, V> { /// Executes all transactions and puts them in a new block with the provided `timestamp` pub fn execute(mut self) -> ExecutedTransactions { let mut transactions = Vec::new(); @@ -263,9 +263,7 @@ pub enum TransactionExecutionOutcome { DatabaseError(Arc, DatabaseError), } -impl<'a, 'b, DB: Db + ?Sized, V: TransactionValidator> Iterator - for &'b mut TransactionExecutor<'a, DB, V> -{ +impl Iterator for &mut TransactionExecutor<'_, DB, V> { type Item = TransactionExecutionOutcome; fn next(&mut self) -> Option { diff --git a/crates/anvil/src/eth/util.rs b/crates/anvil/src/eth/util.rs index 6bcde67d5..ca66f2ed3 100644 --- a/crates/anvil/src/eth/util.rs +++ b/crates/anvil/src/eth/util.rs @@ -29,7 +29,7 @@ impl<'a> HexDisplay<'a> { } } -impl<'a> fmt::Display for HexDisplay<'a> { +impl fmt::Display for HexDisplay<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.0.len() < 1027 { for byte in self.0 { @@ -48,7 +48,7 @@ impl<'a> fmt::Display for HexDisplay<'a> { } } -impl<'a> fmt::Debug for HexDisplay<'a> { +impl fmt::Debug for HexDisplay<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for byte in self.0 { f.write_fmt(format_args!("{byte:02x}"))?; diff --git a/crates/cheatcodes/src/test/assert.rs b/crates/cheatcodes/src/test/assert.rs index 5161716fa..b4b6652ac 100644 --- a/crates/cheatcodes/src/test/assert.rs +++ b/crates/cheatcodes/src/test/assert.rs @@ -37,27 +37,27 @@ macro_rules! format_values { }; } -impl<'a, T: Display> ComparisonAssertionError<'a, T> { +impl ComparisonAssertionError<'_, T> { fn format_for_values(&self) -> String { format_values!(self, T::to_string) } } -impl<'a, T: Display> ComparisonAssertionError<'a, Vec> { +impl ComparisonAssertionError<'_, Vec> { fn format_for_arrays(&self) -> String { let formatter = |v: &Vec| format!("[{}]", v.iter().format(", ")); format_values!(self, formatter) } } -impl<'a> ComparisonAssertionError<'a, U256> { +impl ComparisonAssertionError<'_, U256> { fn format_with_decimals(&self, decimals: &U256) -> String { let formatter = |v: &U256| format_units_uint(v, decimals); format_values!(self, formatter) } } -impl<'a> ComparisonAssertionError<'a, I256> { +impl ComparisonAssertionError<'_, I256> { fn format_with_decimals(&self, decimals: &U256) -> String { let formatter = |v: &I256| format_units_int(v, decimals); format_values!(self, formatter) diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index 5e40862f8..16387a69b 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -1371,7 +1371,7 @@ impl<'a> InstructionIter<'a> { } } -impl<'a> Iterator for InstructionIter<'a> { +impl Iterator for InstructionIter<'_> { type Item = Instruction; fn next(&mut self) -> Option { let pc = self.offset; diff --git a/crates/chisel/src/solidity_helper.rs b/crates/chisel/src/solidity_helper.rs index 4707ab37b..696cca1a1 100644 --- a/crates/chisel/src/solidity_helper.rs +++ b/crates/chisel/src/solidity_helper.rs @@ -278,7 +278,7 @@ pub trait TokenStyle { } /// [TokenStyle] implementation for [Token] -impl<'a> TokenStyle for Token<'a> { +impl TokenStyle for Token<'_> { fn style(&self) -> Style { use Token::*; match self { diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 87564fd44..648d40e66 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -2456,7 +2456,7 @@ impl Provider for BackwardsCompatTomlProvider

{ /// A provider that sets the `src` and `output` path depending on their existence. struct DappHardhatDirProvider<'a>(&'a Path); -impl<'a> Provider for DappHardhatDirProvider<'a> { +impl Provider for DappHardhatDirProvider<'_> { fn metadata(&self) -> Metadata { Metadata::named("Dapp Hardhat dir compat") } diff --git a/crates/config/src/providers/remappings.rs b/crates/config/src/providers/remappings.rs index 2eaab0982..2e849bea4 100644 --- a/crates/config/src/providers/remappings.rs +++ b/crates/config/src/providers/remappings.rs @@ -87,7 +87,7 @@ pub struct RemappingsProvider<'a> { pub remappings: Result, Error>, } -impl<'a> RemappingsProvider<'a> { +impl RemappingsProvider<'_> { /// Find and parse remappings for the projects /// /// **Order** @@ -240,7 +240,7 @@ impl<'a> RemappingsProvider<'a> { } } -impl<'a> Provider for RemappingsProvider<'a> { +impl Provider for RemappingsProvider<'_> { fn metadata(&self) -> Metadata { Metadata::named("Remapping Provider") } diff --git a/crates/doc/src/preprocessor/infer_hyperlinks.rs b/crates/doc/src/preprocessor/infer_hyperlinks.rs index 2d0802789..25fafc032 100644 --- a/crates/doc/src/preprocessor/infer_hyperlinks.rs +++ b/crates/doc/src/preprocessor/infer_hyperlinks.rs @@ -200,7 +200,7 @@ impl<'a> InlineLinkTarget<'a> { } } -impl<'a> std::fmt::Display for InlineLinkTarget<'a> { +impl std::fmt::Display for InlineLinkTarget<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // NOTE: the url should be absolute for markdown and section names are lowercase write!(f, "/{}#{}", self.target_path.display(), self.section.to_lowercase()) diff --git a/crates/doc/src/writer/as_doc.rs b/crates/doc/src/writer/as_doc.rs index a14f9d344..50a847beb 100644 --- a/crates/doc/src/writer/as_doc.rs +++ b/crates/doc/src/writer/as_doc.rs @@ -31,7 +31,7 @@ impl AsDoc for Comments { } } -impl<'a> AsDoc for CommentsRef<'a> { +impl AsDoc for CommentsRef<'_> { // TODO: support other tags fn as_doc(&self) -> AsDocResult { let mut writer = BufWriter::default(); diff --git a/crates/doc/src/writer/markdown.rs b/crates/doc/src/writer/markdown.rs index 2e577ad96..7583c35e7 100644 --- a/crates/doc/src/writer/markdown.rs +++ b/crates/doc/src/writer/markdown.rs @@ -21,7 +21,7 @@ pub enum Markdown<'a> { CodeBlock(&'a str, &'a str), } -impl<'a> AsDoc for Markdown<'a> { +impl AsDoc for Markdown<'_> { fn as_doc(&self) -> AsDocResult { let doc = match self { Self::H1(val) => format!("# {val}"), @@ -37,7 +37,7 @@ impl<'a> AsDoc for Markdown<'a> { } } -impl<'a> std::fmt::Display for Markdown<'a> { +impl std::fmt::Display for Markdown<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!("{}", self.as_doc()?)) } diff --git a/crates/doc/src/writer/traits.rs b/crates/doc/src/writer/traits.rs index b59708ed9..0b79718d5 100644 --- a/crates/doc/src/writer/traits.rs +++ b/crates/doc/src/writer/traits.rs @@ -56,7 +56,7 @@ impl ParamLike for solang_parser::pt::ErrorParameter { } } -impl<'a, T> ParamLike for &'a T +impl ParamLike for &T where T: ParamLike, { diff --git a/crates/evm/core/src/backend/cow.rs b/crates/evm/core/src/backend/cow.rs index d52ac6fd0..fcc2e1596 100644 --- a/crates/evm/core/src/backend/cow.rs +++ b/crates/evm/core/src/backend/cow.rs @@ -110,7 +110,7 @@ impl<'a> CowBackend<'a> { } } -impl<'a> DatabaseExt for CowBackend<'a> { +impl DatabaseExt for CowBackend<'_> { fn snapshot_state(&mut self, journaled_state: &JournaledState, env: &Env) -> U256 { self.backend_mut(env).snapshot_state(journaled_state, env) } @@ -262,7 +262,7 @@ impl<'a> DatabaseExt for CowBackend<'a> { } } -impl<'a> DatabaseRef for CowBackend<'a> { +impl DatabaseRef for CowBackend<'_> { type Error = DatabaseError; fn basic_ref(&self, address: Address) -> Result, Self::Error> { @@ -282,7 +282,7 @@ impl<'a> DatabaseRef for CowBackend<'a> { } } -impl<'a> Database for CowBackend<'a> { +impl Database for CowBackend<'_> { type Error = DatabaseError; fn basic(&mut self, address: Address) -> Result, Self::Error> { @@ -302,7 +302,7 @@ impl<'a> Database for CowBackend<'a> { } } -impl<'a> DatabaseCommit for CowBackend<'a> { +impl DatabaseCommit for CowBackend<'_> { fn commit(&mut self, changes: Map) { self.backend.to_mut().commit(changes) } diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index ac7c31ff9..589866359 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -471,7 +471,7 @@ impl InspectorStack { } } -impl<'a> InspectorStackRefMut<'a> { +impl InspectorStackRefMut<'_> { /// Adjusts the EVM data for the inner EVM context. /// Should be called on the top-level call of inner context (depth == 0 && /// self.in_inner_context) Decreases sender nonce for CALLs to keep backwards compatibility @@ -642,7 +642,7 @@ impl<'a> InspectorStackRefMut<'a> { } } -impl<'a> Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'a> { +impl Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'_> { fn initialize_interp( &mut self, interpreter: &mut Interpreter, diff --git a/crates/fmt/src/comments.rs b/crates/fmt/src/comments.rs index 5b9d7fb27..90054f132 100644 --- a/crates/fmt/src/comments.rs +++ b/crates/fmt/src/comments.rs @@ -409,7 +409,7 @@ impl std::iter::FusedIterator for CommentStateCharIndices<'_> {} /// An Iterator over characters in a string slice which are not a apart of comments pub struct NonCommentChars<'a>(CommentStateCharIndices<'a>); -impl<'a> Iterator for NonCommentChars<'a> { +impl Iterator for NonCommentChars<'_> { type Item = char; #[inline] diff --git a/crates/fmt/src/formatter.rs b/crates/fmt/src/formatter.rs index 1dbf63eac..02b7bd7fe 100644 --- a/crates/fmt/src/formatter.rs +++ b/crates/fmt/src/formatter.rs @@ -1872,7 +1872,7 @@ impl<'a, W: Write> Formatter<'a, W> { } // Traverse the Solidity Parse Tree and write to the code formatter -impl<'a, W: Write> Visitor for Formatter<'a, W> { +impl Visitor for Formatter<'_, W> { type Error = FormatterError; #[instrument(name = "source", skip(self))] @@ -3843,14 +3843,14 @@ struct Transaction<'f, 'a, W> { comments: Comments, } -impl<'f, 'a, W> std::ops::Deref for Transaction<'f, 'a, W> { +impl<'a, W> std::ops::Deref for Transaction<'_, 'a, W> { type Target = Formatter<'a, W>; fn deref(&self) -> &Self::Target { self.fmt } } -impl<'f, 'a, W> std::ops::DerefMut for Transaction<'f, 'a, W> { +impl std::ops::DerefMut for Transaction<'_, '_, W> { fn deref_mut(&mut self) -> &mut Self::Target { self.fmt } diff --git a/crates/fmt/src/solang_ext/loc.rs b/crates/fmt/src/solang_ext/loc.rs index 2fcbaf995..54bf771c6 100644 --- a/crates/fmt/src/solang_ext/loc.rs +++ b/crates/fmt/src/solang_ext/loc.rs @@ -10,19 +10,19 @@ pub trait CodeLocationExt { fn loc(&self) -> pt::Loc; } -impl<'a, T: ?Sized + CodeLocationExt> CodeLocationExt for &'a T { +impl CodeLocationExt for &T { fn loc(&self) -> pt::Loc { (**self).loc() } } -impl<'a, T: ?Sized + CodeLocationExt> CodeLocationExt for &'a mut T { +impl CodeLocationExt for &mut T { fn loc(&self) -> pt::Loc { (**self).loc() } } -impl<'a, T: ?Sized + ToOwned + CodeLocationExt> CodeLocationExt for Cow<'a, T> { +impl CodeLocationExt for Cow<'_, T> { fn loc(&self) -> pt::Loc { (**self).loc() } diff --git a/crates/fmt/src/string.rs b/crates/fmt/src/string.rs index 1dbc2f2f6..ae570a39b 100644 --- a/crates/fmt/src/string.rs +++ b/crates/fmt/src/string.rs @@ -38,7 +38,7 @@ impl<'a> QuoteStateCharIndices<'a> { } } -impl<'a> Iterator for QuoteStateCharIndices<'a> { +impl Iterator for QuoteStateCharIndices<'_> { type Item = (QuoteState, usize, char); fn next(&mut self) -> Option { let (idx, ch) = self.iter.next()?; @@ -68,14 +68,14 @@ impl<'a> Iterator for QuoteStateCharIndices<'a> { /// An iterator over the indices of quoted string locations pub struct QuotedRanges<'a>(QuoteStateCharIndices<'a>); -impl<'a> QuotedRanges<'a> { +impl QuotedRanges<'_> { pub fn with_state(mut self, state: QuoteState) -> Self { self.0 = self.0.with_state(state); self } } -impl<'a> Iterator for QuotedRanges<'a> { +impl Iterator for QuotedRanges<'_> { type Item = (char, usize, usize); fn next(&mut self) -> Option { let (quote, start) = loop { diff --git a/crates/forge/bin/cmd/geiger/find.rs b/crates/forge/bin/cmd/geiger/find.rs index 6629390ca..e3cd65413 100644 --- a/crates/forge/bin/cmd/geiger/find.rs +++ b/crates/forge/bin/cmd/geiger/find.rs @@ -47,7 +47,7 @@ pub struct SolFileMetricsPrinter<'a, 'b> { pub root: &'b Path, } -impl<'a, 'b> fmt::Display for SolFileMetricsPrinter<'a, 'b> { +impl fmt::Display for SolFileMetricsPrinter<'_, '_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let SolFileMetricsPrinter { metrics, root } = *self; diff --git a/crates/forge/src/coverage.rs b/crates/forge/src/coverage.rs index b712c8fc2..ef9c7b0c6 100644 --- a/crates/forge/src/coverage.rs +++ b/crates/forge/src/coverage.rs @@ -86,7 +86,7 @@ impl<'a> LcovReporter<'a> { } } -impl<'a> CoverageReporter for LcovReporter<'a> { +impl CoverageReporter for LcovReporter<'_> { fn report(self, report: &CoverageReport) -> eyre::Result<()> { for (file, items) in report.items_by_source() { let summary = items.iter().fold(CoverageSummary::default(), |mut summary, item| { diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 8f9f7ebdd..2a964789b 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -76,7 +76,7 @@ pub struct ContractRunner<'a> { pub span: tracing::Span, } -impl<'a> ContractRunner<'a> { +impl ContractRunner<'_> { /// Deploys the test contract inside the runner from the sending account, and optionally runs /// the `setUp` function on the test contract. pub fn setup(&mut self, call_setup: bool) -> TestSetup { From d3ce9f08294bf3e78d0d3167f9b4a4669e262600 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Fri, 4 Oct 2024 12:48:22 +0400 Subject: [PATCH 060/100] fix(`--isolate`): track state in journal (#9018) * track in journal * wip * add test * forge fmt * rm selfdestruct test --- crates/evm/evm/src/inspectors/stack.rs | 315 +++++++++++++----------- crates/forge/tests/cli/test_cmd.rs | 30 --- crates/forge/tests/it/repros.rs | 7 + testdata/default/repros/Issue8971.t.sol | 50 ++++ 4 files changed, 232 insertions(+), 170 deletions(-) create mode 100644 testdata/default/repros/Issue8971.t.sol diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 589866359..1e3b36181 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -4,10 +4,7 @@ use super::{ }; use alloy_primitives::{map::AddressHashMap, Address, Bytes, Log, TxKind, U256}; use foundry_cheatcodes::CheatcodesExecutor; -use foundry_evm_core::{ - backend::{update_state, DatabaseExt}, - InspectorExt, -}; +use foundry_evm_core::{backend::DatabaseExt, InspectorExt}; use foundry_evm_coverage::HitMaps; use foundry_evm_traces::{SparsedTraceArena, TraceMode}; use revm::{ @@ -17,7 +14,8 @@ use revm::{ EOFCreateKind, Gas, InstructionResult, Interpreter, InterpreterResult, }, primitives::{ - BlockEnv, CreateScheme, Env, EnvWithHandlerCfg, ExecutionResult, Output, TransactTo, + Account, AccountStatus, BlockEnv, CreateScheme, Env, EnvWithHandlerCfg, ExecutionResult, + HashMap, Output, TransactTo, }, EvmContext, Inspector, }; @@ -218,27 +216,6 @@ macro_rules! call_inspectors { }; } -/// Same as [`call_inspectors!`], but with depth adjustment for isolated execution. -macro_rules! call_inspectors_adjust_depth { - ([$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $call:expr, $self:ident, $data:ident $(,)?) => { - $data.journaled_state.depth += $self.in_inner_context as usize; - call_inspectors!([$($inspector),+], |$id| $call); - $data.journaled_state.depth -= $self.in_inner_context as usize; - }; - (#[ret] [$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $call:expr, $self:ident, $data:ident $(,)?) => { - $data.journaled_state.depth += $self.in_inner_context as usize; - $( - if let Some($id) = $inspector { - if let Some(result) = ({ #[inline(always)] #[cold] || $call })() { - $data.journaled_state.depth -= $self.in_inner_context as usize; - return result; - } - } - )+ - $data.journaled_state.depth -= $self.in_inner_context as usize; - }; -} - /// The collected results of [`InspectorStack`]. pub struct InspectorData { pub logs: Vec, @@ -293,6 +270,7 @@ pub struct InspectorStackInner { /// Flag marking if we are in the inner EVM context. pub in_inner_context: bool, pub inner_context_data: Option, + pub top_frame_journal: HashMap, } /// Struct keeping mutable references to both parts of [InspectorStack] and implementing @@ -489,7 +467,7 @@ impl InspectorStackRefMut<'_> { outcome: CallOutcome, ) -> CallOutcome { let result = outcome.result.result; - call_inspectors_adjust_depth!( + call_inspectors!( #[ret] [&mut self.fuzzer, &mut self.tracer, &mut self.cheatcodes, &mut self.printer], |inspector| { @@ -502,8 +480,56 @@ impl InspectorStackRefMut<'_> { new_outcome.output() != outcome.output()); different.then_some(new_outcome) }, - self, - ecx + ); + + outcome + } + + fn do_create_end( + &mut self, + ecx: &mut EvmContext<&mut dyn DatabaseExt>, + call: &CreateInputs, + outcome: CreateOutcome, + ) -> CreateOutcome { + let result = outcome.result.result; + call_inspectors!( + #[ret] + [&mut self.tracer, &mut self.cheatcodes, &mut self.printer], + |inspector| { + let new_outcome = inspector.create_end(ecx, call, outcome.clone()); + + // If the inspector returns a different status or a revert with a non-empty message, + // we assume it wants to tell us something + let different = new_outcome.result.result != result || + (new_outcome.result.result == InstructionResult::Revert && + new_outcome.output() != outcome.output()); + different.then_some(new_outcome) + }, + ); + + outcome + } + + fn do_eofcreate_end( + &mut self, + ecx: &mut EvmContext<&mut dyn DatabaseExt>, + call: &EOFCreateInputs, + outcome: CreateOutcome, + ) -> CreateOutcome { + let result = outcome.result.result; + call_inspectors!( + #[ret] + [&mut self.tracer, &mut self.cheatcodes, &mut self.printer], + |inspector| { + let new_outcome = inspector.eofcreate_end(ecx, call, outcome.clone()); + + // If the inspector returns a different status or a revert with a non-empty message, + // we assume it wants to tell us something + let different = new_outcome.result.result != result || + (new_outcome.result.result == InstructionResult::Revert && + new_outcome.output() != outcome.output()); + different.then_some(new_outcome) + }, ); outcome @@ -520,8 +546,6 @@ impl InspectorStackRefMut<'_> { ) -> (InterpreterResult, Option

) { let ecx = &mut ecx.inner; - ecx.db.commit(ecx.journaled_state.state.clone()); - let cached_env = ecx.env.clone(); ecx.env.block.basefee = U256::ZERO; @@ -545,6 +569,29 @@ impl InspectorStackRefMut<'_> { let env = EnvWithHandlerCfg::new_with_spec_id(ecx.env.clone(), ecx.spec_id()); let res = self.with_stack(|inspector| { let mut evm = crate::utils::new_evm_with_inspector(&mut ecx.db, env, inspector); + + evm.context.evm.inner.journaled_state.state = { + let mut state = ecx.journaled_state.state.clone(); + + for (addr, acc_mut) in &mut state { + // mark all accounts cold, besides preloaded addresses + if !ecx.journaled_state.warm_preloaded_addresses.contains(addr) { + acc_mut.mark_cold(); + } + + // mark all slots cold + for slot_mut in acc_mut.storage.values_mut() { + slot_mut.is_cold = true; + slot_mut.original_value = slot_mut.present_value; + } + } + + state + }; + + // set depth to 1 to make sure traces are collected correctly + evm.context.evm.inner.journaled_state.depth = 1; + let res = evm.transact(); // need to reset the env in case it was modified via cheatcodes during execution @@ -560,43 +607,35 @@ impl InspectorStackRefMut<'_> { let mut gas = Gas::new(gas_limit); - let Ok(mut res) = res else { + let Ok(res) = res else { // Should we match, encode and propagate error as a revert reason? let result = InterpreterResult { result: InstructionResult::Revert, output: Bytes::new(), gas }; return (result, None); }; - // Commit changes after transaction - ecx.db.commit(res.state.clone()); - - // Update both states with new DB data after commit. - if let Err(e) = update_state(&mut ecx.journaled_state.state, &mut ecx.db, None) { - let res = InterpreterResult { - result: InstructionResult::Revert, - output: Bytes::from(e.to_string()), - gas, - }; - return (res, None); - } - if let Err(e) = update_state(&mut res.state, &mut ecx.db, None) { - let res = InterpreterResult { - result: InstructionResult::Revert, - output: Bytes::from(e.to_string()), - gas, + for (addr, mut acc) in res.state { + let Some(acc_mut) = ecx.journaled_state.state.get_mut(&addr) else { + ecx.journaled_state.state.insert(addr, acc); + continue }; - return (res, None); - } - // Merge transaction journal into the active journal. - for (addr, acc) in res.state { - if let Some(acc_mut) = ecx.journaled_state.state.get_mut(&addr) { - acc_mut.status |= acc.status; - for (key, val) in acc.storage { - acc_mut.storage.entry(key).or_insert(val); - } - } else { - ecx.journaled_state.state.insert(addr, acc); + // make sure accounts that were warmed earlier do not become cold + if acc.status.contains(AccountStatus::Cold) && + !acc_mut.status.contains(AccountStatus::Cold) + { + acc.status -= AccountStatus::Cold; + } + acc_mut.info = acc.info; + acc_mut.status |= acc.status; + + for (key, val) in acc.storage { + let Some(slot_mut) = acc_mut.storage.get_mut(&key) else { + acc_mut.storage.insert(key, val); + continue + }; + slot_mut.present_value = val.present_value; + slot_mut.is_cold &= val.is_cold; } } @@ -640,6 +679,39 @@ impl InspectorStackRefMut<'_> { out } + + /// Invoked at the beginning of a new top-level (0 depth) frame. + fn top_level_frame_start(&mut self, ecx: &mut EvmContext<&mut dyn DatabaseExt>) { + if self.enable_isolation { + // If we're in isolation mode, we need to keep track of the state at the beginning of + // the frame to be able to roll back on revert + self.top_frame_journal = ecx.journaled_state.state.clone(); + } + } + + /// Invoked at the end of root frame. + fn top_level_frame_end( + &mut self, + ecx: &mut EvmContext<&mut dyn DatabaseExt>, + result: InstructionResult, + ) { + if !result.is_revert() { + return; + } + // Encountered a revert, since cheatcodes may have altered the evm state in such a way + // that violates some constraints, e.g. `deal`, we need to manually roll back on revert + // before revm reverts the state itself + if let Some(cheats) = self.cheatcodes.as_mut() { + cheats.on_revert(ecx); + } + + // If we're in isolation mode, we need to rollback to state before the root frame was + // created We can't rely on revm's journal because it doesn't account for changes + // made by isolated calls + if self.enable_isolation { + ecx.journaled_state.state = std::mem::take(&mut self.top_frame_journal); + } + } } impl Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'_> { @@ -648,16 +720,14 @@ impl Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'_> { interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut dyn DatabaseExt>, ) { - call_inspectors_adjust_depth!( + call_inspectors!( [&mut self.coverage, &mut self.tracer, &mut self.cheatcodes, &mut self.printer], |inspector| inspector.initialize_interp(interpreter, ecx), - self, - ecx ); } fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut dyn DatabaseExt>) { - call_inspectors_adjust_depth!( + call_inspectors!( [ &mut self.fuzzer, &mut self.tracer, @@ -666,8 +736,6 @@ impl Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'_> { &mut self.printer, ], |inspector| inspector.step(interpreter, ecx), - self, - ecx ); } @@ -676,11 +744,9 @@ impl Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'_> { interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut dyn DatabaseExt>, ) { - call_inspectors_adjust_depth!( + call_inspectors!( [&mut self.tracer, &mut self.cheatcodes, &mut self.chisel_state, &mut self.printer], |inspector| inspector.step_end(interpreter, ecx), - self, - ecx ); } @@ -690,11 +756,9 @@ impl Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'_> { ecx: &mut EvmContext<&mut dyn DatabaseExt>, log: &Log, ) { - call_inspectors_adjust_depth!( + call_inspectors!( [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer], |inspector| inspector.log(interpreter, ecx, log), - self, - ecx ); } @@ -703,12 +767,16 @@ impl Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'_> { ecx: &mut EvmContext<&mut dyn DatabaseExt>, call: &mut CallInputs, ) -> Option { - if self.in_inner_context && ecx.journaled_state.depth == 0 { + if self.in_inner_context && ecx.journaled_state.depth == 1 { self.adjust_evm_data_for_inner_context(ecx); return None; } - call_inspectors_adjust_depth!( + if ecx.journaled_state.depth == 0 { + self.top_level_frame_start(ecx); + } + + call_inspectors!( #[ret] [&mut self.fuzzer, &mut self.tracer, &mut self.log_collector, &mut self.printer], |inspector| { @@ -720,11 +788,8 @@ impl Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'_> { } out }, - self, - ecx ); - 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) { @@ -740,12 +805,10 @@ impl Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'_> { 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; return Some(output); } } } - ecx.journaled_state.depth -= self.in_inner_context as usize; if self.enable_isolation && call.scheme == CallScheme::Call && @@ -772,20 +835,16 @@ impl Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'_> { inputs: &CallInputs, outcome: CallOutcome, ) -> CallOutcome { - // Inner context calls with depth 0 are being dispatched as top-level calls with depth 1. - // Avoid processing twice. - if self.in_inner_context && ecx.journaled_state.depth == 0 { + // We are processing inner context outputs in the outer context, so need to avoid processing + // twice. + if self.in_inner_context && ecx.journaled_state.depth == 1 { return outcome; } let outcome = self.do_call_end(ecx, inputs, outcome); - if outcome.result.is_revert() { - // Encountered a revert, since cheatcodes may have altered the evm state in such a way - // that violates some constraints, e.g. `deal`, we need to manually roll back on revert - // before revm reverts the state itself - if let Some(cheats) = self.cheatcodes.as_mut() { - cheats.on_revert(ecx); - } + + if ecx.journaled_state.depth == 0 { + self.top_level_frame_end(ecx, outcome.result.result); } outcome @@ -796,17 +855,19 @@ impl Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'_> { ecx: &mut EvmContext<&mut dyn DatabaseExt>, create: &mut CreateInputs, ) -> Option { - if self.in_inner_context && ecx.journaled_state.depth == 0 { + if self.in_inner_context && ecx.journaled_state.depth == 1 { self.adjust_evm_data_for_inner_context(ecx); return None; } - call_inspectors_adjust_depth!( + if ecx.journaled_state.depth == 0 { + self.top_level_frame_start(ecx); + } + + call_inspectors!( #[ret] [&mut self.tracer, &mut self.coverage, &mut self.cheatcodes], |inspector| inspector.create(ecx, create).map(Some), - self, - ecx ); if !matches!(create.scheme, CreateScheme::Create2 { .. }) && @@ -834,30 +895,17 @@ impl Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'_> { call: &CreateInputs, outcome: CreateOutcome, ) -> CreateOutcome { - // Inner context calls with depth 0 are being dispatched as top-level calls with depth 1. - // Avoid processing twice. - if self.in_inner_context && ecx.journaled_state.depth == 0 { + // We are processing inner context outputs in the outer context, so need to avoid processing + // twice. + if self.in_inner_context && ecx.journaled_state.depth == 1 { return outcome; } - let result = outcome.result.result; - - call_inspectors_adjust_depth!( - #[ret] - [&mut self.tracer, &mut self.cheatcodes, &mut self.printer], - |inspector| { - let new_outcome = inspector.create_end(ecx, call, outcome.clone()); + let outcome = self.do_create_end(ecx, call, outcome); - // If the inspector returns a different status or a revert with a non-empty message, - // we assume it wants to tell us something - let different = new_outcome.result.result != result || - (new_outcome.result.result == InstructionResult::Revert && - new_outcome.output() != outcome.output()); - different.then_some(new_outcome) - }, - self, - ecx - ); + if ecx.journaled_state.depth == 0 { + self.top_level_frame_end(ecx, outcome.result.result); + } outcome } @@ -867,17 +915,19 @@ impl Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'_> { ecx: &mut EvmContext<&mut dyn DatabaseExt>, create: &mut EOFCreateInputs, ) -> Option { - if self.in_inner_context && ecx.journaled_state.depth == 0 { + if self.in_inner_context && ecx.journaled_state.depth == 1 { self.adjust_evm_data_for_inner_context(ecx); return None; } - call_inspectors_adjust_depth!( + if ecx.journaled_state.depth == 0 { + self.top_level_frame_start(ecx); + } + + call_inspectors!( #[ret] [&mut self.tracer, &mut self.coverage, &mut self.cheatcodes], |inspector| inspector.eofcreate(ecx, create).map(Some), - self, - ecx ); if matches!(create.kind, EOFCreateKind::Tx { .. }) && @@ -910,30 +960,17 @@ impl Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'_> { call: &EOFCreateInputs, outcome: CreateOutcome, ) -> CreateOutcome { - // Inner context calls with depth 0 are being dispatched as top-level calls with depth 1. - // Avoid processing twice. - if self.in_inner_context && ecx.journaled_state.depth == 0 { + // We are processing inner context outputs in the outer context, so need to avoid processing + // twice. + if self.in_inner_context && ecx.journaled_state.depth == 1 { return outcome; } - let result = outcome.result.result; - - call_inspectors_adjust_depth!( - #[ret] - [&mut self.tracer, &mut self.cheatcodes, &mut self.printer], - |inspector| { - let new_outcome = inspector.eofcreate_end(ecx, call, outcome.clone()); + let outcome = self.do_eofcreate_end(ecx, call, outcome); - // If the inspector returns a different status or a revert with a non-empty message, - // we assume it wants to tell us something - let different = new_outcome.result.result != result || - (new_outcome.result.result == InstructionResult::Revert && - new_outcome.output() != outcome.output()); - different.then_some(new_outcome) - }, - self, - ecx - ); + if ecx.journaled_state.depth == 0 { + self.top_level_frame_end(ecx, outcome.result.result); + } outcome } @@ -951,12 +988,10 @@ impl InspectorExt for InspectorStackRefMut<'_> { ecx: &mut EvmContext<&mut dyn DatabaseExt>, inputs: &mut CreateInputs, ) -> bool { - call_inspectors_adjust_depth!( + call_inspectors!( #[ret] [&mut self.cheatcodes], |inspector| { inspector.should_use_create2_factory(ecx, inputs).then_some(true) }, - self, - ecx ); false diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 63a4a9210..28362671b 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -597,36 +597,6 @@ Encountered a total of 1 failing tests, 1 tests succeeded "#]]); }); -forgetest_init!(can_test_selfdestruct_with_isolation, |prj, cmd| { - prj.wipe_contracts(); - - prj.add_test( - "Contract.t.sol", - r#" -import {Test} from "forge-std/Test.sol"; - -contract Destructing { - function destruct() public { - selfdestruct(payable(address(0))); - } -} - -contract SelfDestructTest is Test { - function test() public { - Destructing d = new Destructing(); - vm.store(address(d), bytes32(0), bytes32(uint256(1))); - d.destruct(); - assertEq(address(d).code.length, 0); - assertEq(vm.load(address(d), bytes32(0)), bytes32(0)); - } -} - "#, - ) - .unwrap(); - - cmd.args(["test", "-vvvv", "--isolate"]).assert_success(); -}); - forgetest_init!(can_test_transient_storage_with_isolation, |prj, cmd| { prj.wipe_contracts(); diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index d463d12dd..9f46e3c2e 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -387,3 +387,10 @@ test_repro!(1543); // https://github.com/foundry-rs/foundry/issues/6643 test_repro!(6643); + +// https://github.com/foundry-rs/foundry/issues/8971 +test_repro!(8971; |config| { + let mut prj_config = Config::clone(&config.runner.config); + prj_config.isolate = true; + config.runner.config = Arc::new(prj_config); +}); diff --git a/testdata/default/repros/Issue8971.t.sol b/testdata/default/repros/Issue8971.t.sol new file mode 100644 index 000000000..d61e04ae6 --- /dev/null +++ b/testdata/default/repros/Issue8971.t.sol @@ -0,0 +1,50 @@ +// 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 number; + + function increment() public { + number++; + } +} + +/// @notice Test is mostly related to --isolate. Ensures that state is not affected by reverted +/// call to handler. +contract Handler { + bool public locked; + Counter public counter = new Counter(); + + function doNothing() public {} + + function doSomething() public { + locked = true; + counter.increment(); + this.doRevert(); + } + + function doRevert() public { + revert(); + } +} + +contract Invariant is DSTest { + Handler h; + + function setUp() public { + h = new Handler(); + } + + function targetContracts() public view returns (address[] memory contracts) { + contracts = new address[](1); + contracts[0] = address(h); + } + + function invariant_unchanged() public { + assertEq(h.locked(), false); + assertEq(h.counter().number(), 0); + } +} From eb046653de4047a27b181394338732e597965257 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 4 Oct 2024 16:47:37 +0200 Subject: [PATCH 061/100] fix: handle large years (#9032) --- crates/anvil/src/eth/backend/mem/mod.rs | 8 +++++++- crates/anvil/tests/it/anvil.rs | 12 ++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 0b002d3db..05a7a3ff7 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -66,6 +66,7 @@ use anvil_core::eth::{ utils::meets_eip155, }; use anvil_rpc::error::RpcError; +use chrono::Datelike; use flate2::{read::GzDecoder, write::GzEncoder, Compression}; use foundry_evm::{ backend::{DatabaseError, DatabaseResult, RevertStateSnapshotAction}, @@ -1123,7 +1124,12 @@ impl Backend { node_info!(" Block Number: {}", block_number); node_info!(" Block Hash: {:?}", block_hash); - node_info!(" Block Time: {:?}\n", timestamp.to_rfc2822()); + if timestamp.year() > 9999 { + // rf2822 panics with more than 4 digits + node_info!(" Block Time: {:?}\n", timestamp.to_rfc3339()); + } else { + node_info!(" Block Time: {:?}\n", timestamp.to_rfc2822()); + } let outcome = MinedBlockOutcome { block_number, included, invalid }; diff --git a/crates/anvil/tests/it/anvil.rs b/crates/anvil/tests/it/anvil.rs index b8aed751d..50e27c57a 100644 --- a/crates/anvil/tests/it/anvil.rs +++ b/crates/anvil/tests/it/anvil.rs @@ -1,5 +1,6 @@ //! tests for anvil specific logic +use alloy_eips::BlockNumberOrTag; use alloy_primitives::Address; use alloy_provider::Provider; use anvil::{spawn, NodeConfig}; @@ -76,3 +77,14 @@ async fn test_can_use_default_genesis_timestamp() { provider.get_block(0.into(), false.into()).await.unwrap().unwrap().header.timestamp ); } + +#[tokio::test(flavor = "multi_thread")] +async fn test_can_handle_large_timestamp() { + let (api, _handle) = spawn(NodeConfig::test()).await; + let num = 317071597274; + api.evm_set_next_block_timestamp(num).unwrap(); + api.mine_one().await; + + let block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); + assert_eq!(block.header.timestamp, num); +} From f089dff1c6c24d1ddf43c7cbefee46ea0197c88f Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sat, 5 Oct 2024 20:14:48 +0400 Subject: [PATCH 062/100] fix(`forge eip712`): fix handling of subtypes (#9035) * fix(forge eip712): fix handling of subtypes * fmt * clippy --- crates/forge/bin/cmd/bind_json.rs | 5 +-- crates/forge/bin/cmd/eip712.rs | 37 +++++++++++++++------- crates/forge/tests/cli/eip712.rs | 52 +++++++++++++++++++++++++++++++ crates/forge/tests/cli/main.rs | 1 + 4 files changed, 80 insertions(+), 15 deletions(-) create mode 100644 crates/forge/tests/cli/eip712.rs diff --git a/crates/forge/bin/cmd/bind_json.rs b/crates/forge/bin/cmd/bind_json.rs index d01abd60b..2736325a7 100644 --- a/crates/forge/bin/cmd/bind_json.rs +++ b/crates/forge/bin/cmd/bind_json.rs @@ -309,10 +309,7 @@ impl CompiledState { for ((path, id), (def, contract_name)) in structs { // For some structs there's no schema (e.g. if they contain a mapping), so we just skip // those. - let Some(schema) = resolver.resolve_struct_eip712(id, &mut Default::default(), true)? - else { - continue - }; + let Some(schema) = resolver.resolve_struct_eip712(id)? else { continue }; if !include.is_empty() { if !include.iter().any(|matcher| matcher.is_match(path)) { diff --git a/crates/forge/bin/cmd/eip712.rs b/crates/forge/bin/cmd/eip712.rs index 636c30580..f026d5848 100644 --- a/crates/forge/bin/cmd/eip712.rs +++ b/crates/forge/bin/cmd/eip712.rs @@ -11,7 +11,7 @@ use foundry_compilers::{ }, CompilerSettings, }; -use std::{collections::BTreeMap, path::PathBuf}; +use std::{collections::BTreeMap, fmt::Write, path::PathBuf}; foundry_config::impl_figment_convert!(Eip712Args, opts); @@ -62,9 +62,7 @@ impl Eip712Args { }; for (id, _) in structs_in_target { - if let Some(resolved) = - resolver.resolve_struct_eip712(id, &mut Default::default(), true)? - { + if let Some(resolved) = resolver.resolve_struct_eip712(id)? { println!("{resolved}"); println!(); } @@ -128,14 +126,19 @@ impl Resolver { /// /// Returns `None` if struct contains any fields that are not supported by EIP-712 (e.g. /// mappings or function pointers). - pub fn resolve_struct_eip712( + pub fn resolve_struct_eip712(&self, id: usize) -> Result> { + self.resolve_eip712_inner(id, &mut Default::default(), true, None) + } + + fn resolve_eip712_inner( &self, id: usize, subtypes: &mut BTreeMap, append_subtypes: bool, + rename: Option<&str>, ) -> Result> { let def = &self.structs[&id]; - let mut result = format!("{}(", def.name); + let mut result = format!("{}(", rename.unwrap_or(&def.name)); for (idx, member) in def.members.iter().enumerate() { let Some(ty) = self.resolve_type( @@ -146,9 +149,7 @@ impl Resolver { return Ok(None) }; - result.push_str(&ty); - result.push(' '); - result.push_str(&member.name); + write!(result, "{ty} {name}", name = member.name)?; if idx < def.members.len() - 1 { result.push(','); @@ -161,11 +162,14 @@ impl Resolver { return Ok(Some(result)) } - for subtype_id in subtypes.values().copied().collect::>() { + for (subtype_name, subtype_id) in + subtypes.iter().map(|(name, id)| (name.clone(), *id)).collect::>() + { if subtype_id == id { continue } - let Some(encoded_subtype) = self.resolve_struct_eip712(subtype_id, subtypes, false)? + let Some(encoded_subtype) = + self.resolve_eip712_inner(subtype_id, subtypes, false, Some(&subtype_name))? else { return Ok(None) }; @@ -204,6 +208,17 @@ impl Resolver { name.clone() // Otherwise, try assigning a new name. } else { + // iterate over members to check if they are resolvable and to populate subtypes + for member in &def.members { + if self.resolve_type( + member.type_name.as_ref().ok_or_eyre("missing type name")?, + subtypes, + )? + .is_none() + { + return Ok(None) + } + } let mut i = 0; let mut name = def.name.clone(); while subtypes.contains_key(&name) { diff --git a/crates/forge/tests/cli/eip712.rs b/crates/forge/tests/cli/eip712.rs new file mode 100644 index 000000000..2f832ae31 --- /dev/null +++ b/crates/forge/tests/cli/eip712.rs @@ -0,0 +1,52 @@ +forgetest!(test_eip712, |prj, cmd| { + let path = prj + .add_source( + "Structs", + r#" +library Structs { + struct Foo { + Bar bar; + } + + struct Bar { + Art art; + } + + struct Art { + uint256 id; + } + + struct Complex { + Structs2.Foo foo2; + Foo[] foos; + } +} + +library Structs2 { + struct Foo { + uint256 id; + } +} +"#, + ) + .unwrap(); + + cmd.forge_fuse().args(["eip712", path.to_string_lossy().as_ref()]).assert_success().stdout_eq( + str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +No files changed, compilation skipped +Foo(Bar bar)Art(uint256 id)Bar(Art art) + +Bar(Art art)Art(uint256 id) + +Art(uint256 id) + +Complex(Foo foo2,Foo_1[] foos)Art(uint256 id)Bar(Art art)Foo(uint256 id)Foo_1(Bar bar) + +Foo(uint256 id) + + +"#]], + ); +}); diff --git a/crates/forge/tests/cli/main.rs b/crates/forge/tests/cli/main.rs index 53c560457..6ad29ca48 100644 --- a/crates/forge/tests/cli/main.rs +++ b/crates/forge/tests/cli/main.rs @@ -15,6 +15,7 @@ mod coverage; mod create; mod debug; mod doc; +mod eip712; mod multi_script; mod script; mod soldeer; From a5f922d6fb0195b46a0c9c39b658175d7ad817ac Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 6 Oct 2024 04:22:59 +0200 Subject: [PATCH 063/100] chore(deps): weekly `cargo update` (#9041) Locking 40 packages to latest compatible versions Updating alloy-chains v0.1.34 -> v0.1.36 Updating alloy-network-primitives v0.4.0 -> v0.4.2 Updating alloy-rpc-types-anvil v0.4.0 -> v0.4.2 Updating alloy-rpc-types-engine v0.4.0 -> v0.4.2 Updating alloy-rpc-types-eth v0.4.0 -> v0.4.2 Updating alloy-rpc-types-trace v0.4.0 -> v0.4.2 Updating alloy-rpc-types-txpool v0.4.0 -> v0.4.2 Updating async-compression v0.4.12 -> v0.4.13 Updating async-stream v0.3.5 -> v0.3.6 Updating async-stream-impl v0.3.5 -> v0.3.6 Updating aws-config v1.5.7 -> v1.5.8 Updating aws-sdk-kms v1.45.0 -> v1.46.0 Updating aws-sdk-sso v1.44.0 -> v1.45.0 Updating aws-sdk-ssooidc v1.45.0 -> v1.46.0 Updating aws-sdk-sts v1.44.0 -> v1.45.0 Updating cc v1.1.24 -> v1.1.25 Updating clap v4.5.18 -> v4.5.19 Updating clap_builder v4.5.18 -> v4.5.19 Updating clap_complete v4.5.29 -> v4.5.32 Updating futures v0.3.30 -> v0.3.31 Updating futures-channel v0.3.30 -> v0.3.31 Updating futures-core v0.3.30 -> v0.3.31 Updating futures-executor v0.3.30 -> v0.3.31 Updating futures-io v0.3.30 -> v0.3.31 Updating futures-macro v0.3.30 -> v0.3.31 Updating futures-sink v0.3.30 -> v0.3.31 Updating futures-task v0.3.30 -> v0.3.31 Updating futures-util v0.3.30 -> v0.3.31 Updating gcloud-sdk v0.25.6 -> v0.25.7 Adding hashbrown v0.15.0 Updating indexmap v2.5.0 -> v2.6.0 Updating ipnet v2.10.0 -> v2.10.1 Updating once_cell v1.20.1 -> v1.20.2 Updating pin-project v1.1.5 -> v1.1.6 Updating pin-project-internal v1.1.5 -> v1.1.6 Updating rustls v0.23.13 -> v0.23.14 Updating snapbox v0.6.17 -> v0.6.18 Updating terminal_size v0.3.0 -> v0.4.0 Updating unicode-bidi v0.3.15 -> v0.3.17 Adding unicode-width v0.2.0 note: pass `--verbose` to see 9 unchanged dependencies behind latest Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> --- Cargo.lock | 265 +++++++++++++++++++++++++++-------------------------- 1 file changed, 137 insertions(+), 128 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 70ea8bef4..80fbb6212 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,9 +74,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-chains" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8158b4878c67837e5413721cc44298e6a2d88d39203175ea025e51892a16ba4c" +checksum = "94c225801d42099570d0674701dddd4142f0ef715282aeb5985042e2ec962df7" dependencies = [ "num_enum", "serde", @@ -122,11 +122,11 @@ dependencies = [ "alloy-dyn-abi", "alloy-json-abi", "alloy-network", - "alloy-network-primitives 0.4.0", + "alloy-network-primitives 0.4.2", "alloy-primitives", "alloy-provider", "alloy-pubsub", - "alloy-rpc-types-eth 0.4.0", + "alloy-rpc-types-eth 0.4.2", "alloy-sol-types", "alloy-transport", "futures", @@ -264,9 +264,9 @@ dependencies = [ "alloy-consensus 0.4.2", "alloy-eips 0.4.2", "alloy-json-rpc", - "alloy-network-primitives 0.4.0", + "alloy-network-primitives 0.4.2", "alloy-primitives", - "alloy-rpc-types-eth 0.4.0", + "alloy-rpc-types-eth 0.4.2", "alloy-serde 0.4.2", "alloy-signer", "alloy-sol-types", @@ -290,9 +290,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8416e4e9ceee8014d2f89fc3dde331da392b26d14226a0d5cbc207ae7799fb2f" +checksum = "801492711d4392b2ccf5fc0bc69e299fa1aab15167d74dcaa9aab96a54f684bd" dependencies = [ "alloy-consensus 0.4.2", "alloy-eips 0.4.2", @@ -317,7 +317,7 @@ dependencies = [ "getrandom", "hashbrown 0.14.5", "hex-literal", - "indexmap 2.5.0", + "indexmap 2.6.0", "itoa", "k256", "keccak-asm", @@ -343,11 +343,11 @@ dependencies = [ "alloy-eips 0.4.2", "alloy-json-rpc", "alloy-network", - "alloy-network-primitives 0.4.0", + "alloy-network-primitives 0.4.2", "alloy-primitives", "alloy-pubsub", "alloy-rpc-client", - "alloy-rpc-types-eth 0.4.0", + "alloy-rpc-types-eth 0.4.2", "alloy-rpc-types-trace", "alloy-rpc-types-txpool", "alloy-transport", @@ -361,7 +361,7 @@ dependencies = [ "futures", "futures-utils-wasm", "lru", - "pin-project 1.1.5", + "pin-project 1.1.6", "reqwest", "serde", "serde_json", @@ -426,7 +426,7 @@ dependencies = [ "alloy-transport-ipc", "alloy-transport-ws", "futures", - "pin-project 1.1.5", + "pin-project 1.1.6", "reqwest", "serde", "serde_json", @@ -446,7 +446,7 @@ dependencies = [ "alloy-primitives", "alloy-rpc-types-anvil", "alloy-rpc-types-engine", - "alloy-rpc-types-eth 0.4.0", + "alloy-rpc-types-eth 0.4.2", "alloy-rpc-types-trace", "alloy-rpc-types-txpool", "alloy-serde 0.4.2", @@ -455,9 +455,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142f6fb21ef1857b3d175dc16b73d67f4b70914e6898610da3c0b65a1281fe7b" +checksum = "d780adaa5d95b07ad92006b2feb68ecfa7e2015f7d5976ceaac4c906c73ebd07" dependencies = [ "alloy-primitives", "alloy-serde 0.4.2", @@ -466,9 +466,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c032e9b725a990be03cc0ddd9fa73c21f61d1449b328083aa22fbfafb03eda1b" +checksum = "e0285c4c09f838ab830048b780d7f4a4f460f309aa1194bb049843309524c64c" dependencies = [ "alloy-consensus 0.4.2", "alloy-eips 0.4.2", @@ -505,13 +505,13 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e1f655dcd5e9ccf215cbffb69272698ef6b3ec76907e8937345f2a82ae04ed4" +checksum = "413f4aa3ccf2c3e4234a047c5fa4727916d7daf25a89f9b765df0ba09784fd87" dependencies = [ "alloy-consensus 0.4.2", "alloy-eips 0.4.2", - "alloy-network-primitives 0.4.0", + "alloy-network-primitives 0.4.2", "alloy-primitives", "alloy-rlp", "alloy-serde 0.4.2", @@ -524,12 +524,12 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6900c7d94528217465f6b619f03adb2eecc9682f9083d49ad7d40ec6eda0ed04" +checksum = "017cad3e5793c5613588c1f9732bcbad77e820ba7d0feaba3527749f856fdbc5" dependencies = [ "alloy-primitives", - "alloy-rpc-types-eth 0.4.0", + "alloy-rpc-types-eth 0.4.2", "alloy-serde 0.4.2", "serde", "serde_json", @@ -538,12 +538,12 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "954781be5ca2e15db08d753712f494504a04771ee4296de1e834e65c105b8ec3" +checksum = "2b230e321c416be7f50530159392b4c41a45596d40d97e185575bcd0b545e521" dependencies = [ "alloy-primitives", - "alloy-rpc-types-eth 0.4.0", + "alloy-rpc-types-eth 0.4.2", "alloy-serde 0.4.2", "serde", ] @@ -702,7 +702,7 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.5.0", + "indexmap 2.6.0", "proc-macro-error2", "proc-macro2", "quote", @@ -797,7 +797,7 @@ dependencies = [ "bytes", "futures", "interprocess", - "pin-project 1.1.5", + "pin-project 1.1.6", "serde", "serde_json", "tempfile", @@ -816,7 +816,7 @@ dependencies = [ "alloy-transport", "futures", "http 1.1.0", - "rustls 0.23.13", + "rustls 0.23.14", "serde_json", "tokio", "tokio-tungstenite", @@ -1032,7 +1032,7 @@ dependencies = [ "futures", "interprocess", "parking_lot", - "pin-project 1.1.5", + "pin-project 1.1.6", "serde", "serde_json", "thiserror", @@ -1062,7 +1062,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44055e597c674aef7cb903b2b9f6e4cba1277ed0d2d61dae7cd52d7ffa81f8e2" dependencies = [ - "unicode-width", + "unicode-width 0.1.14", "yansi", ] @@ -1207,9 +1207,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa" +checksum = "7e614738943d3f68c628ae3dbce7c3daffb196665f82f8c8ea6b65de73c79429" dependencies = [ "flate2", "futures-core", @@ -1240,9 +1240,9 @@ dependencies = [ [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", @@ -1251,9 +1251,9 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", @@ -1332,9 +1332,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-config" -version = "1.5.7" +version = "1.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8191fb3091fa0561d1379ef80333c3c7191c6f0435d986e85821bcf7acbd1126" +checksum = "7198e6f03240fdceba36656d8be440297b6b82270325908c7381f37d826a74f6" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1399,9 +1399,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.45.0" +version = "1.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0caf20b8855dbeb458552e6c8f8f9eb92b95e4a131725b93540ec73d60c38eb3" +checksum = "e33590e8d45206fdc4273ded8a1f292bcceaadd513037aa790fc67b237bc30ee" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1421,9 +1421,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.44.0" +version = "1.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b90cfe6504115e13c41d3ea90286ede5aa14da294f3fe077027a6e83850843c" +checksum = "e33ae899566f3d395cbf42858e433930682cc9c1889fa89318896082fef45efb" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1443,9 +1443,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.45.0" +version = "1.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167c0fad1f212952084137308359e8e4c4724d1c643038ce163f06de9662c1d0" +checksum = "f39c09e199ebd96b9f860b0fce4b6625f211e064ad7c8693b72ecf7ef03881e0" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1465,9 +1465,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.44.0" +version = "1.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb5f98188ec1435b68097daa2a37d74b9d17c9caa799466338a8d1544e71b9d" +checksum = "3d95f93a98130389eb6233b9d615249e543f6c24a68ca1f109af9ca5164a8765" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1994,9 +1994,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.24" +version = "1.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" +checksum = "e8d9e0b4957f635b8d3da819d0db5603620467ecf1f692d22a8c2717ce27e6d8" dependencies = [ "shlex", ] @@ -2109,9 +2109,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.18" +version = "4.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" +checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615" dependencies = [ "clap_builder", "clap_derive", @@ -2119,9 +2119,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.18" +version = "4.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" +checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b" dependencies = [ "anstream", "anstyle", @@ -2129,14 +2129,14 @@ dependencies = [ "strsim", "terminal_size", "unicase", - "unicode-width", + "unicode-width 0.2.0", ] [[package]] name = "clap_complete" -version = "4.5.29" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8937760c3f4c60871870b8c3ee5f9b30771f792a7045c48bcbba999d7d6b3b8e" +checksum = "74a01f4f9ee6c066d42a1c8dedf0dcddad16c72a8981a309d6398de3a75b0c39" dependencies = [ "clap", ] @@ -2307,7 +2307,7 @@ dependencies = [ "crossterm 0.27.0", "strum", "strum_macros", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -2342,7 +2342,7 @@ dependencies = [ "encode_unicode", "lazy_static", "libc", - "unicode-width", + "unicode-width 0.1.14", "windows-sys 0.52.0", ] @@ -4055,7 +4055,7 @@ dependencies = [ "foundry-evm-core", "foundry-evm-coverage", "foundry-evm-traces", - "indexmap 2.5.0", + "indexmap 2.6.0", "itertools 0.13.0", "parking_lot", "proptest", @@ -4239,9 +4239,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -4254,9 +4254,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -4264,15 +4264,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -4281,15 +4281,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -4298,21 +4298,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -4334,9 +4334,9 @@ checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" [[package]] name = "gcloud-sdk" -version = "0.25.6" +version = "0.25.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d92f38cbe5b8796d2ab3f3c5f3bc286aa778015d5c5f67e2d0cbbe5d348473f" +checksum = "2a1130d4e37435a63bd9d968a33c11b64bf35a2013779a353e29cd3598989d39" dependencies = [ "async-trait", "bytes", @@ -4668,7 +4668,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.5.0", + "indexmap 2.6.0", "slab", "tokio", "tokio-util", @@ -4687,7 +4687,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.1.0", - "indexmap 2.5.0", + "indexmap 2.6.0", "slab", "tokio", "tokio-util", @@ -4735,6 +4735,12 @@ dependencies = [ "serde", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + [[package]] name = "heck" version = "0.5.0" @@ -4973,7 +4979,7 @@ dependencies = [ "http 1.1.0", "hyper 1.4.1", "hyper-util", - "rustls 0.23.13", + "rustls 0.23.14", "rustls-native-certs 0.8.0", "rustls-pki-types", "tokio", @@ -5160,13 +5166,13 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "arbitrary", "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.0", "serde", ] @@ -5180,7 +5186,7 @@ dependencies = [ "instant", "number_prefix", "portable-atomic", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -5271,9 +5277,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" [[package]] name = "is-terminal" @@ -5670,7 +5676,7 @@ dependencies = [ "cfg-if", "miette-derive", "thiserror", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -6073,12 +6079,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.1" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" -dependencies = [ - "portable-atomic", -] +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "oorandom" @@ -6423,7 +6426,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.5.0", + "indexmap 2.6.0", ] [[package]] @@ -6518,11 +6521,11 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" dependencies = [ - "pin-project-internal 1.1.5", + "pin-project-internal 1.1.6", ] [[package]] @@ -6538,9 +6541,9 @@ dependencies = [ [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" dependencies = [ "proc-macro2", "quote", @@ -6773,7 +6776,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38ee68ae331824036479c84060534b18254c864fa73366c58d86db3b7b811619" dependencies = [ "futures", - "indexmap 2.5.0", + "indexmap 2.6.0", "nix 0.28.0", "tokio", "tracing", @@ -6911,7 +6914,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62ffd2f9a162cfae131bed6d9d1ed60adced33be340a94f96952897d7cb0c240" dependencies = [ "chrono", - "indexmap 2.5.0", + "indexmap 2.6.0", "newtype-uuid", "quick-xml 0.36.2", "strip-ansi-escapes", @@ -6957,7 +6960,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.23.13", + "rustls 0.23.14", "socket2", "thiserror", "tokio", @@ -6974,7 +6977,7 @@ dependencies = [ "rand", "ring", "rustc-hash", - "rustls 0.23.13", + "rustls 0.23.14", "slab", "thiserror", "tinyvec", @@ -7077,7 +7080,7 @@ dependencies = [ "strum_macros", "unicode-segmentation", "unicode-truncate", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -7205,7 +7208,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.13", + "rustls 0.23.14", "rustls-native-certs 0.8.0", "rustls-pemfile 2.2.0", "rustls-pki-types", @@ -7250,7 +7253,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43c44af0bf801f48d25f7baf25cf72aff4c02d610f83b428175228162fef0246" dependencies = [ "alloy-primitives", - "alloy-rpc-types-eth 0.4.0", + "alloy-rpc-types-eth 0.4.2", "alloy-rpc-types-trace", "alloy-sol-types", "anstyle", @@ -7504,9 +7507,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.13" +version = "0.23.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" dependencies = [ "log", "once_cell", @@ -7622,7 +7625,7 @@ dependencies = [ "nix 0.28.0", "radix_trie", "unicode-segmentation", - "unicode-width", + "unicode-width 0.1.14", "utf8parse", "windows-sys 0.52.0", ] @@ -7906,7 +7909,7 @@ version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.6.0", "itoa", "memchr", "ryu", @@ -8160,9 +8163,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "snapbox" -version = "0.6.17" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "840b73eb3148bc3cbc10ebe00ec9bc6d96033e658d022c4adcbf3f35596fd64a" +checksum = "7ba434818a8a9b1b106404288d6bd75a94348aae8fc9a518b211b609a36a54bc" dependencies = [ "anstream", "anstyle", @@ -8476,12 +8479,12 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" dependencies = [ "rustix", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -8684,7 +8687,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.13", + "rustls 0.23.14", "rustls-pki-types", "tokio", ] @@ -8721,7 +8724,7 @@ checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" dependencies = [ "futures-util", "log", - "rustls 0.23.13", + "rustls 0.23.14", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -8757,7 +8760,7 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.6.0", "serde", "serde_spanned", "toml_datetime", @@ -8779,7 +8782,7 @@ version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.6.0", "serde", "serde_spanned", "toml_datetime", @@ -8805,7 +8808,7 @@ dependencies = [ "hyper-timeout", "hyper-util", "percent-encoding", - "pin-project 1.1.5", + "pin-project 1.1.6", "prost", "rustls-native-certs 0.8.0", "rustls-pemfile 2.2.0", @@ -8834,7 +8837,7 @@ dependencies = [ "futures-core", "futures-util", "indexmap 1.9.3", - "pin-project 1.1.5", + "pin-project 1.1.6", "pin-project-lite", "rand", "slab", @@ -9046,7 +9049,7 @@ dependencies = [ "httparse", "log", "rand", - "rustls 0.23.13", + "rustls 0.23.14", "rustls-pki-types", "sha1", "thiserror", @@ -9103,9 +9106,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-bom" @@ -9142,7 +9145,7 @@ checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" dependencies = [ "itertools 0.13.0", "unicode-segmentation", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -9151,6 +9154,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -9909,7 +9918,7 @@ dependencies = [ "crossbeam-utils", "displaydoc", "flate2", - "indexmap 2.5.0", + "indexmap 2.6.0", "memchr", "thiserror", "zopfli", From 8905af382e04b1bf3a492880abe5904a56e88491 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sun, 6 Oct 2024 13:10:35 +0400 Subject: [PATCH 064/100] fix: normalize EVM version in chisel (#9040) * fix: normalize EVM version in chisel * rm test * clippy * fix --- crates/chisel/src/session_source.rs | 16 +++++----------- crates/chisel/tests/cache.rs | 27 +-------------------------- 2 files changed, 6 insertions(+), 37 deletions(-) diff --git a/crates/chisel/src/session_source.rs b/crates/chisel/src/session_source.rs index e1b31a972..f80761e0d 100644 --- a/crates/chisel/src/session_source.rs +++ b/crates/chisel/src/session_source.rs @@ -106,16 +106,6 @@ impl SessionSourceConfig { match solc_req { SolcReq::Version(version) => { - // Validate that the requested evm version is supported by the solc version - let req_evm_version = self.foundry_config.evm_version; - if let Some(compat_evm_version) = req_evm_version.normalize_version_solc(&version) { - if req_evm_version > compat_evm_version { - eyre::bail!( - "The set evm version, {req_evm_version}, is not supported by solc {version}. Upgrade to a newer solc version." - ); - } - } - let solc = if let Some(solc) = Solc::find_svm_installed_version(&version)? { solc } else { @@ -322,7 +312,11 @@ impl SessionSource { let settings = Settings { remappings, - evm_version: Some(self.config.foundry_config.evm_version), + evm_version: self + .config + .foundry_config + .evm_version + .normalize_version_solc(&self.solc.version), ..Default::default() }; diff --git a/crates/chisel/tests/cache.rs b/crates/chisel/tests/cache.rs index 5f0864bee..7016bce09 100644 --- a/crates/chisel/tests/cache.rs +++ b/crates/chisel/tests/cache.rs @@ -1,7 +1,6 @@ use chisel::session::ChiselSession; use foundry_compilers::artifacts::EvmVersion; -use foundry_config::{Config, SolcReq}; -use semver::Version; +use foundry_config::Config; use serial_test::serial; use std::path::Path; @@ -221,27 +220,3 @@ fn test_load_latest_cache() { assert_eq!(new_env.id.unwrap(), "1"); assert_eq!(new_env.session_source.to_repl_source(), env.session_source.to_repl_source()); } - -#[test] -#[serial] -fn test_solc_evm_configuration_mismatch() { - // Create and clear the cache directory - ChiselSession::create_cache_dir().unwrap(); - ChiselSession::clear_cache().unwrap(); - - // Force the solc version to be 0.8.13 which does not support Paris - let foundry_config = Config { - evm_version: EvmVersion::Paris, - solc: Some(SolcReq::Version(Version::new(0, 8, 13))), - ..Default::default() - }; - - // Create a new session that is expected to fail - let error = ChiselSession::new(chisel::session_source::SessionSourceConfig { - foundry_config, - ..Default::default() - }) - .unwrap_err(); - - assert_eq!(error.to_string(), "The set evm version, paris, is not supported by solc 0.8.13. Upgrade to a newer solc version."); -} From 47f1ecb9c6f7e251c5bf2452c1f327d5508481a9 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Mon, 7 Oct 2024 12:12:16 +0200 Subject: [PATCH 065/100] fix(`cheatcodes`): mark `vm.breakpoint` as `pure` (#9051) breakpoint external -> external pure --- crates/cheatcodes/assets/cheatcodes.json | 8 ++++---- crates/cheatcodes/spec/src/vm.rs | 4 ++-- testdata/cheats/Vm.sol | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 9f0870226..ac42e63c1 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -3035,9 +3035,9 @@ "func": { "id": "breakpoint_0", "description": "Writes a breakpoint to jump to in the debugger.", - "declaration": "function breakpoint(string calldata char) external;", + "declaration": "function breakpoint(string calldata char) external pure;", "visibility": "external", - "mutability": "", + "mutability": "pure", "signature": "breakpoint(string)", "selector": "0xf0259e92", "selectorBytes": [ @@ -3055,9 +3055,9 @@ "func": { "id": "breakpoint_1", "description": "Writes a conditional breakpoint to jump to in the debugger.", - "declaration": "function breakpoint(string calldata char, bool value) external;", + "declaration": "function breakpoint(string calldata char, bool value) external pure;", "visibility": "external", - "mutability": "", + "mutability": "pure", "signature": "breakpoint(string,bool)", "selector": "0xf7d39a8d", "selectorBytes": [ diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 4ec51130a..129c553a9 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -757,11 +757,11 @@ interface Vm { /// Writes a breakpoint to jump to in the debugger. #[cheatcode(group = Testing, safety = Safe)] - function breakpoint(string calldata char) external; + function breakpoint(string calldata char) external pure; /// Writes a conditional breakpoint to jump to in the debugger. #[cheatcode(group = Testing, safety = Safe)] - function breakpoint(string calldata char, bool value) external; + function breakpoint(string calldata char, bool value) external pure; /// Returns the Foundry version. /// Format: ++ diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 7dc38480a..64c599a9e 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -147,8 +147,8 @@ interface Vm { function assumeNoRevert() external pure; function blobBaseFee(uint256 newBlobBaseFee) external; function blobhashes(bytes32[] calldata hashes) external; - function breakpoint(string calldata char) external; - function breakpoint(string calldata char, bool value) external; + function breakpoint(string calldata char) external pure; + function breakpoint(string calldata char, bool value) external pure; function broadcastRawTransaction(bytes calldata data) external; function broadcast() external; function broadcast(address signer) external; From 22a72d50aed05f5828655df2f29a1f8bab361653 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Mon, 7 Oct 2024 16:07:01 +0200 Subject: [PATCH 066/100] fix: include `traces` field when running `forge test -vvvv --json` (#9034) * enable traces as part of verbose output when tests are ran with --json, includes tests * Update crates/forge/tests/cli/test_cmd.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * Update crates/forge/tests/cli/test_cmd.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * use forgetest! directly --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/forge/bin/cmd/test/mod.rs | 3 +- crates/forge/src/result.rs | 1 - crates/forge/tests/cli/test_cmd.rs | 41 +++++ .../SimpleContractTestNonVerbose.json | 27 +++ .../fixtures/SimpleContractTestVerbose.json | 172 ++++++++++++++++++ 5 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 crates/forge/tests/fixtures/SimpleContractTestNonVerbose.json create mode 100644 crates/forge/tests/fixtures/SimpleContractTestVerbose.json diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 6d53a4756..0e87a4c66 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -499,6 +499,7 @@ impl TestArgs { runner.decode_internal = InternalTraceMode::Full; } + // Run tests in a non-streaming fashion and collect results for serialization. if self.json { let results = runner.test_collect(filter); println!("{}", serde_json::to_string(&results)?); @@ -516,7 +517,7 @@ impl TestArgs { let libraries = runner.libraries.clone(); - // Run tests. + // Run tests in a streaming fashion. let (tx, rx) = channel::<(String, SuiteResult)>(); let timer = Instant::now(); let show_progress = config.show_progress; diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index 0e00bb5e2..a6d7fded9 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -393,7 +393,6 @@ pub struct TestResult { pub kind: TestKind, /// Traces - #[serde(skip)] pub traces: Traces, /// Additional traces to use for gas report. diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 28362671b..f8b54cc41 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -274,6 +274,47 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) "#]]); }); +const SIMPLE_CONTRACT: &str = r#" +import "./test.sol"; + +contract SimpleContract { + uint256 public num; + + function setValues(uint256 _num) public { + num = _num; + } +} + +contract SimpleContractTest is DSTest { + function test() public { + SimpleContract c = new SimpleContract(); + c.setValues(100); + } +} + "#; + +forgetest!(can_run_test_with_json_output_verbose, |prj, cmd| { + prj.insert_ds_test(); + + prj.add_source("Simple.t.sol", SIMPLE_CONTRACT).unwrap(); + + // Assert that with verbose output the json output includes the traces + cmd.args(["test", "-vvv", "--json"]) + .assert_success() + .stdout_eq(file!["../fixtures/SimpleContractTestVerbose.json": Json]); +}); + +forgetest!(can_run_test_with_json_output_non_verbose, |prj, cmd| { + prj.insert_ds_test(); + + prj.add_source("Simple.t.sol", SIMPLE_CONTRACT).unwrap(); + + // Assert that without verbose output the json output does not include the traces + cmd.args(["test", "--json"]) + .assert_success() + .stdout_eq(file!["../fixtures/SimpleContractTestNonVerbose.json": Json]); +}); + // tests that `forge test` will pick up tests that are stored in the `test = ` config value forgetest!(can_run_test_in_custom_test_folder, |prj, cmd| { prj.insert_ds_test(); diff --git a/crates/forge/tests/fixtures/SimpleContractTestNonVerbose.json b/crates/forge/tests/fixtures/SimpleContractTestNonVerbose.json new file mode 100644 index 000000000..8fd8a0fae --- /dev/null +++ b/crates/forge/tests/fixtures/SimpleContractTestNonVerbose.json @@ -0,0 +1,27 @@ +{ + "src/Simple.t.sol:SimpleContractTest": { + "duration": "{...}", + "test_results": { + "test()": { + "status": "Success", + "reason": null, + "counterexample": null, + "logs": [], + "kind": { + "Unit": { + "gas": "{...}" + } + }, + "traces": [], + "labeled_addresses": {}, + "duration": { + "secs": "{...}", + "nanos": "{...}" + }, + "breakpoints": {}, + "gas_snapshots": {} + } + }, + "warnings": [] + } +} \ No newline at end of file diff --git a/crates/forge/tests/fixtures/SimpleContractTestVerbose.json b/crates/forge/tests/fixtures/SimpleContractTestVerbose.json new file mode 100644 index 000000000..c7f47cf53 --- /dev/null +++ b/crates/forge/tests/fixtures/SimpleContractTestVerbose.json @@ -0,0 +1,172 @@ +{ + "src/Simple.t.sol:SimpleContractTest": { + "duration": "{...}", + "test_results": { + "test()": { + "status": "Success", + "reason": null, + "counterexample": null, + "logs": [], + "kind": { + "Unit": { + "gas": "{...}" + } + }, + "traces": [ + [ + "Deployment", + { + "arena": [ + { + "parent": null, + "children": [], + "idx": 0, + "trace": { + "depth": 0, + "success": true, + "caller": "0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38", + "address": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", + "maybe_precompile": false, + "selfdestruct_address": null, + "selfdestruct_refund_target": null, + "selfdestruct_transferred_value": null, + "kind": "CREATE", + "value": "0x0", + "data": "{...}", + "output": "{...}", + "gas_used": "{...}", + "gas_limit": "{...}", + "status": "Return", + "steps": [], + "decoded": { + "label": null, + "return_data": null, + "call_data": null + } + }, + "logs": [], + "ordering": [] + } + ] + } + ], + [ + "Execution", + { + "arena": [ + { + "parent": null, + "children": [ + 1, + 2 + ], + "idx": 0, + "trace": { + "depth": 0, + "success": true, + "caller": "0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38", + "address": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", + "maybe_precompile": null, + "selfdestruct_address": null, + "selfdestruct_refund_target": null, + "selfdestruct_transferred_value": null, + "kind": "CALL", + "value": "0x0", + "data": "0xf8a8fd6d", + "output": "0x", + "gas_used": "{...}", + "gas_limit": "{...}", + "status": "Stop", + "steps": [], + "decoded": { + "label": null, + "return_data": null, + "call_data": null + } + }, + "logs": [], + "ordering": [ + { + "Call": 0 + }, + { + "Call": 1 + } + ] + }, + { + "parent": 0, + "children": [], + "idx": 1, + "trace": { + "depth": 1, + "success": true, + "caller": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", + "address": "0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f", + "maybe_precompile": false, + "selfdestruct_address": null, + "selfdestruct_refund_target": null, + "selfdestruct_transferred_value": null, + "kind": "CREATE", + "value": "0x0", + "data": "{...}", + "output": "{...}", + "gas_used": "{...}", + "gas_limit": "{...}", + "status": "Return", + "steps": [], + "decoded": { + "label": null, + "return_data": null, + "call_data": null + } + }, + "logs": [], + "ordering": [] + }, + { + "parent": 0, + "children": [], + "idx": 2, + "trace": { + "depth": 1, + "success": true, + "caller": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", + "address": "0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f", + "maybe_precompile": null, + "selfdestruct_address": null, + "selfdestruct_refund_target": null, + "selfdestruct_transferred_value": null, + "kind": "CALL", + "value": "0x0", + "data": "0xe26d14740000000000000000000000000000000000000000000000000000000000000064", + "output": "0x", + "gas_used": "{...}", + "gas_limit": "{...}", + "status": "Stop", + "steps": [], + "decoded": { + "label": null, + "return_data": null, + "call_data": null + } + }, + "logs": [], + "ordering": [] + } + ] + } + ] + ], + "labeled_addresses": {}, + "duration": { + "secs": "{...}", + "nanos": "{...}" + }, + "breakpoints": {}, + "gas_snapshots": {} + } + }, + "warnings": [] + } +} \ No newline at end of file From d7d9b407b20a5d2df1d06b07dafc1371a7e715b3 Mon Sep 17 00:00:00 2001 From: Yotam Bar-On Date: Mon, 7 Oct 2024 17:12:02 +0300 Subject: [PATCH 067/100] feat(cheatcodes): Add `vm.mockCalls` to mock different return data for multiple calls (#9024) * Refactor vm.mockCall to be based on mutable VecDeque * Add vm.mockCalls cheatcode * Refactor mock_call to be wrapper for mock_calls * Add a test to vm.mockCalls * Add test for vm.mockCalls with msg.value * Fix fmt & clippy following vm.mockCalls implementation * Fix Solidity fmt in testdata/default/cheats/MockCalls.t.sol * Add test in MockCalls.t.sol to check last mocked data persists * Remove allow(clippy::ptr_arg) from mock_call & mock_calls * Apply suggestions from code review --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/cheatcodes/assets/cheatcodes.json | 40 ++++++++++++++++ crates/cheatcodes/spec/src/vm.rs | 8 ++++ crates/cheatcodes/src/evm/mock.rs | 38 +++++++++++++-- crates/cheatcodes/src/inspector.rs | 40 ++++++++++------ testdata/cheats/Vm.sol | 2 + testdata/default/cheats/MockCalls.t.sol | 59 ++++++++++++++++++++++++ 6 files changed, 169 insertions(+), 18 deletions(-) create mode 100644 testdata/default/cheats/MockCalls.t.sol diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index ac42e63c1..9ef78f8b0 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -5777,6 +5777,46 @@ "status": "stable", "safety": "unsafe" }, + { + "func": { + "id": "mockCalls_0", + "description": "Mocks multiple calls to an address, returning specified data for each call.", + "declaration": "function mockCalls(address callee, bytes calldata data, bytes[] calldata returnData) external;", + "visibility": "external", + "mutability": "", + "signature": "mockCalls(address,bytes,bytes[])", + "selector": "0x5c5c3de9", + "selectorBytes": [ + 92, + 92, + 61, + 233 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, + { + "func": { + "id": "mockCalls_1", + "description": "Mocks multiple calls to an address with a specific `msg.value`, returning specified data for each call.", + "declaration": "function mockCalls(address callee, uint256 msgValue, bytes calldata data, bytes[] calldata returnData) external;", + "visibility": "external", + "mutability": "", + "signature": "mockCalls(address,uint256,bytes,bytes[])", + "selector": "0x08bcbae1", + "selectorBytes": [ + 8, + 188, + 186, + 225 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, { "func": { "id": "mockFunction", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 129c553a9..79249366c 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -465,6 +465,14 @@ interface Vm { #[cheatcode(group = Evm, safety = Unsafe)] function mockCall(address callee, uint256 msgValue, bytes calldata data, bytes calldata returnData) external; + /// Mocks multiple calls to an address, returning specified data for each call. + #[cheatcode(group = Evm, safety = Unsafe)] + function mockCalls(address callee, bytes calldata data, bytes[] calldata returnData) external; + + /// Mocks multiple calls to an address with a specific `msg.value`, returning specified data for each call. + #[cheatcode(group = Evm, safety = Unsafe)] + function mockCalls(address callee, uint256 msgValue, bytes calldata data, bytes[] calldata returnData) external; + /// Reverts a call to an address with specified revert data. #[cheatcode(group = Evm, safety = Unsafe)] function mockCallRevert(address callee, bytes calldata data, bytes calldata revertData) external; diff --git a/crates/cheatcodes/src/evm/mock.rs b/crates/cheatcodes/src/evm/mock.rs index ab858c612..50551d179 100644 --- a/crates/cheatcodes/src/evm/mock.rs +++ b/crates/cheatcodes/src/evm/mock.rs @@ -1,7 +1,7 @@ use crate::{inspector::InnerEcx, Cheatcode, Cheatcodes, CheatsCtxt, Result, Vm::*}; use alloy_primitives::{Address, Bytes, U256}; use revm::{interpreter::InstructionResult, primitives::Bytecode}; -use std::cmp::Ordering; +use std::{cmp::Ordering, collections::VecDeque}; /// Mocked call data. #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] @@ -65,6 +65,25 @@ impl Cheatcode for mockCall_1Call { } } +impl Cheatcode for mockCalls_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { callee, data, returnData } = self; + let _ = make_acc_non_empty(callee, ccx.ecx)?; + + mock_calls(ccx.state, callee, data, None, returnData, InstructionResult::Return); + Ok(Default::default()) + } +} + +impl Cheatcode for mockCalls_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { callee, msgValue, data, returnData } = self; + ccx.ecx.load_account(*callee)?; + mock_calls(ccx.state, callee, data, Some(msgValue), returnData, InstructionResult::Return); + Ok(Default::default()) + } +} + impl Cheatcode for mockCallRevert_0Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { callee, data, revertData } = self; @@ -94,7 +113,6 @@ impl Cheatcode for mockFunctionCall { } } -#[allow(clippy::ptr_arg)] // Not public API, doesn't matter fn mock_call( state: &mut Cheatcodes, callee: &Address, @@ -102,10 +120,24 @@ fn mock_call( value: Option<&U256>, rdata: &Bytes, ret_type: InstructionResult, +) { + mock_calls(state, callee, cdata, value, std::slice::from_ref(rdata), ret_type) +} + +fn mock_calls( + state: &mut Cheatcodes, + callee: &Address, + cdata: &Bytes, + value: Option<&U256>, + rdata_vec: &[Bytes], + ret_type: InstructionResult, ) { state.mocked_calls.entry(*callee).or_default().insert( MockCallDataContext { calldata: Bytes::copy_from_slice(cdata), value: value.copied() }, - MockCallReturnData { ret_type, data: Bytes::copy_from_slice(rdata) }, + rdata_vec + .iter() + .map(|rdata| MockCallReturnData { ret_type, data: rdata.clone() }) + .collect::>(), ); } diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index fe8834d5f..bdcb1ad8d 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -402,7 +402,7 @@ pub struct Cheatcodes { /// Mocked calls // **Note**: inner must a BTreeMap because of special `Ord` impl for `MockCallDataContext` - pub mocked_calls: HashMap>, + pub mocked_calls: HashMap>>, /// Mocked functions. Maps target address to be mocked to pair of (calldata, mock address). pub mocked_functions: HashMap>, @@ -889,26 +889,36 @@ where { } // Handle mocked calls - if let Some(mocks) = self.mocked_calls.get(&call.bytecode_address) { + if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) { let ctx = MockCallDataContext { calldata: call.input.clone(), value: call.transfer_value() }; - if let Some(return_data) = mocks.get(&ctx).or_else(|| { - mocks - .iter() + + if let Some(return_data_queue) = match mocks.get_mut(&ctx) { + Some(queue) => Some(queue), + None => mocks + .iter_mut() .find(|(mock, _)| { call.input.get(..mock.calldata.len()) == Some(&mock.calldata[..]) && mock.value.map_or(true, |value| Some(value) == call.transfer_value()) }) - .map(|(_, v)| v) - }) { - return Some(CallOutcome { - result: InterpreterResult { - result: return_data.ret_type, - output: return_data.data.clone(), - gas, - }, - memory_offset: call.return_memory_offset.clone(), - }); + .map(|(_, v)| v), + } { + if let Some(return_data) = if return_data_queue.len() == 1 { + // If the mocked calls stack has a single element in it, don't empty it + return_data_queue.front().map(|x| x.to_owned()) + } else { + // Else, we pop the front element + return_data_queue.pop_front() + } { + return Some(CallOutcome { + result: InterpreterResult { + result: return_data.ret_type, + output: return_data.data, + gas, + }, + memory_offset: call.return_memory_offset.clone(), + }); + } } } diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 64c599a9e..8bb13577e 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -284,6 +284,8 @@ 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 mockCalls(address callee, bytes calldata data, bytes[] calldata returnData) external; + function mockCalls(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); diff --git a/testdata/default/cheats/MockCalls.t.sol b/testdata/default/cheats/MockCalls.t.sol new file mode 100644 index 000000000..13e3cb78c --- /dev/null +++ b/testdata/default/cheats/MockCalls.t.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract MockCallsTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function testMockCallsLastShouldPersist() public { + address mockUser = vm.addr(vm.randomUint()); + address mockErc20 = vm.addr(vm.randomUint()); + bytes memory data = abi.encodeWithSignature("balanceOf(address)", mockUser); + bytes[] memory mocks = new bytes[](2); + mocks[0] = abi.encode(2 ether); + mocks[1] = abi.encode(7.219 ether); + vm.mockCalls(mockErc20, data, mocks); + (, bytes memory ret1) = mockErc20.call(data); + assertEq(abi.decode(ret1, (uint256)), 2 ether); + (, bytes memory ret2) = mockErc20.call(data); + assertEq(abi.decode(ret2, (uint256)), 7.219 ether); + (, bytes memory ret3) = mockErc20.call(data); + assertEq(abi.decode(ret3, (uint256)), 7.219 ether); + } + + function testMockCallsWithValue() public { + address mockUser = vm.addr(vm.randomUint()); + address mockErc20 = vm.addr(vm.randomUint()); + bytes memory data = abi.encodeWithSignature("balanceOf(address)", mockUser); + bytes[] memory mocks = new bytes[](3); + mocks[0] = abi.encode(2 ether); + mocks[1] = abi.encode(1 ether); + mocks[2] = abi.encode(6.423 ether); + vm.mockCalls(mockErc20, 1 ether, data, mocks); + (, bytes memory ret1) = mockErc20.call{value: 1 ether}(data); + assertEq(abi.decode(ret1, (uint256)), 2 ether); + (, bytes memory ret2) = mockErc20.call{value: 1 ether}(data); + assertEq(abi.decode(ret2, (uint256)), 1 ether); + (, bytes memory ret3) = mockErc20.call{value: 1 ether}(data); + assertEq(abi.decode(ret3, (uint256)), 6.423 ether); + } + + function testMockCalls() public { + address mockUser = vm.addr(vm.randomUint()); + address mockErc20 = vm.addr(vm.randomUint()); + bytes memory data = abi.encodeWithSignature("balanceOf(address)", mockUser); + bytes[] memory mocks = new bytes[](3); + mocks[0] = abi.encode(2 ether); + mocks[1] = abi.encode(1 ether); + mocks[2] = abi.encode(6.423 ether); + vm.mockCalls(mockErc20, data, mocks); + (, bytes memory ret1) = mockErc20.call(data); + assertEq(abi.decode(ret1, (uint256)), 2 ether); + (, bytes memory ret2) = mockErc20.call(data); + assertEq(abi.decode(ret2, (uint256)), 1 ether); + (, bytes memory ret3) = mockErc20.call(data); + assertEq(abi.decode(ret3, (uint256)), 6.423 ether); + } +} From 3b2e99973badc1d1597e37ae37dceb398fef4f8f Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 7 Oct 2024 16:34:28 +0200 Subject: [PATCH 068/100] chore(deps): bump alloy-core 0.8.6 (#9045) --- Cargo.lock | 60 ++++++++++--------- Cargo.toml | 3 +- .../anvil/src/eth/backend/mem/in_memory_db.rs | 7 +-- crates/anvil/src/eth/backend/mem/storage.rs | 3 +- crates/anvil/src/eth/pool/transactions.rs | 14 ++--- crates/cheatcodes/Cargo.toml | 1 - crates/cheatcodes/src/evm.rs | 7 +-- crates/cheatcodes/src/fs.rs | 3 +- crates/cheatcodes/src/inspector.rs | 3 +- crates/cheatcodes/src/test/expect.rs | 7 ++- crates/common/Cargo.toml | 1 - crates/common/src/evm.rs | 8 +-- crates/common/src/selectors.rs | 3 +- crates/evm/abi/Cargo.toml | 1 - crates/evm/abi/src/console/hardhat.rs | 7 +-- crates/evm/core/Cargo.toml | 1 - crates/evm/core/src/backend/mod.rs | 3 +- crates/evm/core/src/decode.rs | 5 +- crates/evm/core/src/fork/database.rs | 3 +- crates/evm/core/src/ic.rs | 10 ++-- crates/evm/coverage/Cargo.toml | 1 - crates/evm/coverage/src/analysis.rs | 4 +- crates/evm/coverage/src/anchors.rs | 6 +- crates/evm/coverage/src/lib.rs | 4 +- crates/evm/evm/src/lib.rs | 6 -- crates/evm/fuzz/src/strategies/state.rs | 8 +-- crates/evm/traces/Cargo.toml | 1 - crates/evm/traces/src/debug/sources.rs | 3 +- crates/evm/traces/src/decoder/mod.rs | 18 +++--- .../evm/traces/src/identifier/signatures.rs | 11 +--- crates/forge/Cargo.toml | 1 - crates/forge/bin/cmd/clone.rs | 3 +- crates/forge/bin/cmd/coverage.rs | 10 ++-- crates/forge/src/coverage.rs | 6 +- crates/forge/src/gas_report.rs | 6 +- crates/forge/src/runner.rs | 9 +-- crates/script/src/providers.rs | 7 +-- crates/script/src/simulate.rs | 4 +- crates/test-utils/src/rpc.rs | 3 +- crates/verify/src/etherscan/mod.rs | 3 +- crates/verify/src/sourcify.rs | 4 +- crates/wallets/src/wallet.rs | 3 +- deny.toml | 1 + 43 files changed, 110 insertions(+), 162 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 80fbb6212..d94ee063d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -136,9 +136,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b499852e1d0e9b8c6db0f24c48998e647c0d5762a01090f955106a7700e4611" +checksum = "1109c57718022ac84c194f775977a534e1b3969b405e55693a61c42187cc0612" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -231,9 +231,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a438d4486b5d525df3b3004188f9d5cd1d65cd30ecc41e5a3ccef6f6342e8af9" +checksum = "c4cc0e59c803dd44d14fc0cfa9fea1f74cfa8fd9fb60ca303ced390c58c28d4e" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -303,9 +303,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260d3ff3bff0bb84599f032a2f2c6828180b0ea0cd41fdaf44f39cef3ba41861" +checksum = "a289ffd7448036f2f436b377f981c79ce0b2090877bad938d43387dc09931877" dependencies = [ "alloy-rlp", "arbitrary", @@ -314,8 +314,9 @@ dependencies = [ "const-hex", "derive_arbitrary", "derive_more 1.0.0", + "foldhash", "getrandom", - "hashbrown 0.14.5", + "hashbrown 0.15.0", "hex-literal", "indexmap 2.6.0", "itoa", @@ -680,9 +681,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68e7f6e8fe5b443f82b3f1e15abfa191128f71569148428e49449d01f6f49e8b" +checksum = "0409e3ba5d1de409997a7db8b8e9d679d52088c1dee042a85033affd3cadeab4" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -694,9 +695,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b96ce28d2fde09abb6135f410c41fad670a3a770b6776869bd852f1df102e6f" +checksum = "a18372ef450d59f74c7a64a738f546ba82c92f816597fed1802ef559304c81f1" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -713,9 +714,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "906746396a8296537745711630d9185746c0b50c033d5e9d18b0a6eba3d53f90" +checksum = "f7bad89dd0d5f109e8feeaf787a9ed7a05a91a9a0efc6687d147a70ebca8eff7" dependencies = [ "alloy-json-abi", "const-hex", @@ -730,9 +731,9 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc85178909a49c8827ffccfc9103a7ce1767ae66a801b69bdc326913870bf8e6" +checksum = "dbd3548d5262867c2c4be6223fe4f2583e21ade0ca1c307fd23bc7f28fca479e" dependencies = [ "serde", "winnow", @@ -740,9 +741,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86a533ce22525969661b25dfe296c112d35eb6861f188fd284f8bd4bb3842ae" +checksum = "4aa666f1036341b46625e72bd36878bf45ad0185f1b88601223e1ec6ed4b72b1" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -1994,9 +1995,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.25" +version = "1.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d9e0b4957f635b8d3da819d0db5603620467ecf1f692d22a8c2717ce27e6d8" +checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1" dependencies = [ "shlex", ] @@ -3305,6 +3306,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + [[package]] name = "foreign-types" version = "0.3.2" @@ -3386,7 +3393,6 @@ dependencies = [ "regex", "reqwest", "revm-inspectors", - "rustc-hash", "semver 1.0.23", "serde", "serde_json", @@ -3666,7 +3672,6 @@ dependencies = [ "proptest", "rand", "revm", - "rustc-hash", "semver 1.0.23", "serde_json", "thiserror", @@ -3754,7 +3759,6 @@ dependencies = [ "foundry-macros", "num-format", "reqwest", - "rustc-hash", "semver 1.0.23", "serde", "serde_json", @@ -3987,7 +3991,6 @@ dependencies = [ "foundry-macros", "foundry-test-utils", "itertools 0.13.0", - "rustc-hash", ] [[package]] @@ -4016,7 +4019,6 @@ dependencies = [ "parking_lot", "revm", "revm-inspectors", - "rustc-hash", "serde", "serde_json", "thiserror", @@ -4035,7 +4037,6 @@ dependencies = [ "foundry-evm-core", "rayon", "revm", - "rustc-hash", "semver 1.0.23", "tracing", ] @@ -4086,7 +4087,6 @@ dependencies = [ "rayon", "revm", "revm-inspectors", - "rustc-hash", "serde", "solang-parser", "tempfile", @@ -4740,6 +4740,10 @@ name = "hashbrown" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +dependencies = [ + "foldhash", + "serde", +] [[package]] name = "heck" @@ -8411,9 +8415,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab661c8148c2261222a4d641ad5477fd4bea79406a99056096a0b41b35617a5" +checksum = "f3a850d65181df41b83c6be01a7d91f5e9377c43d48faa5af7d95816f437f5a3" dependencies = [ "paste", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index e6ccd75bd..bbefa8c4b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -208,7 +208,7 @@ alloy-json-abi = "0.8.5" alloy-primitives = { version = "0.8.5", features = [ "getrandom", "rand", - "map-fxhash", + "map-foldhash", ] } alloy-sol-macro-expander = "0.8.5" alloy-sol-macro-input = "0.8.5" @@ -249,7 +249,6 @@ k256 = "0.13" parking_lot = "0.12" mesc = "0.3" rand = "0.8" -rustc-hash = "2.0" semver = "1" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["arbitrary_precision"] } 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 01243c90f..9e34448ad 100644 --- a/crates/anvil/src/eth/backend/mem/in_memory_db.rs +++ b/crates/anvil/src/eth/backend/mem/in_memory_db.rs @@ -8,12 +8,9 @@ use crate::{ mem::state::state_root, revm::{db::DbAccount, primitives::AccountInfo}, }; -use alloy_primitives::{Address, B256, U256, U64}; +use alloy_primitives::{map::HashMap, Address, B256, U256, U64}; use alloy_rpc_types::BlockId; -use foundry_evm::{ - backend::{BlockchainDb, DatabaseResult, StateSnapshot}, - hashbrown::HashMap, -}; +use foundry_evm::backend::{BlockchainDb, DatabaseResult, StateSnapshot}; // reexport for convenience pub use foundry_evm::{backend::MemDb, revm::db::DatabaseRef}; diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 29ae47207..942ce5a9d 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -581,9 +581,8 @@ pub struct MinedTransactionReceipt { } #[cfg(test)] +#[allow(clippy::needless_return)] mod tests { - #![allow(clippy::needless_return)] - use super::*; use crate::eth::backend::db::Db; use alloy_primitives::{hex, Address}; diff --git a/crates/anvil/src/eth/pool/transactions.rs b/crates/anvil/src/eth/pool/transactions.rs index a1b7beb68..631064549 100644 --- a/crates/anvil/src/eth/pool/transactions.rs +++ b/crates/anvil/src/eth/pool/transactions.rs @@ -1,17 +1,13 @@ use crate::eth::{error::PoolError, util::hex_fmt_many}; -use alloy_primitives::{Address, TxHash}; +use alloy_primitives::{ + map::{HashMap, HashSet}, + Address, TxHash, +}; use alloy_rpc_types::Transaction as RpcTransaction; use alloy_serde::WithOtherFields; use anvil_core::eth::transaction::{PendingTransaction, TypedTransaction}; use parking_lot::RwLock; -use std::{ - cmp::Ordering, - collections::{BTreeSet, HashMap, HashSet}, - fmt, - str::FromStr, - sync::Arc, - time::Instant, -}; +use std::{cmp::Ordering, collections::BTreeSet, fmt, str::FromStr, sync::Arc, time::Instant}; /// A unique identifying marker for a transaction pub type TxMarker = Vec; diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index cbf66d684..7f990a8e5 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -58,7 +58,6 @@ p256 = "0.13.2" ecdsa = "0.16" rand = "0.8" revm.workspace = true -rustc-hash.workspace = true semver.workspace = true serde_json.workspace = true thiserror.workspace = true diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index af2026a9e..7691cd4c9 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -6,7 +6,7 @@ use crate::{ }; use alloy_consensus::TxEnvelope; use alloy_genesis::{Genesis, GenesisAccount}; -use alloy_primitives::{Address, Bytes, B256, U256}; +use alloy_primitives::{map::HashMap, Address, Bytes, B256, U256}; use alloy_rlp::Decodable; use alloy_sol_types::SolValue; use foundry_common::fs::{read_json_file, write_json_file}; @@ -16,10 +16,7 @@ use foundry_evm_core::{ }; use rand::Rng; use revm::primitives::{Account, Bytecode, SpecId, KECCAK_EMPTY}; -use std::{ - collections::{BTreeMap, HashMap}, - path::Path, -}; +use std::{collections::BTreeMap, path::Path}; mod fork; pub(crate) mod mapping; diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index f36c8d6fe..c8c512b6f 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -4,7 +4,7 @@ use super::string::parse; use crate::{Cheatcode, Cheatcodes, CheatcodesExecutor, CheatsCtxt, Result, Vm::*}; use alloy_dyn_abi::DynSolType; use alloy_json_abi::ContractObject; -use alloy_primitives::{hex, Bytes, U256}; +use alloy_primitives::{hex, map::Entry, Bytes, U256}; use alloy_sol_types::SolValue; use dialoguer::{Input, Password}; use foundry_common::fs; @@ -12,7 +12,6 @@ use foundry_config::fs_permissions::FsAccessKind; use revm::interpreter::CreateInputs; use semver::Version; use std::{ - collections::hash_map::Entry, io::{BufRead, BufReader, Write}, path::{Path, PathBuf}, process::Command, diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index bdcb1ad8d..9a40c3cb1 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -49,7 +49,6 @@ use revm::{ primitives::{BlockEnv, CreateScheme, EVMError, EvmStorageSlot, SpecId, EOF_MAGIC_BYTES}, EvmContext, InnerEvmContext, Inspector, }; -use rustc_hash::FxHashMap; use serde_json::Value; use std::{ collections::{BTreeMap, VecDeque}, @@ -413,7 +412,7 @@ pub struct Cheatcodes { pub expected_emits: VecDeque, /// Map of context depths to memory offset ranges that may be written to within the call depth. - pub allowed_mem_writes: FxHashMap>>, + pub allowed_mem_writes: HashMap>>, /// Current broadcasting information pub broadcast: Option, diff --git a/crates/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs index 7a58c7ab8..602ac5125 100644 --- a/crates/cheatcodes/src/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -1,5 +1,9 @@ use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Error, Result, Vm::*}; -use alloy_primitives::{address, hex, Address, Bytes, LogData as RawLog, U256}; +use alloy_primitives::{ + address, hex, + map::{hash_map::Entry, HashMap}, + Address, Bytes, LogData as RawLog, U256, +}; use alloy_sol_types::{SolError, SolValue}; use foundry_common::ContractsByArtifact; use foundry_evm_core::decode::RevertDecoder; @@ -7,7 +11,6 @@ use revm::interpreter::{ return_ok, InstructionResult, Interpreter, InterpreterAction, InterpreterResult, }; use spec::Vm; -use std::collections::{hash_map::Entry, HashMap}; /// For some cheatcodes we may internally change the status of the call, i.e. in `expectRevert`. /// Solidity will see a successful call and attempt to decode the return data. Therefore, we need diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 73bd90cc7..fbc5c82ca 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -52,7 +52,6 @@ dunce.workspace = true eyre.workspace = true num-format.workspace = true reqwest.workspace = true -rustc-hash.workspace = true semver.workspace = true serde_json.workspace = true serde.workspace = true diff --git a/crates/common/src/evm.rs b/crates/common/src/evm.rs index d281d5652..30bcd4d09 100644 --- a/crates/common/src/evm.rs +++ b/crates/common/src/evm.rs @@ -1,5 +1,6 @@ -//! cli arguments for configuring the evm settings -use alloy_primitives::{Address, B256, U256}; +//! CLI arguments for configuring the EVM settings. + +use alloy_primitives::{map::HashMap, Address, B256, U256}; use clap::{ArgAction, Parser}; use eyre::ContextCompat; use foundry_config::{ @@ -11,11 +12,10 @@ use foundry_config::{ }, Chain, Config, }; -use rustc_hash::FxHashMap; use serde::Serialize; /// Map keyed by breakpoints char to their location (contract address, pc) -pub type Breakpoints = FxHashMap; +pub type Breakpoints = HashMap; /// `EvmArgs` and `EnvArgs` take the highest precedence in the Config/Figment hierarchy. /// diff --git a/crates/common/src/selectors.rs b/crates/common/src/selectors.rs index 3b1073916..23a272a2a 100644 --- a/crates/common/src/selectors.rs +++ b/crates/common/src/selectors.rs @@ -579,9 +579,8 @@ pub fn parse_signatures(tokens: Vec) -> ParsedSignatures { } #[cfg(test)] +#[allow(clippy::needless_return)] mod tests { - #![allow(clippy::needless_return)] - use super::*; #[tokio::test(flavor = "multi_thread")] diff --git a/crates/evm/abi/Cargo.toml b/crates/evm/abi/Cargo.toml index 330ea6626..b3202c967 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 -rustc-hash.workspace = true [dev-dependencies] foundry-test-utils.workspace = true diff --git a/crates/evm/abi/src/console/hardhat.rs b/crates/evm/abi/src/console/hardhat.rs index a99ad8fc6..8154c9ff7 100644 --- a/crates/evm/abi/src/console/hardhat.rs +++ b/crates/evm/abi/src/console/hardhat.rs @@ -1,8 +1,7 @@ -use alloy_primitives::Selector; +use alloy_primitives::{map::HashMap, Selector}; use alloy_sol_types::sol; use foundry_common_fmt::*; use foundry_macros::ConsoleFmt; -use rustc_hash::FxHashMap; use std::sync::LazyLock; sol!( @@ -39,8 +38,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: LazyLock> = - LazyLock::new(|| FxHashMap::from_iter(include!("./patches.rs"))); +pub static HARDHAT_CONSOLE_SELECTOR_PATCHES: LazyLock> = + LazyLock::new(|| HashMap::from_iter(include!("./patches.rs"))); #[cfg(test)] mod tests { diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index ce06dc9d0..464412016 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -54,7 +54,6 @@ eyre.workspace = true futures.workspace = true itertools.workspace = true parking_lot.workspace = true -rustc-hash.workspace = true serde.workspace = true serde_json.workspace = true thiserror.workspace = true diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 8cf59d96e..27c2d6944 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -1960,9 +1960,8 @@ fn apply_state_changeset( } #[cfg(test)] +#[allow(clippy::needless_return)] mod tests { - #![allow(clippy::needless_return)] - use crate::{backend::Backend, fork::CreateFork, opts::EvmOpts}; use alloy_primitives::{Address, U256}; use alloy_provider::Provider; diff --git a/crates/evm/core/src/decode.rs b/crates/evm/core/src/decode.rs index 95fcaf02a..d6e8b3dec 100644 --- a/crates/evm/core/src/decode.rs +++ b/crates/evm/core/src/decode.rs @@ -3,12 +3,11 @@ use crate::abi::{Console, Vm}; use alloy_dyn_abi::JsonAbiExt; use alloy_json_abi::{Error, JsonAbi}; -use alloy_primitives::{hex, Log, Selector}; +use alloy_primitives::{hex, map::HashMap, Log, Selector}; use alloy_sol_types::{SolEventInterface, SolInterface, SolValue}; use foundry_common::SELECTOR_LEN; use itertools::Itertools; use revm::interpreter::InstructionResult; -use rustc_hash::FxHashMap; use std::{fmt, sync::OnceLock}; /// A skip reason. @@ -60,7 +59,7 @@ pub fn decode_console_log(log: &Log) -> Option { #[derive(Clone, Debug, Default)] pub struct RevertDecoder { /// The custom errors to use for decoding. - pub errors: FxHashMap>, + pub errors: HashMap>, } impl Default for &RevertDecoder { diff --git a/crates/evm/core/src/fork/database.rs b/crates/evm/core/src/fork/database.rs index 1bab76515..29dccb9c7 100644 --- a/crates/evm/core/src/fork/database.rs +++ b/crates/evm/core/src/fork/database.rs @@ -262,9 +262,8 @@ impl DatabaseRef for ForkDbStateSnapshot { } #[cfg(test)] +#[allow(clippy::needless_return)] mod tests { - #![allow(clippy::needless_return)] - use super::*; use crate::backend::BlockchainDbMeta; use foundry_common::provider::get_http_provider; diff --git a/crates/evm/core/src/ic.rs b/crates/evm/core/src/ic.rs index acb9cc50e..f7ab3093c 100644 --- a/crates/evm/core/src/ic.rs +++ b/crates/evm/core/src/ic.rs @@ -1,12 +1,12 @@ +use alloy_primitives::map::HashMap; use revm::interpreter::opcode::{PUSH0, PUSH1, PUSH32}; -use rustc_hash::FxHashMap; /// Maps from program counter to instruction counter. /// /// Inverse of [`IcPcMap`]. #[derive(Debug, Clone)] pub struct PcIcMap { - pub inner: FxHashMap, + pub inner: HashMap, } impl PcIcMap { @@ -35,7 +35,7 @@ impl PcIcMap { /// /// Inverse of [`PcIcMap`]. pub struct IcPcMap { - pub inner: FxHashMap, + pub inner: HashMap, } impl IcPcMap { @@ -60,8 +60,8 @@ impl IcPcMap { } } -fn make_map(code: &[u8]) -> FxHashMap { - let mut map = FxHashMap::default(); +fn make_map(code: &[u8]) -> HashMap { + let mut map = HashMap::default(); let mut pc = 0; let mut cumulative_push_size = 0; diff --git a/crates/evm/coverage/Cargo.toml b/crates/evm/coverage/Cargo.toml index 777c37d99..e38d33e2a 100644 --- a/crates/evm/coverage/Cargo.toml +++ b/crates/evm/coverage/Cargo.toml @@ -23,5 +23,4 @@ eyre.workspace = true revm.workspace = true semver.workspace = true tracing.workspace = true -rustc-hash.workspace = true rayon.workspace = true diff --git a/crates/evm/coverage/src/analysis.rs b/crates/evm/coverage/src/analysis.rs index 1b68c89d3..e5eda5262 100644 --- a/crates/evm/coverage/src/analysis.rs +++ b/crates/evm/coverage/src/analysis.rs @@ -1,8 +1,8 @@ use super::{CoverageItem, CoverageItemKind, SourceLocation}; +use alloy_primitives::map::HashMap; use foundry_common::TestFunctionExt; use foundry_compilers::artifacts::ast::{self, Ast, Node, NodeType}; use rayon::prelude::*; -use rustc_hash::FxHashMap; use std::sync::Arc; /// A visitor that walks the AST of a single contract and finds coverage items. @@ -583,7 +583,7 @@ impl<'a> SourceAnalyzer<'a> { #[derive(Debug, Default)] pub struct SourceFiles<'a> { /// The versioned sources. - pub sources: FxHashMap>, + pub sources: HashMap>, } /// The source code and AST of a file. diff --git a/crates/evm/coverage/src/anchors.rs b/crates/evm/coverage/src/anchors.rs index c5bb8196a..f42fd1a62 100644 --- a/crates/evm/coverage/src/anchors.rs +++ b/crates/evm/coverage/src/anchors.rs @@ -1,9 +1,9 @@ use super::{CoverageItem, CoverageItemKind, ItemAnchor, SourceLocation}; +use alloy_primitives::map::{DefaultHashBuilder, HashMap, HashSet}; use eyre::ensure; use foundry_compilers::artifacts::sourcemap::{SourceElement, SourceMap}; use foundry_evm_core::utils::IcPcMap; use revm::interpreter::opcode; -use rustc_hash::{FxHashMap, FxHashSet}; /// Attempts to find anchors for the given items using the given source map and bytecode. pub fn find_anchors( @@ -11,9 +11,9 @@ pub fn find_anchors( source_map: &SourceMap, ic_pc_map: &IcPcMap, items: &[CoverageItem], - items_by_source_id: &FxHashMap>, + items_by_source_id: &HashMap>, ) -> Vec { - let mut seen = FxHashSet::default(); + let mut seen = HashSet::with_hasher(DefaultHashBuilder::default()); source_map .iter() .filter_map(|element| items_by_source_id.get(&(element.index()? as usize))) diff --git a/crates/evm/coverage/src/lib.rs b/crates/evm/coverage/src/lib.rs index 4481813ac..220ab6b41 100644 --- a/crates/evm/coverage/src/lib.rs +++ b/crates/evm/coverage/src/lib.rs @@ -8,12 +8,12 @@ #[macro_use] extern crate tracing; -use alloy_primitives::{Bytes, B256}; +use alloy_primitives::{map::HashMap, Bytes, B256}; use eyre::{Context, Result}; use foundry_compilers::artifacts::sourcemap::SourceMap; use semver::Version; use std::{ - collections::{BTreeMap, HashMap}, + collections::BTreeMap, fmt::Display, ops::{AddAssign, Deref, DerefMut}, path::{Path, PathBuf}, diff --git a/crates/evm/evm/src/lib.rs b/crates/evm/evm/src/lib.rs index 8bbd7f141..15858c0f3 100644 --- a/crates/evm/evm/src/lib.rs +++ b/crates/evm/evm/src/lib.rs @@ -19,9 +19,3 @@ pub use foundry_evm_traces as traces; // TODO: We should probably remove these, but it's a pretty big breaking change. #[doc(hidden)] pub use revm; - -#[doc(hidden)] -#[deprecated = "use `{hash_map, hash_set, HashMap, HashSet}` in `std::collections` or `revm::primitives` instead"] -pub mod hashbrown { - pub use revm::primitives::{hash_map, hash_set, HashMap, HashSet}; -} diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index f4f1dde92..4df55772c 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -1,7 +1,7 @@ use crate::invariant::{BasicTxDetails, FuzzRunIdentifiedContracts}; use alloy_dyn_abi::{DynSolType, DynSolValue, EventExt, FunctionExt}; use alloy_json_abi::{Function, JsonAbi}; -use alloy_primitives::{Address, Bytes, Log, B256, U256}; +use alloy_primitives::{map::HashMap, Address, Bytes, Log, B256, U256}; use foundry_config::FuzzDictionaryConfig; use foundry_evm_core::utils::StateChangeset; use indexmap::IndexSet; @@ -11,11 +11,7 @@ use revm::{ interpreter::opcode, primitives::AccountInfo, }; -use std::{ - collections::{BTreeMap, HashMap}, - fmt, - sync::Arc, -}; +use std::{collections::BTreeMap, fmt, sync::Arc}; type AIndexSet = IndexSet>; diff --git a/crates/evm/traces/Cargo.toml b/crates/evm/traces/Cargo.toml index 96fb8d1c6..3423213fb 100644 --- a/crates/evm/traces/Cargo.toml +++ b/crates/evm/traces/Cargo.toml @@ -38,7 +38,6 @@ itertools.workspace = true serde.workspace = true tokio = { workspace = true, features = ["time", "macros"] } tracing.workspace = true -rustc-hash.workspace = true tempfile.workspace = true rayon.workspace = true solang-parser.workspace = true diff --git a/crates/evm/traces/src/debug/sources.rs b/crates/evm/traces/src/debug/sources.rs index 6ad335302..40e540a97 100644 --- a/crates/evm/traces/src/debug/sources.rs +++ b/crates/evm/traces/src/debug/sources.rs @@ -11,7 +11,6 @@ use foundry_compilers::{ use foundry_evm_core::utils::PcIcMap; use foundry_linking::Linker; use rayon::prelude::*; -use rustc_hash::FxHashMap; use solang_parser::pt::SourceUnitPart; use std::{ collections::{BTreeMap, HashMap}, @@ -117,7 +116,7 @@ impl ArtifactData { #[derive(Clone, Debug, Default)] pub struct ContractSources { /// Map over build_id -> file_id -> (source code, language) - pub sources_by_id: HashMap>>, + pub sources_by_id: HashMap>>, /// Map over contract name -> Vec<(bytecode, build_id, file_id)> pub artifacts_by_name: HashMap>, } diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index 1bb20381b..25d9f4f2b 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -7,7 +7,10 @@ use crate::{ }; use alloy_dyn_abi::{DecodedEvent, DynSolValue, EventExt, FunctionExt, JsonAbiExt}; use alloy_json_abi::{Error, Event, Function, JsonAbi}; -use alloy_primitives::{Address, LogData, Selector, B256}; +use alloy_primitives::{ + map::{hash_map::Entry, HashMap}, + Address, LogData, Selector, B256, +}; use foundry_common::{ abi::get_indexed_event, fmt::format_token, get_contract_name, ContractsByArtifact, SELECTOR_LEN, }; @@ -25,11 +28,7 @@ use foundry_evm_core::{ }; use itertools::Itertools; use revm_inspectors::tracing::types::{DecodedCallLog, DecodedCallTrace}; -use rustc_hash::FxHashMap; -use std::{ - collections::{hash_map::Entry, BTreeMap, HashMap}, - sync::OnceLock, -}; +use std::{collections::BTreeMap, sync::OnceLock}; mod precompiles; @@ -124,7 +123,7 @@ pub struct CallTraceDecoder { pub receive_contracts: Vec
, /// All known functions. - pub functions: FxHashMap>, + pub functions: HashMap>, /// All known events. pub events: BTreeMap<(B256, usize), Vec>, /// Revert decoder. Contains all known custom errors. @@ -171,7 +170,7 @@ impl CallTraceDecoder { Self { contracts: Default::default(), - labels: [ + labels: HashMap::from_iter([ (CHEATCODE_ADDRESS, "VM".to_string()), (HARDHAT_CONSOLE_ADDRESS, "console".to_string()), (DEFAULT_CREATE2_DEPLOYER, "Create2Deployer".to_string()), @@ -187,8 +186,7 @@ impl CallTraceDecoder { (EC_PAIRING, "ECPairing".to_string()), (BLAKE_2F, "Blake2F".to_string()), (POINT_EVALUATION, "PointEvaluation".to_string()), - ] - .into(), + ]), receive_contracts: Default::default(), functions: hh_funcs() diff --git a/crates/evm/traces/src/identifier/signatures.rs b/crates/evm/traces/src/identifier/signatures.rs index 2d7cadad1..1e3924aa3 100644 --- a/crates/evm/traces/src/identifier/signatures.rs +++ b/crates/evm/traces/src/identifier/signatures.rs @@ -1,16 +1,12 @@ use alloy_json_abi::{Event, Function}; -use alloy_primitives::hex; +use alloy_primitives::{hex, map::HashSet}; use foundry_common::{ abi::{get_event, get_func}, fs, selectors::{OpenChainClient, SelectorType}, }; use serde::{Deserialize, Serialize}; -use std::{ - collections::{BTreeMap, HashSet}, - path::PathBuf, - sync::Arc, -}; +use std::{collections::BTreeMap, path::PathBuf, sync::Arc}; use tokio::sync::RwLock; pub type SingleSignaturesIdentifier = Arc>; @@ -161,9 +157,8 @@ impl Drop for SignaturesIdentifier { } #[cfg(test)] +#[allow(clippy::needless_return)] mod tests { - #![allow(clippy::needless_return)] - use super::*; #[tokio::test(flavor = "multi_thread")] diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index f636e4c62..2cd660785 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -98,7 +98,6 @@ watchexec-events = "3.0" watchexec-signals = "3.0" clearscreen = "3.0" evm-disassembler.workspace = true -rustc-hash.workspace = true # doc server axum = { workspace = true, features = ["ws"] } diff --git a/crates/forge/bin/cmd/clone.rs b/crates/forge/bin/cmd/clone.rs index f3879b931..1c9ee47ec 100644 --- a/crates/forge/bin/cmd/clone.rs +++ b/crates/forge/bin/cmd/clone.rs @@ -608,9 +608,8 @@ impl EtherscanClient for Client { } #[cfg(test)] +#[allow(clippy::needless_return)] mod tests { - #![allow(clippy::needless_return)] - use super::*; use alloy_primitives::hex; use foundry_compilers::Artifact; diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index 487c0a7f1..e21153d09 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -1,5 +1,5 @@ use super::{install, test::TestArgs}; -use alloy_primitives::{Address, Bytes, U256}; +use alloy_primitives::{map::HashMap, Address, Bytes, U256}; use clap::{Parser, ValueEnum, ValueHint}; use eyre::{Context, Result}; use forge::{ @@ -24,10 +24,8 @@ use foundry_compilers::{ }; use foundry_config::{Config, SolcReq}; use rayon::prelude::*; -use rustc_hash::FxHashMap; use semver::Version; use std::{ - collections::HashMap, path::{Path, PathBuf}, sync::Arc, }; @@ -150,7 +148,7 @@ impl CoverageArgs { // Collect source files. let project_paths = &project.paths; - let mut versioned_sources = HashMap::>::new(); + let mut versioned_sources = HashMap::>::default(); for (path, source_file, version) in output.output().sources.sources_with_version() { report.add_source(version.clone(), source_file.id as usize, path.clone()); @@ -191,7 +189,7 @@ impl CoverageArgs { let source_analysis = SourceAnalyzer::new(sources).analyze()?; // Build helper mapping used by `find_anchors` - let mut items_by_source_id = FxHashMap::<_, Vec<_>>::with_capacity_and_hasher( + let mut items_by_source_id = HashMap::<_, Vec<_>>::with_capacity_and_hasher( source_analysis.items.len(), Default::default(), ); @@ -410,7 +408,7 @@ impl BytecodeData { pub fn find_anchors( &self, source_analysis: &SourceAnalysis, - items_by_source_id: &FxHashMap>, + items_by_source_id: &HashMap>, ) -> Vec { find_anchors( &self.bytecode, diff --git a/crates/forge/src/coverage.rs b/crates/forge/src/coverage.rs index ef9c7b0c6..4a00675dd 100644 --- a/crates/forge/src/coverage.rs +++ b/crates/forge/src/coverage.rs @@ -1,15 +1,17 @@ //! Coverage reports. +use alloy_primitives::map::HashMap; use comfy_table::{presets::ASCII_MARKDOWN, Attribute, Cell, Color, Row, Table}; use evm_disassembler::disassemble_bytes; use foundry_common::fs; -pub use foundry_evm::coverage::*; use std::{ - collections::{hash_map, HashMap}, + collections::hash_map, io::Write, path::{Path, PathBuf}, }; +pub use foundry_evm::coverage::*; + /// A coverage reporter. pub trait CoverageReporter { fn report(self, report: &CoverageReport) -> eyre::Result<()>; diff --git a/crates/forge/src/gas_report.rs b/crates/forge/src/gas_report.rs index 85f53b5f5..ff7628bd5 100644 --- a/crates/forge/src/gas_report.rs +++ b/crates/forge/src/gas_report.rs @@ -4,14 +4,12 @@ use crate::{ constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS}, traces::{CallTraceArena, CallTraceDecoder, CallTraceNode, DecodedCallData}, }; +use alloy_primitives::map::HashSet; use comfy_table::{presets::ASCII_MARKDOWN, *}; use foundry_common::{calc, TestFunctionExt}; use foundry_evm::traces::CallKind; use serde::{Deserialize, Serialize}; -use std::{ - collections::{BTreeMap, HashSet}, - fmt::Display, -}; +use std::{collections::BTreeMap, fmt::Display}; use yansi::Paint; /// Represents the gas report for a set of contracts. diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 2a964789b..e580fb8b2 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -9,7 +9,7 @@ use crate::{ }; use alloy_dyn_abi::DynSolValue; use alloy_json_abi::Function; -use alloy_primitives::{address, Address, Bytes, U256}; +use alloy_primitives::{address, map::HashMap, Address, Bytes, U256}; use eyre::Result; use foundry_common::{ contracts::{ContractsByAddress, ContractsByArtifact}, @@ -35,12 +35,7 @@ use foundry_evm::{ }; use proptest::test_runner::TestRunner; use rayon::prelude::*; -use std::{ - borrow::Cow, - cmp::min, - collections::{BTreeMap, HashMap}, - time::Instant, -}; +use std::{borrow::Cow, cmp::min, collections::BTreeMap, time::Instant}; /// When running tests, we deploy all external libraries present in the project. To avoid additional /// libraries affecting nonces of senders used in tests, we are using separate address to diff --git a/crates/script/src/providers.rs b/crates/script/src/providers.rs index 7f1aa0eb3..eb6ea9319 100644 --- a/crates/script/src/providers.rs +++ b/crates/script/src/providers.rs @@ -1,12 +1,9 @@ +use alloy_primitives::map::{hash_map::Entry, HashMap}; use alloy_provider::{utils::Eip1559Estimation, Provider}; use eyre::{Result, WrapErr}; use foundry_common::provider::{get_http_provider, RetryProvider}; use foundry_config::Chain; -use std::{ - collections::{hash_map::Entry, HashMap}, - ops::Deref, - sync::Arc, -}; +use std::{ops::Deref, sync::Arc}; /// Contains a map of RPC urls to single instances of [`ProviderInfo`]. #[derive(Default)] diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 7c692ea23..432a8ccd5 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, Bytes, TxKind, U256}; +use alloy_primitives::{map::HashMap, utils::format_units, Address, Bytes, TxKind, U256}; use dialoguer::Confirm; use eyre::{Context, Result}; use foundry_cheatcodes::ScriptWallets; @@ -23,7 +23,7 @@ use foundry_evm::traces::{decode_trace_arena, render_trace_arena}; use futures::future::{join_all, try_join_all}; use parking_lot::RwLock; use std::{ - collections::{BTreeMap, HashMap, VecDeque}, + collections::{BTreeMap, VecDeque}, sync::Arc, }; use yansi::Paint; diff --git a/crates/test-utils/src/rpc.rs b/crates/test-utils/src/rpc.rs index 51ec2c8e0..a9ff6a7e6 100644 --- a/crates/test-utils/src/rpc.rs +++ b/crates/test-utils/src/rpc.rs @@ -177,9 +177,8 @@ fn next_url(is_ws: bool, chain: NamedChain) -> String { } #[cfg(test)] +#[allow(clippy::needless_return)] mod tests { - #![allow(clippy::needless_return)] - use super::*; use alloy_primitives::address; use foundry_config::Chain; diff --git a/crates/verify/src/etherscan/mod.rs b/crates/verify/src/etherscan/mod.rs index 3839b845b..8b8c2bc35 100644 --- a/crates/verify/src/etherscan/mod.rs +++ b/crates/verify/src/etherscan/mod.rs @@ -451,9 +451,8 @@ async fn ensure_solc_build_metadata(version: Version) -> Result { } #[cfg(test)] +#[allow(clippy::needless_return)] mod tests { - #![allow(clippy::needless_return)] - use super::*; use clap::Parser; use foundry_common::fs; diff --git a/crates/verify/src/sourcify.rs b/crates/verify/src/sourcify.rs index bbb5e9f9e..9dbe027ef 100644 --- a/crates/verify/src/sourcify.rs +++ b/crates/verify/src/sourcify.rs @@ -8,7 +8,6 @@ use eyre::Result; use foundry_common::{fs, retry::Retry}; use futures::FutureExt; use reqwest::Url; -use revm_primitives::map::FxBuildHasher; use serde::{Deserialize, Serialize}; use std::str::FromStr; @@ -114,8 +113,7 @@ impl SourcifyVerificationProvider { let metadata = context.get_target_metadata()?; let imports = context.get_target_imports()?; - let mut files = - HashMap::with_capacity_and_hasher(2 + imports.len(), FxBuildHasher::default()); + let mut files = HashMap::with_capacity_and_hasher(2 + imports.len(), Default::default()); let metadata = serde_json::to_string_pretty(&metadata)?; files.insert("metadata.json".to_string(), metadata); diff --git a/crates/wallets/src/wallet.rs b/crates/wallets/src/wallet.rs index 37b731ddb..01a232895 100644 --- a/crates/wallets/src/wallet.rs +++ b/crates/wallets/src/wallet.rs @@ -148,9 +148,8 @@ impl From for WalletOpts { } #[cfg(test)] +#[allow(clippy::needless_return)] mod tests { - #![allow(clippy::needless_return)] - use alloy_signer::Signer; use std::{path::Path, str::FromStr}; diff --git a/deny.toml b/deny.toml index e908828cc..b932b0388 100644 --- a/deny.toml +++ b/deny.toml @@ -53,6 +53,7 @@ allow = [ "0BSD", "MPL-2.0", "CDDL-1.0", + "Zlib", ] # Allow 1 or more licenses on a per-crate basis, so that particular licenses From 1ba5d6fa58a80a5b24372f8a4894fc681bf0188a Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 7 Oct 2024 18:33:22 +0300 Subject: [PATCH 069/100] feat(cheatcodes): add vm.cloneAccount() cheatcode (#9048) * feat(cheatcodes): add vm.cloneAccount() cheatcode * Fmt * Cargo cheats * Changes after review: - use autogenerated getter - consistent clone naming - nits --- crates/cheatcodes/assets/cheatcodes.json | 22 ++++- crates/cheatcodes/spec/src/vm.rs | 6 +- crates/cheatcodes/src/evm.rs | 51 ++++++++---- crates/evm/core/src/backend/cow.rs | 9 ++ crates/evm/core/src/backend/mod.rs | 96 ++++++++++++++-------- testdata/cheats/Vm.sol | 1 + testdata/default/cheats/CloneAccount.t.sol | 45 ++++++++++ 7 files changed, 176 insertions(+), 54 deletions(-) create mode 100644 testdata/default/cheats/CloneAccount.t.sol diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 9ef78f8b0..e6fef48b0 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -3191,6 +3191,26 @@ "status": "stable", "safety": "unsafe" }, + { + "func": { + "id": "cloneAccount", + "description": "Clones a source account code, state, balance and nonce to a target account and updates in-memory EVM state.", + "declaration": "function cloneAccount(address source, address target) external;", + "visibility": "external", + "mutability": "", + "signature": "cloneAccount(address,address)", + "selector": "0x533d61c9", + "selectorBytes": [ + 83, + 61, + 97, + 201 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, { "func": { "id": "closeFile", @@ -5600,7 +5620,7 @@ { "func": { "id": "loadAllocs", - "description": "Load a genesis JSON file's `allocs` into the in-memory revm state.", + "description": "Load a genesis JSON file's `allocs` into the in-memory EVM state.", "declaration": "function loadAllocs(string calldata pathToAllocsJson) external;", "visibility": "external", "mutability": "", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 79249366c..0ee95e43f 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -283,10 +283,14 @@ interface Vm { #[cheatcode(group = Evm, safety = Safe)] function load(address target, bytes32 slot) external view returns (bytes32 data); - /// Load a genesis JSON file's `allocs` into the in-memory revm state. + /// Load a genesis JSON file's `allocs` into the in-memory EVM state. #[cheatcode(group = Evm, safety = Unsafe)] function loadAllocs(string calldata pathToAllocsJson) external; + /// Clones a source account code, state, balance and nonce to a target account and updates in-memory EVM state. + #[cheatcode(group = Evm, safety = Unsafe)] + function cloneAccount(address source, address target) external; + // -------- Record Storage -------- /// Records all storage reads and writes. diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 7691cd4c9..b9a3d7047 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -156,6 +156,22 @@ impl Cheatcode for loadAllocsCall { } } +impl Cheatcode for cloneAccountCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { source, target } = self; + + let account = ccx.ecx.journaled_state.load_account(*source, &mut ccx.ecx.db)?; + ccx.ecx.db.clone_account( + &genesis_account(account.data), + target, + &mut ccx.ecx.journaled_state, + )?; + // Cloned account should persist in forked envs. + ccx.ecx.db.add_persistent_account(*target); + Ok(Default::default()) + } +} + impl Cheatcode for dumpStateCall { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { pathToStateJson } = self; @@ -178,23 +194,7 @@ impl Cheatcode for dumpStateCall { .state() .iter_mut() .filter(|(key, val)| !skip(key, val)) - .map(|(key, val)| { - ( - key, - GenesisAccount { - nonce: Some(val.info.nonce), - balance: val.info.balance, - code: val.info.code.as_ref().map(|o| o.original_bytes()), - storage: Some( - val.storage - .iter() - .map(|(k, v)| (B256::from(*k), B256::from(v.present_value()))) - .collect(), - ), - private_key: None, - }, - ) - }) + .map(|(key, val)| (key, genesis_account(val))) .collect::>(); write_json_file(path, &alloc)?; @@ -957,3 +957,20 @@ fn get_state_diff(state: &mut Cheatcodes) -> Result { .collect::>(); Ok(res.abi_encode()) } + +/// Helper function that creates a `GenesisAccount` from a regular `Account`. +fn genesis_account(account: &Account) -> GenesisAccount { + GenesisAccount { + nonce: Some(account.info.nonce), + balance: account.info.balance, + code: account.info.code.as_ref().map(|o| o.original_bytes()), + storage: Some( + account + .storage + .iter() + .map(|(k, v)| (B256::from(*k), B256::from(v.present_value()))) + .collect(), + ), + private_key: None, + } +} diff --git a/crates/evm/core/src/backend/cow.rs b/crates/evm/core/src/backend/cow.rs index fcc2e1596..8623ca2f9 100644 --- a/crates/evm/core/src/backend/cow.rs +++ b/crates/evm/core/src/backend/cow.rs @@ -233,6 +233,15 @@ impl DatabaseExt for CowBackend<'_> { self.backend_mut(&Env::default()).load_allocs(allocs, journaled_state) } + fn clone_account( + &mut self, + source: &GenesisAccount, + target: &Address, + journaled_state: &mut JournaledState, + ) -> Result<(), BackendError> { + self.backend_mut(&Env::default()).clone_account(source, target, journaled_state) + } + fn is_persistent(&self, acc: &Address) -> bool { self.backend.is_persistent(acc) } diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 27c2d6944..d6774b89a 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -279,6 +279,17 @@ pub trait DatabaseExt: Database + DatabaseCommit { journaled_state: &mut JournaledState, ) -> Result<(), BackendError>; + /// Copies bytecode, storage, nonce and balance from the given genesis account to the target + /// address. + /// + /// Returns [Ok] if data was successfully inserted into the journal, [Err] otherwise. + fn clone_account( + &mut self, + source: &GenesisAccount, + target: &Address, + journaled_state: &mut JournaledState, + ) -> Result<(), BackendError>; + /// Returns true if the given account is currently marked as persistent. fn is_persistent(&self, acc: &Address) -> bool; @@ -1367,45 +1378,60 @@ impl DatabaseExt for Backend { ) -> Result<(), BackendError> { // Loop through all of the allocs defined in the map and commit them to the journal. 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 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() { - state_acc.info.code_hash = keccak256(bytecode); - let bytecode = Bytecode::new_raw(bytecode.0.clone().into()); - state_acc.info.code = Some(bytecode); - } + self.clone_account(acc, addr, journaled_state)?; + } - // Set the account's storage, if the `storage` field is present. - if let Some(storage) = acc.storage.as_ref() { - state_acc.storage = storage - .iter() - .map(|(slot, value)| { - let slot = U256::from_be_bytes(slot.0); - ( - slot, - EvmStorageSlot::new_changed( - state_acc - .storage - .get(&slot) - .map(|s| s.present_value) - .unwrap_or_default(), - U256::from_be_bytes(value.0), - ), - ) - }) - .collect(); - } - // Set the account's nonce and balance. - state_acc.info.nonce = acc.nonce.unwrap_or_default(); - state_acc.info.balance = acc.balance; + Ok(()) + } - // Touch the account to ensure the loaded information persists if called in `setUp`. - journaled_state.touch(addr); + /// Copies bytecode, storage, nonce and balance from the given genesis account to the target + /// address. + /// + /// Returns [Ok] if data was successfully inserted into the journal, [Err] otherwise. + fn clone_account( + &mut self, + source: &GenesisAccount, + target: &Address, + journaled_state: &mut JournaledState, + ) -> Result<(), BackendError> { + // Fetch the account from the journaled state. Will create a new account if it does + // not already exist. + let mut state_acc = journaled_state.load_account(*target, self)?; + + // Set the account's bytecode and code hash, if the `bytecode` field is present. + if let Some(bytecode) = source.code.as_ref() { + state_acc.info.code_hash = keccak256(bytecode); + let bytecode = Bytecode::new_raw(bytecode.0.clone().into()); + state_acc.info.code = Some(bytecode); } + // Set the account's storage, if the `storage` field is present. + if let Some(storage) = source.storage.as_ref() { + state_acc.storage = storage + .iter() + .map(|(slot, value)| { + let slot = U256::from_be_bytes(slot.0); + ( + slot, + EvmStorageSlot::new_changed( + state_acc + .storage + .get(&slot) + .map(|s| s.present_value) + .unwrap_or_default(), + U256::from_be_bytes(value.0), + ), + ) + }) + .collect(); + } + // Set the account's nonce and balance. + state_acc.info.nonce = source.nonce.unwrap_or_default(); + state_acc.info.balance = source.balance; + + // Touch the account to ensure the loaded information persists if called in `setUp`. + journaled_state.touch(target); + Ok(()) } diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 8bb13577e..be6522ac4 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -155,6 +155,7 @@ interface Vm { function broadcast(uint256 privateKey) external; function chainId(uint256 newChainId) external; function clearMockedCalls() external; + function cloneAccount(address source, address target) external; function closeFile(string calldata path) external; function coinbase(address newCoinbase) external; function computeCreate2Address(bytes32 salt, bytes32 initCodeHash, address deployer) external pure returns (address); diff --git a/testdata/default/cheats/CloneAccount.t.sol b/testdata/default/cheats/CloneAccount.t.sol new file mode 100644 index 000000000..5342a92d3 --- /dev/null +++ b/testdata/default/cheats/CloneAccount.t.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract Source { + uint256 public a; + address public b; + uint256[3] public c; + bool public d; + + constructor() { + a = 100; + b = address(111); + c[0] = 222; + c[1] = 333; + c[2] = 444; + d = true; + } +} + +contract CloneAccountTest is DSTest { + Vm vm = Vm(HEVM_ADDRESS); + + address clone = address(777); + + function setUp() public { + Source src = new Source(); + vm.deal(address(src), 0.123 ether); + vm.cloneAccount(address(src), clone); + } + + function test_clone_account() public { + // Check clone balance. + assertEq(clone.balance, 0.123 ether); + // Check clone storage. + assertEq(Source(clone).a(), 100); + assertEq(Source(clone).b(), address(111)); + assertEq(Source(clone).c(0), 222); + assertEq(Source(clone).c(1), 333); + assertEq(Source(clone).c(2), 444); + assertEq(Source(clone).d(), true); + } +} From e215f3fdeada259a8886a7611151794d280ca298 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Mon, 7 Oct 2024 17:39:49 +0200 Subject: [PATCH 070/100] fix(`anvil`): eth_gasPrice returned `1000000000` with `--block-base-fee-per-gas 0`, adds new `--disable-min-priority-fee` to return `0` (#9049) * add new flag to disable min suggested priority fee: `--disable-min-priority-fee` * documentation * remove unnecessary value_name --- crates/anvil/src/cmd.rs | 5 +++++ crates/anvil/src/config.rs | 11 +++++++++++ crates/anvil/src/eth/api.rs | 6 +++++- crates/anvil/src/eth/backend/mem/mod.rs | 5 +++++ crates/anvil/src/eth/fees.rs | 8 ++++++++ 5 files changed, 34 insertions(+), 1 deletion(-) diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 149b9c863..76eb0510a 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -254,6 +254,7 @@ impl NodeArgs { .fork_compute_units_per_second(compute_units_per_second) .with_eth_rpc_url(self.evm_opts.fork_url.map(|fork| fork.url)) .with_base_fee(self.evm_opts.block_base_fee_per_gas) + .disable_min_priority_fee(self.evm_opts.disable_min_priority_fee) .with_storage_caching(self.evm_opts.no_storage_caching) .with_server_config(self.server_config) .with_host(self.host) @@ -547,6 +548,10 @@ pub struct AnvilEvmArgs { )] pub block_base_fee_per_gas: Option, + /// Disable the enforcement of a minimum suggested priority fee. + #[arg(long, visible_alias = "no-priority-fee", help_heading = "Environment config")] + pub disable_min_priority_fee: bool, + /// The chain ID. #[arg(long, alias = "chain", help_heading = "Environment config")] pub chain_id: Option, diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 189cf5175..273dbad89 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -99,6 +99,8 @@ pub struct NodeConfig { pub gas_price: Option, /// Default base fee pub base_fee: Option, + /// If set to `true`, disables the enforcement of a minimum suggested priority fee + pub disable_min_priority_fee: bool, /// Default blob excess gas and price pub blob_excess_gas_and_price: Option, /// The hardfork to use @@ -432,6 +434,7 @@ impl Default for NodeConfig { fork_choice: None, account_generator: None, base_fee: None, + disable_min_priority_fee: false, blob_excess_gas_and_price: None, enable_tracing: true, enable_steps_tracing: false, @@ -623,6 +626,13 @@ impl NodeConfig { self } + /// Disable the enforcement of a minimum suggested priority fee + #[must_use] + pub fn disable_min_priority_fee(mut self, disable_min_priority_fee: bool) -> Self { + self.disable_min_priority_fee = disable_min_priority_fee; + self + } + /// Sets the init genesis (genesis.json) #[must_use] pub fn with_genesis(mut self, genesis: Option) -> Self { @@ -994,6 +1004,7 @@ impl NodeConfig { let fees = FeeManager::new( cfg.handler_cfg.spec_id, self.get_base_fee(), + !self.disable_min_priority_fee, self.get_gas_price(), self.get_blob_excess_gas_and_price(), ); diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index c8fd497a3..78b52d494 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -592,7 +592,11 @@ impl EthApi { /// Returns the current gas price pub fn gas_price(&self) -> u128 { if self.backend.is_eip1559() { - (self.backend.base_fee() as u128).saturating_add(self.lowest_suggestion_tip()) + if self.backend.is_min_priority_fee_enforced() { + (self.backend.base_fee() as u128).saturating_add(self.lowest_suggestion_tip()) + } else { + self.backend.base_fee() as u128 + } } else { self.backend.fees().raw_gas_price() } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 05a7a3ff7..0b7777f2d 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -668,6 +668,11 @@ impl Backend { self.fees.base_fee() } + /// Returns whether the minimum suggested priority fee is enforced + pub fn is_min_priority_fee_enforced(&self) -> bool { + self.fees.is_min_priority_fee_enforced() + } + pub fn excess_blob_gas_and_price(&self) -> Option { self.fees.excess_blob_gas_and_price() } diff --git a/crates/anvil/src/eth/fees.rs b/crates/anvil/src/eth/fees.rs index 72d66b113..f41c51505 100644 --- a/crates/anvil/src/eth/fees.rs +++ b/crates/anvil/src/eth/fees.rs @@ -48,6 +48,8 @@ pub struct FeeManager { /// /// This value will be updated after a new block was mined base_fee: Arc>, + /// Whether the minimum suggested priority fee is enforced + is_min_priority_fee_enforced: bool, /// Tracks the excess blob gas, and the base fee, for the next block post Cancun /// /// This value will be updated after a new block was mined @@ -63,12 +65,14 @@ impl FeeManager { pub fn new( spec_id: SpecId, base_fee: u64, + is_min_priority_fee_enforced: bool, gas_price: u128, blob_excess_gas_and_price: BlobExcessGasAndPrice, ) -> Self { Self { spec_id, base_fee: Arc::new(RwLock::new(base_fee)), + is_min_priority_fee_enforced, gas_price: Arc::new(RwLock::new(gas_price)), blob_excess_gas_and_price: Arc::new(RwLock::new(blob_excess_gas_and_price)), elasticity: Arc::new(RwLock::new(default_elasticity())), @@ -105,6 +109,10 @@ impl FeeManager { } } + pub fn is_min_priority_fee_enforced(&self) -> bool { + self.is_min_priority_fee_enforced + } + /// Raw base gas price pub fn raw_gas_price(&self) -> u128 { *self.gas_price.read() From 0b9bdf35e14708cd88504bda55599eba196d21fc Mon Sep 17 00:00:00 2001 From: m4rio <92288535+mario-eth@users.noreply.github.com> Date: Tue, 8 Oct 2024 14:15:36 +0300 Subject: [PATCH 071/100] feat: update to Soldeer v0.4.0 (#9014) * updated to version 0.4.0 * fmt and clippy * added cargo modifications * solving small nits * forcing special chars on windows * escaping special chars * removing stderr checks * fmt * remvoving err assert from login --- Cargo.lock | 92 ++++++++- Cargo.toml | 2 +- crates/forge/Cargo.toml | 2 +- crates/forge/bin/cmd/soldeer.rs | 21 +- crates/forge/bin/main.rs | 2 +- crates/forge/tests/cli/soldeer.rs | 308 ++++++++---------------------- 6 files changed, 175 insertions(+), 252 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d94ee063d..d29783c24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1834,6 +1834,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "bon" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97493a391b4b18ee918675fb8663e53646fd09321c58b46afa04e8ce2499c869" +dependencies = [ + "bon-macros", + "rustversion", +] + +[[package]] +name = "bon-macros" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2af3eac944c12cdf4423eab70d310da0a8e5851a18ffb192c0a5e3f7ae1663" +dependencies = [ + "darling", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.79", +] + [[package]] name = "bs58" version = "0.5.1" @@ -2183,6 +2206,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "cliclack" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a80570d35684e725e9d2d4aaaf32bc0cbfcfb8539898f9afea3da0d2e5189e4" +dependencies = [ + "console", + "indicatif", + "once_cell", + "strsim", + "textwrap", + "zeroize", +] + [[package]] name = "clipboard-win" version = "5.4.0" @@ -3399,7 +3436,7 @@ dependencies = [ "similar", "similar-asserts", "solang-parser", - "soldeer", + "soldeer-commands", "strum", "svm-rs", "tempfile", @@ -8165,6 +8202,12 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + [[package]] name = "snapbox" version = "0.6.18" @@ -8215,24 +8258,39 @@ dependencies = [ ] [[package]] -name = "soldeer" -version = "0.3.4" +name = "soldeer-commands" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ed763c2bb43241ca0fb6c00feea54187895b7f4eb1090654fbf82807127369" +checksum = "236ae9bfdac074b0cf30caef23e59265b8f609334be3941727c1babcbb04c9cf" dependencies = [ - "chrono", "clap", + "cliclack", + "derive_more 1.0.0", + "email-address-parser", + "rayon", + "soldeer-core", +] + +[[package]] +name = "soldeer-core" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d84a27730c18b4ae2bce01a81bec17397009e3dd739e26f9c10ed8807a97d93e" +dependencies = [ + "bon", + "chrono", + "cliclack", "const-hex", + "derive_more 1.0.0", "dunce", - "email-address-parser", - "futures", "home", "ignore", "path-slash", + "rayon", "regex", "reqwest", - "rpassword", "sanitize-filename", + "semver 1.0.23", "serde", "serde_json", "sha2", @@ -8240,7 +8298,6 @@ dependencies = [ "tokio", "toml_edit", "uuid 1.10.0", - "yansi", "zip", "zip-extract", ] @@ -8510,6 +8567,17 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" +[[package]] +name = "textwrap" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width 0.1.14", +] + [[package]] name = "thiserror" version = "1.0.64" @@ -9126,6 +9194,12 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + [[package]] name = "unicode-normalization" version = "0.1.24" diff --git a/Cargo.toml b/Cargo.toml index bbefa8c4b..99637920d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -274,7 +274,7 @@ reqwest = { version = "0.12", default-features = false } tower = "0.4" tower-http = "0.5" # soldeer -soldeer = "=0.3.4" +soldeer-commands = "=0.4.0" proptest = "1" comfy-table = "7" diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 2cd660785..0e359191a 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -106,7 +106,7 @@ tower-http = { workspace = true, features = ["fs"] } opener = "0.7" # soldeer -soldeer.workspace = true +soldeer-commands.workspace = true quick-junit = "0.5.0" [target.'cfg(unix)'.dependencies] diff --git a/crates/forge/bin/cmd/soldeer.rs b/crates/forge/bin/cmd/soldeer.rs index 5482bdc6f..56b8c31f3 100644 --- a/crates/forge/bin/cmd/soldeer.rs +++ b/crates/forge/bin/cmd/soldeer.rs @@ -1,7 +1,7 @@ use clap::Parser; use eyre::Result; -use soldeer::commands::Subcommands; +use soldeer_commands::Command; // CLI arguments for `forge soldeer`. // The following list of commands and their actions: @@ -22,14 +22,14 @@ use soldeer::commands::Subcommands; 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 must be one of the following init/install/login/push/uninstall/update/version. #[command(subcommand)] - command: Subcommands, + command: Command, } impl SoldeerArgs { - pub fn run(self) -> Result<()> { - match soldeer::run(self.command) { + pub async fn run(self) -> Result<()> { + match soldeer_commands::run(self.command).await { Ok(_) => Ok(()), Err(err) => Err(eyre::eyre!("Failed to run soldeer {}", err)), } @@ -38,11 +38,12 @@ impl SoldeerArgs { #[cfg(test)] mod tests { - use super::*; - use soldeer::commands::Version; + use soldeer_commands::{commands::Version, Command}; - #[test] - fn test_soldeer_version() { - assert!(soldeer::run(Subcommands::Version(Version {})).is_ok()); + #[tokio::test] + #[allow(clippy::needless_return)] + async fn test_soldeer_version() { + let command = Command::Version(Version::default()); + assert!(soldeer_commands::run(command).await.is_ok()); } } diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs index 9a98d8aef..ba89d6dcf 100644 --- a/crates/forge/bin/main.rs +++ b/crates/forge/bin/main.rs @@ -118,7 +118,7 @@ fn main() -> Result<()> { ForgeSubcommand::Generate(cmd) => match cmd.sub { GenerateSubcommands::Test(cmd) => cmd.run(), }, - ForgeSubcommand::Soldeer(cmd) => cmd.run(), + ForgeSubcommand::Soldeer(cmd) => utils::block_on(cmd.run()), ForgeSubcommand::Eip712(cmd) => cmd.run(), ForgeSubcommand::BindJson(cmd) => cmd.run(), } diff --git a/crates/forge/tests/cli/soldeer.rs b/crates/forge/tests/cli/soldeer.rs index 6c57f69f3..30b0c4957 100644 --- a/crates/forge/tests/cli/soldeer.rs +++ b/crates/forge/tests/cli/soldeer.rs @@ -1,32 +1,25 @@ //! Contains various tests related to `forge soldeer`. -use std::{ - fs::{self, OpenOptions}, - path::Path, -}; +use std::{fs, path::Path}; 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 mut foundry_contents = r#"[profile.default] +src = "src" +out = "out" +libs = ["lib"] + +[dependencies] + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options +"#; let foundry_file = prj.root().join("foundry.toml"); + fs::write(&foundry_file, foundry_contents).unwrap(); - 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 forge-std~1.8.1 to remappings - -"#]]); + cmd.arg("soldeer").args([command, dependency]).assert_success(); // Making sure the path was created to the dependency and that foundry.toml exists // meaning that the dependencies were installed correctly @@ -36,20 +29,12 @@ Added forge-std~1.8.1 to remappings // 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] + foundry_contents = r#"[profile.default] src = "src" out = "out" libs = ["lib"] @@ -68,39 +53,19 @@ forgesoldeer!(install_dependency_git, |prj, cmd| { let dependency = "forge-std~1.8.1"; let git = "https://gitlab.com/mario4582928/Mario.git"; + let mut foundry_contents = r#"[profile.default] +src = "src" +out = "out" +libs = ["lib"] + +[dependencies] + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options +"#; let foundry_file = prj.root().join("foundry.toml"); + fs::write(&foundry_file, foundry_contents).unwrap(); - 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 - -"#]]); + cmd.arg("soldeer").args([command, dependency, git]).assert_success(); // Making sure the path was created to the dependency and that README.md exists // meaning that the dependencies were installed correctly @@ -109,19 +74,12 @@ Added forge-std~1.8.1 to remappings // 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")); // Making sure the foundry contents are the right ones - let foundry_contents = r#"[profile.default] + foundry_contents = r#"[profile.default] src = "src" out = "out" libs = ["lib"] @@ -142,25 +100,19 @@ forgesoldeer!(install_dependency_git_commit, |prj, cmd| { let rev_flag = "--rev"; let commit = "7a0663eaf7488732f39550be655bad6694974cb3"; + let mut foundry_contents = r#"[profile.default] +src = "src" +out = "out" +libs = ["lib"] + +[dependencies] + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options +"#; let foundry_file = prj.root().join("foundry.toml"); + fs::write(&foundry_file, foundry_contents).unwrap(); - cmd.arg("soldeer") - .args([command, dependency, git, rev_flag, commit]) - .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 - -"#]]); + cmd.arg("soldeer").args([command, dependency, git, rev_flag, commit]).assert_success(); // Making sure the path was created to the dependency and that README.md exists // meaning that the dependencies were installed correctly @@ -170,19 +122,12 @@ Added forge-std~1.8.1 to remappings // 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")); // Making sure the foundry contents are the right ones - let foundry_contents = r#"[profile.default] + foundry_contents = r#"[profile.default] src = "src" out = "out" libs = ["lib"] @@ -200,7 +145,13 @@ forgesoldeer!(update_dependencies, |prj, cmd| { let command = "update"; // We need to write this into the foundry.toml to make the update install the dependency - let foundry_updates = r#" + let foundry_updates = r#"[profile.default] +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] "@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" } @@ -210,12 +161,7 @@ mario-custom-tag = { version = "1.0", git = "https://gitlab.com/mario4582928/Mar mario-custom-branch = { version = "1.0", git = "https://gitlab.com/mario4582928/Mario.git", tag = "custom-branch" } "#; 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}"); - } + fs::write(&foundry_file, foundry_updates).unwrap(); cmd.arg("soldeer").arg(command).assert_success(); @@ -237,45 +183,6 @@ mario-custom-branch = { version = "1.0", git = "https://gitlab.com/mario4582928/ // 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); @@ -297,7 +204,6 @@ mario = { version = "1.0", git = "https://gitlab.com/mario4582928/Mario.git", re 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); }); @@ -306,27 +212,21 @@ forgesoldeer!(update_dependencies_simple_version, |prj, cmd| { // We need to write this into the foundry.toml to make the update install the dependency, this // is he simplified version of version specification - let foundry_updates = r#" + let foundry_updates = r#"[profile.default] +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" "#; 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").arg(command).assert_success().stdout_eq(str![[r#" -🦌 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. - -"#]]); + fs::write(&foundry_file, foundry_updates).unwrap(); + 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 = @@ -335,16 +235,8 @@ Writing forge-std~1.8.1 to the lock file. // 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 @@ -362,47 +254,28 @@ forge-std = "1.8.1" assert_data_eq!(read_file_to_string(&foundry_file), foundry_contents); }); -forgesoldeer!(login, |prj, cmd| { - let command = "login"; - - let output = cmd.arg("soldeer").arg(command).execute(); - - // 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| { let command = "install"; let dependency = "forge-std~1.8.1"; - let foundry_updates = r#" + let foundry_updates = r#"[profile.default] +src = "src" +out = "out" +libs = ["lib", "dependencies"] + +# 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] "#; 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]).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 - -"#]]); + fs::write(&foundry_file, foundry_updates).unwrap(); + + cmd.arg("soldeer").args([command, dependency]).assert_success(); // Making sure the path was created to the dependency and that foundry.toml exists // meaning that the dependencies were installed correctly @@ -412,23 +285,15 @@ Added all dependencies to remapppings // 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" out = "out" -libs = ["lib"] +libs = ["lib", "dependencies"] 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 @@ -450,6 +315,8 @@ forgesoldeer!(install_dependency_with_remappings_txt, |prj, cmd| { let command = "install"; let dependency = "forge-std~1.8.1"; let foundry_updates = r#" +[dependencies] + [soldeer] remappings_generate = true remappings_prefix = "@custom-f@" @@ -457,26 +324,9 @@ 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]).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 - -"#]]); + fs::write(&foundry_file, foundry_updates).unwrap(); + + cmd.arg("soldeer").args([command, dependency]).assert_success(); // Making sure the path was created to the dependency and that foundry.toml exists // meaning that the dependencies were installed correctly @@ -486,16 +336,8 @@ Added all dependencies to remapppings // 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 @@ -505,6 +347,12 @@ Added all dependencies to remapppings assert_data_eq!(read_file_to_string(&remappings_file), remappings_content); }); +forgesoldeer!(login, |prj, cmd| { + let command = "login"; + + let _ = cmd.arg("soldeer").arg(command).assert_failure(); +}); + fn read_file_to_string(path: &Path) -> String { let contents: String = fs::read_to_string(path).unwrap_or_default(); contents From a17869a6dcce7ce3765c5ed521d40ddb572de9f0 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 8 Oct 2024 19:42:37 +0300 Subject: [PATCH 072/100] fix(invariant): do not commit state if assume returns (#9062) --- crates/evm/evm/src/executors/invariant/mod.rs | 15 +++--- crates/forge/tests/it/invariant.rs | 54 +++++++++++++++++++ 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 2e2e06e6d..f6edd586a 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -317,10 +317,11 @@ impl<'a> InvariantExecutor<'a> { TestCaseError::fail("No input generated to call fuzzed target.") })?; - // Execute call from the randomly generated sequence and commit state changes. - let call_result = current_run + // Execute call from the randomly generated sequence without committing state. + // State is committed only if call is not a magic assume. + let mut call_result = current_run .executor - .transact_raw( + .call_raw( tx.sender, tx.call_details.target, tx.call_details.calldata.clone(), @@ -343,9 +344,11 @@ impl<'a> InvariantExecutor<'a> { return Err(TestCaseError::fail("Max number of vm.assume rejects reached.")) } } else { + // Commit executed call result. + current_run.executor.commit(&mut call_result); + // Collect data for fuzzing from the state changeset. let mut state_changeset = call_result.state_changeset.clone(); - if !call_result.reverted { collect_data( &invariant_test, @@ -369,13 +372,13 @@ impl<'a> InvariantExecutor<'a> { { warn!(target: "forge::test", "{error}"); } - current_run.fuzz_runs.push(FuzzCase { calldata: tx.call_details.calldata.clone(), gas: call_result.gas_used, stipend: call_result.stipend, }); + // Determine if test can continue or should exit. let result = can_continue( &invariant_contract, &invariant_test, @@ -385,11 +388,9 @@ impl<'a> InvariantExecutor<'a> { &state_changeset, ) .map_err(|e| TestCaseError::fail(e.to_string()))?; - if !result.can_continue || current_run.depth == self.config.depth - 1 { invariant_test.set_last_run_inputs(¤t_run.inputs); } - // If test cannot continue then stop current run and exit test suite. if !result.can_continue { return Err(TestCaseError::fail("Test cannot continue.")) diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index a6fa61512..0a9e7910a 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -769,3 +769,57 @@ contract AssumeTest is Test { ... "#]]); }); + +// Test too many inputs rejected for `assumePrecompile`/`assumeForgeAddress`. +// +forgetest_init!(should_revert_with_assume_code, |prj, cmd| { + let config = Config { + invariant: { + InvariantConfig { fail_on_revert: true, max_assume_rejects: 10, ..Default::default() } + }, + ..Default::default() + }; + prj.write_config(config); + + // Add initial test that breaks invariant. + prj.add_test( + "AssumeTest.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract BalanceTestHandler is Test { + address public ref = address(1412323); + address alice; + + constructor(address _alice) { + alice = _alice; + } + + function increment(uint256 amount_, address addr) public { + assumeNotPrecompile(addr); + assumeNotForgeAddress(addr); + assertEq(alice.balance, 100_000 ether); + } +} + +contract BalanceAssumeTest is Test { + function setUp() public { + address alice = makeAddr("alice"); + vm.deal(alice, 100_000 ether); + targetSender(alice); + BalanceTestHandler handler = new BalanceTestHandler(alice); + targetContract(address(handler)); + } + + function invariant_balance() public {} +} + "#, + ) + .unwrap(); + + cmd.args(["test", "--mt", "invariant_balance"]).assert_failure().stdout_eq(str![[r#" +... +[FAIL: `vm.assume` rejected too many inputs (10 allowed)] invariant_balance() (runs: 0, calls: 0, reverts: 0) +... +"#]]); +}); From 373ad46de9034f3b9e30b95084c9d1bd076d66a7 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Wed, 9 Oct 2024 11:31:38 +0530 Subject: [PATCH 073/100] feat(`cheatcodes`): vm.getScriptWallets() (#9052) * feat(`cheatcodes`): vm.getScriptWallets() * feat: load default anvil accounts in script * Revert "feat: load default anvil accounts in script" This reverts commit 4d64356a51bf226482269a2af47f947c4e49e462. * clippy * test --------- Co-authored-by: grandizzy --------- Co-authored-by: grandizzy --- crates/cheatcodes/assets/cheatcodes.json | 20 ++++++++++++ crates/cheatcodes/spec/src/vm.rs | 4 +++ crates/cheatcodes/src/script.rs | 15 +++++++++ crates/forge/tests/cli/script.rs | 40 ++++++++++++++++++++++++ testdata/cheats/Vm.sol | 1 + 5 files changed, 80 insertions(+) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index e6fef48b0..06ddba5bd 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -5395,6 +5395,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "getScriptWallets", + "description": "Returns addresses of available unlocked wallets in the script environment.", + "declaration": "function getScriptWallets() external returns (address[] memory wallets);", + "visibility": "external", + "mutability": "", + "signature": "getScriptWallets()", + "selector": "0x7c49aa1f", + "selectorBytes": [ + 124, + 73, + 170, + 31 + ] + }, + "group": "scripting", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "indexOf", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 0ee95e43f..e73755de1 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1877,6 +1877,10 @@ interface Vm { #[cheatcode(group = Scripting)] function broadcastRawTransaction(bytes calldata data) external; + /// Returns addresses of available unlocked wallets in the script environment. + #[cheatcode(group = Scripting)] + function getScriptWallets() external returns (address[] memory wallets); + // ======== Utilities ======== // -------- Strings -------- diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index 93d5aaaf8..f9535844c 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -3,6 +3,7 @@ use crate::{Cheatcode, CheatsCtxt, Result, Vm::*}; use alloy_primitives::{Address, B256, U256}; use alloy_signer_local::PrivateKeySigner; +use alloy_sol_types::SolValue; use foundry_wallets::{multi_wallet::MultiWallet, WalletSigner}; use parking_lot::Mutex; use std::sync::Arc; @@ -60,6 +61,20 @@ impl Cheatcode for stopBroadcastCall { } } +impl Cheatcode for getScriptWalletsCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let script_wallets = + ccx.state.script_wallets().cloned().map(|sw| sw.signers().unwrap_or_default()); + + if let Some(script_wallets) = script_wallets { + let script_wallets: Vec
= script_wallets.into_iter().collect(); + Ok(script_wallets.abi_encode()) + } else { + Ok(Default::default()) + } + } +} + #[derive(Clone, Debug, Default)] pub struct Broadcast { /// Address of the transaction origin diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 61c2456fe..826d66282 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -2055,3 +2055,43 @@ ONCHAIN EXECUTION COMPLETE & SUCCESSFUL. "#]]); }); + +forgetest_init!(can_get_script_wallets, |prj, cmd| { + let script = prj + .add_source( + "Foo", + r#" +import "forge-std/Script.sol"; + +interface Vm { + function getScriptWallets() external returns (address[] memory wallets); +} + +contract WalletScript is Script { + function run() public { + address[] memory wallets = Vm(address(vm)).getScriptWallets(); + console.log(wallets[0]); + } +}"#, + ) + .unwrap(); + cmd.arg("script") + .arg(script) + .args([ + "--private-key", + "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6", + "-v", + ]) + .assert_success() + .stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! +Script ran successfully. +[GAS] + +== Logs == + 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 + +"#]]); +}); diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index be6522ac4..1458e3e46 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -265,6 +265,7 @@ interface Vm { function getNonce(address account) external view returns (uint64 nonce); function getNonce(Wallet calldata wallet) external returns (uint64 nonce); function getRecordedLogs() external returns (Log[] memory logs); + function getScriptWallets() external returns (address[] memory wallets); function indexOf(string calldata input, string calldata key) external pure returns (uint256); function isContext(ForgeContext context) external view returns (bool result); function isDir(string calldata path) external returns (bool result); From ad86979e06c0577fc097577358e460e7f5ec9bdf Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 9 Oct 2024 11:48:40 +0400 Subject: [PATCH 074/100] fix: support EOF opcodes in `cast da` (#9070) * fix: support EOF opcodes in cast da * fix * fix doc * fmt --- Cargo.lock | 1 - crates/cast/Cargo.toml | 1 - crates/cast/bin/main.rs | 2 +- crates/cast/src/lib.rs | 30 +++++++++++++++++++++++++----- crates/evm/core/src/ic.rs | 33 ++++++++++++++++++++++++++++++++- 5 files changed, 58 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d29783c24..be6aebeea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3644,7 +3644,6 @@ dependencies = [ "comfy-table", "criterion", "dunce", - "evm-disassembler", "evmole", "eyre", "foundry-block-explorers", diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 46f8eac95..9f0b6758c 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -54,7 +54,6 @@ alloy-sol-types.workspace = true alloy-transport.workspace = true chrono.workspace = true -evm-disassembler.workspace = true eyre.workspace = true futures.workspace = true rand.workspace = true diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 0b861f90d..63894d980 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -307,7 +307,7 @@ async fn main_args(args: CastArgs) -> Result<()> { println!("Computed Address: {}", computed.to_checksum(None)); } CastSubcommand::Disassemble { bytecode } => { - println!("{}", SimpleCast::disassemble(&bytecode)?); + println!("{}", SimpleCast::disassemble(&hex::decode(bytecode)?)?); } CastSubcommand::Selectors { bytecode, resolve } => { let functions = SimpleCast::extract_functions(&bytecode)?; diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 8132f8e4f..69f86268b 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -21,7 +21,6 @@ use alloy_sol_types::sol; use alloy_transport::Transport; use base::{Base, NumberWithBase, ToBase}; use chrono::DateTime; -use evm_disassembler::{disassemble_bytes, disassemble_str, format_operations}; use eyre::{Context, ContextCompat, Result}; use foundry_block_explorers::Client; use foundry_common::{ @@ -37,6 +36,7 @@ use rayon::prelude::*; use revm::primitives::Eof; use std::{ borrow::Cow, + fmt::Write, io, marker::PhantomData, path::PathBuf, @@ -45,6 +45,7 @@ use std::{ time::Duration, }; use tokio::signal::ctrl_c; +use utils::decode_instructions; use foundry_common::abi::encode_function_args_packed; pub use foundry_evm::*; @@ -670,7 +671,7 @@ where if disassemble { let code = self.provider.get_code_at(who).block_id(block.unwrap_or_default()).await?.to_vec(); - Ok(format_operations(disassemble_bytes(code)?)?) + SimpleCast::disassemble(&code) } else { Ok(format!( "{}", @@ -1959,17 +1960,36 @@ impl SimpleCast { /// # Example /// /// ``` + /// use alloy_primitives::hex; /// use cast::SimpleCast as Cast; /// /// # async fn foo() -> eyre::Result<()> { /// let bytecode = "0x608060405260043610603f57600035"; - /// let opcodes = Cast::disassemble(bytecode)?; + /// let opcodes = Cast::disassemble(&hex::decode(bytecode)?)?; /// println!("{}", opcodes); /// # Ok(()) /// # } /// ``` - pub fn disassemble(bytecode: &str) -> Result { - format_operations(disassemble_str(bytecode)?) + pub fn disassemble(code: &[u8]) -> Result { + let mut output = String::new(); + + for step in decode_instructions(code) { + write!(output, "{:08x}: ", step.pc)?; + + if let Some(op) = step.op { + write!(output, "{op}")?; + } else { + write!(output, "INVALID")?; + } + + if !step.immediate.is_empty() { + write!(output, " {}", hex::encode_prefixed(step.immediate))?; + } + + writeln!(output)?; + } + + Ok(output) } /// Gets the selector for a given function signature diff --git a/crates/evm/core/src/ic.rs b/crates/evm/core/src/ic.rs index f7ab3093c..2711f8933 100644 --- a/crates/evm/core/src/ic.rs +++ b/crates/evm/core/src/ic.rs @@ -1,5 +1,9 @@ use alloy_primitives::map::HashMap; -use revm::interpreter::opcode::{PUSH0, PUSH1, PUSH32}; +use revm::interpreter::{ + opcode::{PUSH0, PUSH1, PUSH32}, + OpCode, +}; +use revm_inspectors::opcode::immediate_size; /// Maps from program counter to instruction counter. /// @@ -84,3 +88,30 @@ fn make_map(code: &[u8]) -> HashMap { } map } + +/// Represents a single instruction consisting of the opcode and its immediate data. +pub struct Instruction<'a> { + /// OpCode, if it could be decoded. + pub op: Option, + /// Immediate data following the opcode. + pub immediate: &'a [u8], + /// Program counter of the opcode. + pub pc: usize, +} + +/// Decodes raw opcode bytes into [`Instruction`]s. +pub fn decode_instructions(code: &[u8]) -> Vec> { + let mut pc = 0; + let mut steps = Vec::new(); + + while pc < code.len() { + let op = OpCode::new(code[pc]); + let immediate_size = op.map(|op| immediate_size(op, &code[pc + 1..])).unwrap_or(0) as usize; + + steps.push(Instruction { op, pc, immediate: &code[pc + 1..pc + 1 + immediate_size] }); + + pc += 1 + immediate_size; + } + + steps +} From 641132f5418bd7c268366c2da09e5300f3a8e272 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 9 Oct 2024 14:57:05 +0400 Subject: [PATCH 075/100] feat(forge): allow passing value to `--optimize` (#9071) feat(forge): allow passing value to --optimize --- crates/cli/src/opts/build/core.rs | 4 ++-- crates/cli/src/opts/build/mod.rs | 4 ++-- crates/forge/bin/cmd/create.rs | 7 +++++-- crates/forge/bin/cmd/inspect.rs | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/crates/cli/src/opts/build/core.rs b/crates/cli/src/opts/build/core.rs index b0d0ccbbd..59da53372 100644 --- a/crates/cli/src/opts/build/core.rs +++ b/crates/cli/src/opts/build/core.rs @@ -266,8 +266,8 @@ impl Provider for CoreBuildArgs { dict.insert("ast".to_string(), true.into()); } - if self.compiler.optimize { - dict.insert("optimizer".to_string(), self.compiler.optimize.into()); + if let Some(optimize) = self.compiler.optimize { + dict.insert("optimizer".to_string(), optimize.into()); } if !self.compiler.extra_output.is_empty() { diff --git a/crates/cli/src/opts/build/mod.rs b/crates/cli/src/opts/build/mod.rs index dfd5d8366..fe50a3a9a 100644 --- a/crates/cli/src/opts/build/mod.rs +++ b/crates/cli/src/opts/build/mod.rs @@ -26,9 +26,9 @@ pub struct CompilerArgs { pub evm_version: Option, /// Activate the Solidity optimizer. - #[arg(long)] + #[arg(long, default_missing_value="true", num_args = 0..=1)] #[serde(skip)] - pub optimize: bool, + pub optimize: Option, /// 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 diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 3bcf6b36c..df42f458c 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -335,8 +335,11 @@ impl CreateArgs { println!("Starting contract verification..."); - let num_of_optimizations = - if self.opts.compiler.optimize { self.opts.compiler.optimizer_runs } else { None }; + let num_of_optimizations = if self.opts.compiler.optimize.unwrap_or_default() { + self.opts.compiler.optimizer_runs + } else { + None + }; let verify = forge_verify::VerifyArgs { address, contract: Some(self.contract), diff --git a/crates/forge/bin/cmd/inspect.rs b/crates/forge/bin/cmd/inspect.rs index ddfe60e61..14d43d6f5 100644 --- a/crates/forge/bin/cmd/inspect.rs +++ b/crates/forge/bin/cmd/inspect.rs @@ -52,7 +52,7 @@ impl InspectArgs { // Run Optimized? let optimized = if field == ContractArtifactField::AssemblyOptimized { - true + Some(true) } else { build.compiler.optimize }; From a96b8266cf1f11e08ef0dfca9325ea6560d17c55 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:18:51 +0300 Subject: [PATCH 076/100] fix(forge): add logs/decoded logs in json test results (#9074) --- crates/forge/bin/cmd/test/mod.rs | 13 ++++- crates/forge/src/result.rs | 4 ++ crates/forge/tests/cli/test_cmd.rs | 6 +++ .../SimpleContractTestNonVerbose.json | 1 + .../fixtures/SimpleContractTestVerbose.json | 49 ++++++++++++++++++- 5 files changed, 70 insertions(+), 3 deletions(-) diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 0e87a4c66..f83d336ed 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -501,7 +501,18 @@ impl TestArgs { // Run tests in a non-streaming fashion and collect results for serialization. if self.json { - let results = runner.test_collect(filter); + let mut results = runner.test_collect(filter); + results.values_mut().for_each(|suite_result| { + for test_result in suite_result.test_results.values_mut() { + if verbosity >= 2 { + // Decode logs at level 2 and above. + test_result.decoded_logs = decode_console_logs(&test_result.logs); + } else { + // Empty logs for non verbose runs. + test_result.logs = vec![]; + } + } + }); println!("{}", serde_json::to_string(&results)?); return Ok(TestOutcome::new(results, self.allow_failure)); } diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index a6d7fded9..4fb88dfd0 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -389,6 +389,10 @@ pub struct TestResult { /// be printed to the user. pub logs: Vec, + /// The decoded DSTest logging events and Hardhat's `console.log` from [logs](Self::logs). + /// Used for json output. + pub decoded_logs: Vec, + /// What kind of test this was pub kind: TestKind, diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index f8b54cc41..30c7f9deb 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -275,7 +275,10 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) }); const SIMPLE_CONTRACT: &str = r#" +pragma solidity 0.8.18; + import "./test.sol"; +import "./console.sol"; contract SimpleContract { uint256 public num; @@ -289,12 +292,14 @@ contract SimpleContractTest is DSTest { function test() public { SimpleContract c = new SimpleContract(); c.setValues(100); + console.log("Value set: ", 100); } } "#; forgetest!(can_run_test_with_json_output_verbose, |prj, cmd| { prj.insert_ds_test(); + prj.insert_console(); prj.add_source("Simple.t.sol", SIMPLE_CONTRACT).unwrap(); @@ -306,6 +311,7 @@ forgetest!(can_run_test_with_json_output_verbose, |prj, cmd| { forgetest!(can_run_test_with_json_output_non_verbose, |prj, cmd| { prj.insert_ds_test(); + prj.insert_console(); prj.add_source("Simple.t.sol", SIMPLE_CONTRACT).unwrap(); diff --git a/crates/forge/tests/fixtures/SimpleContractTestNonVerbose.json b/crates/forge/tests/fixtures/SimpleContractTestNonVerbose.json index 8fd8a0fae..b4e396863 100644 --- a/crates/forge/tests/fixtures/SimpleContractTestNonVerbose.json +++ b/crates/forge/tests/fixtures/SimpleContractTestNonVerbose.json @@ -7,6 +7,7 @@ "reason": null, "counterexample": null, "logs": [], + "decoded_logs": [], "kind": { "Unit": { "gas": "{...}" diff --git a/crates/forge/tests/fixtures/SimpleContractTestVerbose.json b/crates/forge/tests/fixtures/SimpleContractTestVerbose.json index c7f47cf53..adc700d4d 100644 --- a/crates/forge/tests/fixtures/SimpleContractTestVerbose.json +++ b/crates/forge/tests/fixtures/SimpleContractTestVerbose.json @@ -6,7 +6,18 @@ "status": "Success", "reason": null, "counterexample": null, - "logs": [], + "logs": [ + { + "address": "0x000000000000000000636F6e736F6c652e6c6f67", + "topics": [ + "0x41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000f56616c7565207365743a20203130300000000000000000000000000000000000" + } + ], + "decoded_logs": [ + "Value set: 100" + ], "kind": { "Unit": { "gas": "{...}" @@ -58,7 +69,8 @@ "parent": null, "children": [ 1, - 2 + 2, + 3 ], "idx": 0, "trace": { @@ -91,6 +103,9 @@ }, { "Call": 1 + }, + { + "Call": 2 } ] }, @@ -153,6 +168,36 @@ }, "logs": [], "ordering": [] + }, + { + "parent": 0, + "children": [], + "idx": 3, + "trace": { + "depth": 1, + "success": true, + "caller": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", + "address": "0x000000000000000000636F6e736F6c652e6c6f67", + "maybe_precompile": null, + "selfdestruct_address": null, + "selfdestruct_refund_target": null, + "selfdestruct_transferred_value": null, + "kind": "STATICCALL", + "value": "0x0", + "data": "{...}", + "output": "0x", + "gas_used": "{...}", + "gas_limit": "{...}", + "status": "Stop", + "steps": [], + "decoded": { + "label": null, + "return_data": null, + "call_data": null + } + }, + "logs": [], + "ordering": [] } ] } From d847e0f09a95ef6ff8463521b98136e74dac37da Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:53:33 +0300 Subject: [PATCH 077/100] fix(`forge`): avoid panic when empty fuzz selectors in invariants (#9076) --- crates/evm/evm/src/executors/invariant/mod.rs | 8 +++-- crates/forge/tests/it/invariant.rs | 32 +++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index f6edd586a..58c7efd8f 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -742,9 +742,6 @@ impl<'a> InvariantExecutor<'a> { ) -> Result<()> { for (address, (identifier, _)) in self.setup_contracts.iter() { if let Some(selectors) = self.artifact_filters.targeted.get(identifier) { - if selectors.is_empty() { - continue; - } self.add_address_with_functions(*address, selectors, false, targeted_contracts)?; } } @@ -774,6 +771,11 @@ impl<'a> InvariantExecutor<'a> { should_exclude: bool, targeted_contracts: &mut TargetedContracts, ) -> eyre::Result<()> { + // Do not add address in target contracts if no function selected. + if selectors.is_empty() { + return Ok(()) + } + let contract = match targeted_contracts.entry(address) { Entry::Occupied(entry) => entry.into_mut(), Entry::Vacant(entry) => { diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index 0a9e7910a..37b3c9b23 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -823,3 +823,35 @@ contract BalanceAssumeTest is Test { ... "#]]); }); + +// Test proper message displayed if `targetSelector`/`excludeSelector` called with empty selectors. +// +forgetest_init!(should_not_panic_if_no_selectors, |prj, cmd| { + prj.add_test( + "NoSelectorTest.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract TestHandler is Test {} + +contract NoSelectorTest is Test { + bytes4[] selectors; + + function setUp() public { + TestHandler handler = new TestHandler(); + targetSelector(FuzzSelector({addr: address(handler), selectors: selectors})); + excludeSelector(FuzzSelector({addr: address(handler), selectors: selectors})); + } + + function invariant_panic() public {} +} + "#, + ) + .unwrap(); + + cmd.args(["test", "--mt", "invariant_panic"]).assert_failure().stdout_eq(str![[r#" +... +[FAIL: failed to set up invariant testing environment: No contracts to fuzz.] invariant_panic() (runs: 0, calls: 0, reverts: 0) +... +"#]]); +}); From 97ce8c33b518d3600a48bf0e614d98454bf11463 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Wed, 9 Oct 2024 18:30:55 +0530 Subject: [PATCH 078/100] chore(`anvil`): use op-alloy types (#9047) --- Cargo.lock | 226 +++++--------- Cargo.toml | 7 +- crates/anvil/Cargo.toml | 3 + crates/anvil/core/Cargo.toml | 1 + crates/anvil/core/src/eth/transaction/mod.rs | 15 +- .../core/src/eth/transaction/optimism.rs | 284 +----------------- crates/anvil/src/eth/api.rs | 2 +- crates/anvil/src/eth/sign.rs | 22 +- 8 files changed, 112 insertions(+), 448 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index be6aebeea..381ed5a75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,30 +83,16 @@ dependencies = [ "strum", ] -[[package]] -name = "alloy-consensus" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629b62e38d471cc15fea534eb7283d2f8a4e8bdb1811bcc5d66dda6cfce6fae1" -dependencies = [ - "alloy-eips 0.3.6", - "alloy-primitives", - "alloy-rlp", - "alloy-serde 0.3.6", - "c-kzg", - "serde", -] - [[package]] name = "alloy-consensus" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "705687d5bfd019fee57cf9e206b27b30a9a9617535d5590a02b171e813208f8e" dependencies = [ - "alloy-eips 0.4.2", + "alloy-eips", "alloy-primitives", "alloy-rlp", - "alloy-serde 0.4.2", + "alloy-serde", "auto_impl", "c-kzg", "derive_more 1.0.0", @@ -122,11 +108,11 @@ dependencies = [ "alloy-dyn-abi", "alloy-json-abi", "alloy-network", - "alloy-network-primitives 0.4.2", + "alloy-network-primitives", "alloy-primitives", "alloy-provider", "alloy-pubsub", - "alloy-rpc-types-eth 0.4.2", + "alloy-rpc-types-eth", "alloy-sol-types", "alloy-transport", "futures", @@ -182,24 +168,6 @@ dependencies = [ "serde", ] -[[package]] -name = "alloy-eips" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f923dd5fca5f67a43d81ed3ebad0880bd41f6dd0ada930030353ac356c54cd0f" -dependencies = [ - "alloy-eip2930", - "alloy-eip7702", - "alloy-primitives", - "alloy-rlp", - "alloy-serde 0.3.6", - "c-kzg", - "derive_more 1.0.0", - "once_cell", - "serde", - "sha2", -] - [[package]] name = "alloy-eips" version = "0.4.2" @@ -210,7 +178,7 @@ dependencies = [ "alloy-eip7702", "alloy-primitives", "alloy-rlp", - "alloy-serde 0.4.2", + "alloy-serde", "c-kzg", "derive_more 1.0.0", "once_cell", @@ -225,7 +193,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8429cf4554eed9b40feec7f4451113e76596086447550275e3def933faf47ce3" dependencies = [ "alloy-primitives", - "alloy-serde 0.4.2", + "alloy-serde", "serde", ] @@ -261,13 +229,13 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85fa23a6a9d612b52e402c995f2d582c25165ec03ac6edf64c861a76bc5b87cd" dependencies = [ - "alloy-consensus 0.4.2", - "alloy-eips 0.4.2", + "alloy-consensus", + "alloy-eips", "alloy-json-rpc", - "alloy-network-primitives 0.4.2", + "alloy-network-primitives", "alloy-primitives", - "alloy-rpc-types-eth 0.4.2", - "alloy-serde 0.4.2", + "alloy-rpc-types-eth", + "alloy-serde", "alloy-signer", "alloy-sol-types", "async-trait", @@ -276,28 +244,16 @@ dependencies = [ "thiserror", ] -[[package]] -name = "alloy-network-primitives" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94ad40869867ed2d9cd3842b1e800889e5b49e6b92da346e93862b4a741bedf3" -dependencies = [ - "alloy-eips 0.3.6", - "alloy-primitives", - "alloy-serde 0.3.6", - "serde", -] - [[package]] name = "alloy-network-primitives" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "801492711d4392b2ccf5fc0bc69e299fa1aab15167d74dcaa9aab96a54f684bd" dependencies = [ - "alloy-consensus 0.4.2", - "alloy-eips 0.4.2", + "alloy-consensus", + "alloy-eips", "alloy-primitives", - "alloy-serde 0.4.2", + "alloy-serde", "serde", ] @@ -340,15 +296,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcfaa4ffec0af04e3555686b8aacbcdf7d13638133a0672749209069750f78a6" dependencies = [ "alloy-chains", - "alloy-consensus 0.4.2", - "alloy-eips 0.4.2", + "alloy-consensus", + "alloy-eips", "alloy-json-rpc", "alloy-network", - "alloy-network-primitives 0.4.2", + "alloy-network-primitives", "alloy-primitives", "alloy-pubsub", "alloy-rpc-client", - "alloy-rpc-types-eth 0.4.2", + "alloy-rpc-types-eth", "alloy-rpc-types-trace", "alloy-rpc-types-txpool", "alloy-transport", @@ -447,10 +403,10 @@ dependencies = [ "alloy-primitives", "alloy-rpc-types-anvil", "alloy-rpc-types-engine", - "alloy-rpc-types-eth 0.4.2", + "alloy-rpc-types-eth", "alloy-rpc-types-trace", "alloy-rpc-types-txpool", - "alloy-serde 0.4.2", + "alloy-serde", "serde", ] @@ -461,7 +417,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d780adaa5d95b07ad92006b2feb68ecfa7e2015f7d5976ceaac4c906c73ebd07" dependencies = [ "alloy-primitives", - "alloy-serde 0.4.2", + "alloy-serde", "serde", ] @@ -471,11 +427,11 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0285c4c09f838ab830048b780d7f4a4f460f309aa1194bb049843309524c64c" dependencies = [ - "alloy-consensus 0.4.2", - "alloy-eips 0.4.2", + "alloy-consensus", + "alloy-eips", "alloy-primitives", "alloy-rlp", - "alloy-serde 0.4.2", + "alloy-serde", "derive_more 1.0.0", "jsonwebtoken", "rand", @@ -483,39 +439,18 @@ dependencies = [ "strum", ] -[[package]] -name = "alloy-rpc-types-eth" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83aa984386deda02482660aa31cb8ca1e63d533f1c31a52d7d181ac5ec68e9b8" -dependencies = [ - "alloy-consensus 0.3.6", - "alloy-eips 0.3.6", - "alloy-network-primitives 0.3.6", - "alloy-primitives", - "alloy-rlp", - "alloy-serde 0.3.6", - "alloy-sol-types", - "cfg-if", - "derive_more 1.0.0", - "hashbrown 0.14.5", - "itertools 0.13.0", - "serde", - "serde_json", -] - [[package]] name = "alloy-rpc-types-eth" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413f4aa3ccf2c3e4234a047c5fa4727916d7daf25a89f9b765df0ba09784fd87" dependencies = [ - "alloy-consensus 0.4.2", - "alloy-eips 0.4.2", - "alloy-network-primitives 0.4.2", + "alloy-consensus", + "alloy-eips", + "alloy-network-primitives", "alloy-primitives", "alloy-rlp", - "alloy-serde 0.4.2", + "alloy-serde", "alloy-sol-types", "derive_more 1.0.0", "itertools 0.13.0", @@ -530,8 +465,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "017cad3e5793c5613588c1f9732bcbad77e820ba7d0feaba3527749f856fdbc5" dependencies = [ "alloy-primitives", - "alloy-rpc-types-eth 0.4.2", - "alloy-serde 0.4.2", + "alloy-rpc-types-eth", + "alloy-serde", "serde", "serde_json", "thiserror", @@ -544,20 +479,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b230e321c416be7f50530159392b4c41a45596d40d97e185575bcd0b545e521" dependencies = [ "alloy-primitives", - "alloy-rpc-types-eth 0.4.2", - "alloy-serde 0.4.2", - "serde", -] - -[[package]] -name = "alloy-serde" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "731f75ec5d383107fd745d781619bd9cedf145836c51ecb991623d41278e71fa" -dependencies = [ - "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", "serde", - "serde_json", ] [[package]] @@ -593,7 +517,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "417e19d9844350d11f7426d4920a5df777f8c2abbb7a70d9de6f1faf284db15b" dependencies = [ - "alloy-consensus 0.4.2", + "alloy-consensus", "alloy-network", "alloy-primitives", "alloy-signer", @@ -611,7 +535,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fd12ae28e8330766821058ed9a6a06ae4f9b12c776e8a7cfb16e3a954f508e" dependencies = [ - "alloy-consensus 0.4.2", + "alloy-consensus", "alloy-network", "alloy-primitives", "alloy-signer", @@ -629,7 +553,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3a02400dea370022151f07581b73a836115c88ce4df350206653493bec024e2" dependencies = [ - "alloy-consensus 0.4.2", + "alloy-consensus", "alloy-dyn-abi", "alloy-network", "alloy-primitives", @@ -649,7 +573,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "494e0a256f3e99f2426f994bcd1be312c02cb8f88260088dacb33a8b8936475f" dependencies = [ - "alloy-consensus 0.4.2", + "alloy-consensus", "alloy-network", "alloy-primitives", "alloy-signer", @@ -668,7 +592,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0d91ddee2390b002148128e47902893deba353eb1b818a3afb6c960ebf4e42c" dependencies = [ - "alloy-consensus 0.4.2", + "alloy-consensus", "alloy-network", "alloy-primitives", "alloy-signer", @@ -928,10 +852,10 @@ name = "anvil" version = "0.2.0" dependencies = [ "alloy-chains", - "alloy-consensus 0.4.2", + "alloy-consensus", "alloy-contract", "alloy-dyn-abi", - "alloy-eips 0.4.2", + "alloy-eips", "alloy-genesis", "alloy-json-abi", "alloy-json-rpc", @@ -942,7 +866,7 @@ dependencies = [ "alloy-rlp", "alloy-rpc-client", "alloy-rpc-types", - "alloy-serde 0.4.2", + "alloy-serde", "alloy-signer", "alloy-signer-local", "alloy-sol-types", @@ -973,6 +897,7 @@ dependencies = [ "hyper 1.4.1", "itertools 0.13.0", "k256", + "op-alloy-consensus", "op-alloy-rpc-types", "parking_lot", "rand", @@ -996,17 +921,18 @@ dependencies = [ name = "anvil-core" version = "0.2.0" dependencies = [ - "alloy-consensus 0.4.2", + "alloy-consensus", "alloy-dyn-abi", - "alloy-eips 0.4.2", + "alloy-eips", "alloy-primitives", "alloy-rlp", "alloy-rpc-types", - "alloy-serde 0.4.2", + "alloy-serde", "alloy-trie", "bytes", "foundry-common", "foundry-evm", + "op-alloy-consensus", "rand", "revm", "serde", @@ -3369,14 +3295,14 @@ name = "forge" version = "0.2.0" dependencies = [ "alloy-chains", - "alloy-consensus 0.4.2", + "alloy-consensus", "alloy-dyn-abi", "alloy-json-abi", "alloy-network", "alloy-primitives", "alloy-provider", "alloy-rpc-types", - "alloy-serde 0.4.2", + "alloy-serde", "alloy-signer", "alloy-signer-local", "alloy-sol-macro-expander", @@ -3498,15 +3424,15 @@ name = "forge-script" version = "0.2.0" dependencies = [ "alloy-chains", - "alloy-consensus 0.4.2", + "alloy-consensus", "alloy-dyn-abi", - "alloy-eips 0.4.2", + "alloy-eips", "alloy-json-abi", "alloy-network", "alloy-primitives", "alloy-provider", "alloy-rpc-types", - "alloy-serde 0.4.2", + "alloy-serde", "alloy-signer", "alloy-transport", "async-recursion", @@ -3619,7 +3545,7 @@ name = "foundry-cast" version = "0.2.0" dependencies = [ "alloy-chains", - "alloy-consensus 0.4.2", + "alloy-consensus", "alloy-contract", "alloy-dyn-abi", "alloy-json-abi", @@ -3629,7 +3555,7 @@ dependencies = [ "alloy-provider", "alloy-rlp", "alloy-rpc-types", - "alloy-serde 0.4.2", + "alloy-serde", "alloy-signer", "alloy-signer-local", "alloy-sol-types", @@ -3676,7 +3602,7 @@ dependencies = [ name = "foundry-cheatcodes" version = "0.2.0" dependencies = [ - "alloy-consensus 0.4.2", + "alloy-consensus", "alloy-dyn-abi", "alloy-genesis", "alloy-json-abi", @@ -3767,7 +3693,7 @@ dependencies = [ name = "foundry-common" version = "0.2.0" dependencies = [ - "alloy-consensus 0.4.2", + "alloy-consensus", "alloy-contract", "alloy-dyn-abi", "alloy-json-abi", @@ -3777,7 +3703,7 @@ dependencies = [ "alloy-pubsub", "alloy-rpc-client", "alloy-rpc-types", - "alloy-serde 0.4.2", + "alloy-serde", "alloy-sol-types", "alloy-transport", "alloy-transport-http", @@ -3812,11 +3738,11 @@ dependencies = [ name = "foundry-common-fmt" version = "0.2.0" dependencies = [ - "alloy-consensus 0.4.2", + "alloy-consensus", "alloy-dyn-abi", "alloy-primitives", "alloy-rpc-types", - "alloy-serde 0.4.2", + "alloy-serde", "chrono", "comfy-table", "foundry-macros", @@ -4039,7 +3965,7 @@ dependencies = [ "alloy-primitives", "alloy-provider", "alloy-rpc-types", - "alloy-serde 0.4.2", + "alloy-serde", "alloy-sol-types", "alloy-transport", "auto_impl", @@ -4139,7 +4065,7 @@ dependencies = [ "alloy-primitives", "alloy-provider", "alloy-rpc-types", - "alloy-serde 0.4.2", + "alloy-serde", "alloy-transport", "eyre", "futures", @@ -4199,7 +4125,7 @@ dependencies = [ name = "foundry-wallets" version = "0.2.0" dependencies = [ - "alloy-consensus 0.4.2", + "alloy-consensus", "alloy-dyn-abi", "alloy-network", "alloy-primitives", @@ -4768,7 +4694,6 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", - "serde", ] [[package]] @@ -6131,15 +6056,15 @@ checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" [[package]] name = "op-alloy-consensus" -version = "0.2.12" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21aad1fbf80d2bcd7406880efc7ba109365f44bbb72896758ddcbfa46bf1592c" +checksum = "c4f7f318f885db6e1455370ca91f74b7faed152c8142f6418f0936d606e582ff" dependencies = [ - "alloy-consensus 0.3.6", - "alloy-eips 0.3.6", + "alloy-consensus", + "alloy-eips", "alloy-primitives", "alloy-rlp", - "alloy-serde 0.3.6", + "alloy-serde", "derive_more 1.0.0", "serde", "spin", @@ -6147,17 +6072,16 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types" -version = "0.2.12" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e281fbfc2198b7c0c16457d6524f83d192662bc9f3df70f24c3038d4521616df" +checksum = "547d29c5ab957ff32e14edddb93652dad748d2ef6cbe4b0fe8615ce06b0a3ddb" dependencies = [ - "alloy-eips 0.3.6", - "alloy-network-primitives 0.3.6", + "alloy-consensus", + "alloy-eips", + "alloy-network-primitives", "alloy-primitives", - "alloy-rpc-types-eth 0.3.6", - "alloy-serde 0.3.6", - "cfg-if", - "hashbrown 0.14.5", + "alloy-rpc-types-eth", + "alloy-serde", "op-alloy-consensus", "serde", "serde_json", @@ -7293,7 +7217,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43c44af0bf801f48d25f7baf25cf72aff4c02d610f83b428175228162fef0246" dependencies = [ "alloy-primitives", - "alloy-rpc-types-eth 0.4.2", + "alloy-rpc-types-eth", "alloy-rpc-types-trace", "alloy-sol-types", "anstyle", @@ -7739,9 +7663,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" dependencies = [ "windows-sys 0.59.0", ] diff --git a/Cargo.toml b/Cargo.toml index 99637920d..9b660adbb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -219,8 +219,9 @@ alloy-chains = "0.1" alloy-rlp = "0.3" alloy-trie = "0.6.0" -## op-alloy for tests in anvil -op-alloy-rpc-types = "0.2.9" +## op-alloy +op-alloy-rpc-types = "0.3.3" +op-alloy-consensus = "0.3.3" ## misc async-trait = "0.1" @@ -278,7 +279,7 @@ soldeer-commands = "=0.4.0" proptest = "1" comfy-table = "7" -# [patch.crates-io] +[patch.crates-io] ## alloy-core # alloy-dyn-abi = { path = "../../alloy-rs/core/crates/dyn-abi" } # alloy-json-abi = { path = "../../alloy-rs/core/crates/json-abi" } diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 0723fdb47..922cb6efc 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -67,6 +67,7 @@ alloy-transport.workspace = true alloy-chains.workspace = true alloy-genesis.workspace = true alloy-trie.workspace = true +op-alloy-consensus.workspace = true # axum related axum.workspace = true @@ -121,8 +122,10 @@ 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"] cmd = ["clap", "clap_complete", "ctrlc", "anvil-server/clap"] diff --git a/crates/anvil/core/Cargo.toml b/crates/anvil/core/Cargo.toml index 8c2720c8f..6ea5e5318 100644 --- a/crates/anvil/core/Cargo.toml +++ b/crates/anvil/core/Cargo.toml @@ -30,6 +30,7 @@ alloy-eips.workspace = true alloy-consensus = { workspace = true, features = ["k256", "kzg"] } alloy-dyn-abi = { workspace = true, features = ["std", "eip712"] } alloy-trie.workspace = true +op-alloy-consensus.workspace = true serde = { workspace = true, optional = true } serde_json.workspace = true diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 2ed75db28..c31207920 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -1,6 +1,6 @@ //! Transaction related types -use crate::eth::transaction::optimism::{DepositTransaction, DepositTransactionRequest}; +use crate::eth::transaction::optimism::DepositTransaction; use alloy_consensus::{ transaction::{ eip4844::{TxEip4844, TxEip4844Variant, TxEip4844WithSidecar}, @@ -21,6 +21,7 @@ use alloy_rpc_types::{ use alloy_serde::{OtherFields, WithOtherFields}; use bytes::BufMut; use foundry_evm::traces::CallTraceNode; +use op_alloy_consensus::TxDeposit; use revm::{ interpreter::InstructionResult, primitives::{OptimismFields, TxEnv}, @@ -59,14 +60,16 @@ pub fn transaction_request_to_typed( // Special case: OP-stack deposit tx if transaction_type == Some(0x7E) || has_optimism_fields(&other) { - return Some(TypedTransactionRequest::Deposit(DepositTransactionRequest { + let mint = other.get_deserialized::("mint")?.map(|m| m.to::()).ok()?; + + return Some(TypedTransactionRequest::Deposit(TxDeposit { from: from.unwrap_or_default(), source_hash: other.get_deserialized::("sourceHash")?.ok()?, - kind: to.unwrap_or_default(), - mint: other.get_deserialized::("mint")?.ok()?, + to: to.unwrap_or_default(), + mint: Some(mint), value: value.unwrap_or_default(), gas_limit: gas.unwrap_or_default(), - is_system_tx: other.get_deserialized::("isSystemTx")?.ok()?, + is_system_transaction: other.get_deserialized::("isSystemTx")?.ok()?, input: input.into_input().unwrap_or_default(), })); } @@ -165,7 +168,7 @@ pub enum TypedTransactionRequest { EIP2930(TxEip2930), EIP1559(TxEip1559), EIP4844(TxEip4844Variant), - Deposit(DepositTransactionRequest), + Deposit(TxDeposit), } /// A wrapper for [TypedTransaction] that allows impersonating accounts. diff --git a/crates/anvil/core/src/eth/transaction/optimism.rs b/crates/anvil/core/src/eth/transaction/optimism.rs index 170d67f1d..6bb4b2abb 100644 --- a/crates/anvil/core/src/eth/transaction/optimism.rs +++ b/crates/anvil/core/src/eth/transaction/optimism.rs @@ -1,276 +1,25 @@ -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 alloy_primitives::{Address, Bytes, TxKind, B256, U256}; +use alloy_rlp::{Decodable, Encodable, Error as DecodeError, Header as RlpHeader}; +use op_alloy_consensus::TxDeposit; 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, - pub from: Address, - pub kind: TxKind, - pub mint: U256, - pub value: U256, - pub gas_limit: u64, - pub is_system_tx: bool, - pub input: Bytes, -} - -impl DepositTransactionRequest { - pub fn hash(&self) -> B256 { - 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.source_hash.encode(out); - self.from.encode(out); - self.kind.encode(out); - self.mint.encode(out); - self.value.encode(out); - self.gas_limit.encode(out); - self.is_system_tx.encode(out); - self.input.encode(out); - } - - /// Calculates the length of the RLP-encoded transaction's fields. - pub(crate) fn fields_len(&self) -> usize { - let mut len = 0; - len += self.source_hash.length(); - len += self.from.length(); - len += self.kind.length(); - len += self.mint.length(); - len += self.value.length(); - len += self.gas_limit.length(); - len += self.is_system_tx.length(); - len += self.input.length(); - len - } - - /// Decodes the inner [DepositTransactionRequest] fields from RLP bytes. - /// - /// NOTE: This assumes a RLP header has already been decoded, and _just_ decodes the following - /// RLP fields in the following order: - /// - /// - `source_hash` - /// - `from` - /// - `kind` - /// - `mint` - /// - `value` - /// - `gas_limit` - /// - `is_system_tx` - /// - `input` - pub fn decode_inner(buf: &mut &[u8]) -> Result { - Ok(Self { - source_hash: Decodable::decode(buf)?, - from: Decodable::decode(buf)?, - kind: Decodable::decode(buf)?, - mint: Decodable::decode(buf)?, - value: Decodable::decode(buf)?, - gas_limit: Decodable::decode(buf)?, - is_system_tx: Decodable::decode(buf)?, - input: Decodable::decode(buf)?, - }) - } - - /// Inner encoding function that is used for both rlp [`Encodable`] trait and for calculating - /// hash that for eip2718 does not require rlp header - pub(crate) fn encode_with_signature( - &self, - signature: &Signature, - out: &mut dyn alloy_rlp::BufMut, - ) { - let payload_length = self.fields_len() + signature.rlp_vrs_len(); - let header = alloy_rlp::Header { list: true, payload_length }; - header.encode(out); - self.encode_fields(out); - signature.write_rlp_vrs(out); - } - - /// Output the length of the RLP signed transaction encoding, _without_ a RLP string header. - pub fn payload_len_with_signature_without_header(&self, signature: &Signature) -> usize { - let payload_length = self.fields_len() + signature.rlp_vrs_len(); - // 'transaction type byte length' + 'header length' + 'payload length' - 1 + length_of_length(payload_length) + payload_length - } - - /// Output the length of the RLP signed transaction encoding. This encodes with a RLP header. - pub fn payload_len_with_signature(&self, signature: &Signature) -> usize { - let len = self.payload_len_with_signature_without_header(signature); - length_of_length(len) + len - } - - /// Get transaction type - pub(crate) const fn tx_type(&self) -> u8 { - DEPOSIT_TX_TYPE_ID - } - - /// Calculates a heuristic for the in-memory size of the [DepositTransaction] transaction. - pub fn size(&self) -> usize { - mem::size_of::() + // source_hash - mem::size_of::
() + // from - self.kind.size() + // to - mem::size_of::() + // mint - mem::size_of::() + // value - mem::size_of::() + // gas_limit - mem::size_of::() + // is_system_transaction - self.input.len() // input - } - - /// 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()); - alloy_rlp::Header { list: true, payload_length: self.fields_len() }.encode(out); - self.encode_fields(out); - } - - /// Outputs the length of the signature RLP encoding for the transaction. - pub(crate) fn payload_len_for_signature(&self) -> usize { - let payload_length = self.fields_len(); - // 'transaction type byte length' + 'header length' + 'payload length' - 1 + length_of_length(payload_length) + payload_length - } - - fn encoded_len_with_signature(&self, signature: &Signature) -> usize { - // this counts the tx fields and signature fields - let payload_length = self.fields_len() + signature.rlp_vrs_len(); - - // this counts: - // * tx type byte - // * inner header length - // * inner payload length - 1 + alloy_rlp::Header { list: true, payload_length }.length() + payload_length - } -} - -impl Transaction for DepositTransactionRequest { - fn input(&self) -> &[u8] { - &self.input - } - - /// Get `to`. - fn to(&self) -> TxKind { - self.kind - } - - /// Get `value`. - fn value(&self) -> U256 { - self.value - } - - /// Get `chain_id`. - fn chain_id(&self) -> Option { - None - } - - /// Get `nonce`. - fn nonce(&self) -> u64 { - u64::MAX - } - - /// Get `gas_limit`. - fn gas_limit(&self) -> u64 { - self.gas_limit - } - - /// Get `gas_price`. - 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 { - fn set_chain_id(&mut self, _chain_id: ChainId) {} - - fn payload_len_for_signature(&self) -> usize { - self.payload_len_for_signature() - } - - fn into_signed(self, signature: Signature) -> Signed { - let mut buf = Vec::with_capacity(self.encoded_len_with_signature(&signature)); - self.encode_with_signature(&signature, &mut buf); - let hash = keccak256(&buf); - - // Drop any v chain id value to ensure the signature format is correct at the time of - // combination for an EIP-4844 transaction. V should indicate the y-parity of the - // signature. - Signed::new_unchecked(self, signature.with_parity_bool(), hash) - } - - fn encode_for_signing(&self, out: &mut dyn alloy_rlp::BufMut) { - self.encode_for_signing(out); - } -} - -impl From for DepositTransactionRequest { +impl From for TxDeposit { fn from(tx: DepositTransaction) -> Self { Self { from: tx.from, source_hash: tx.source_hash, - kind: tx.kind, - mint: tx.mint, + to: tx.kind, + mint: Some(tx.mint.to::()), value: tx.value, gas_limit: tx.gas_limit, - is_system_tx: tx.is_system_tx, + is_system_transaction: tx.is_system_tx, input: tx.input, } } } -impl Encodable for DepositTransactionRequest { - fn encode(&self, out: &mut dyn bytes::BufMut) { - RlpHeader { list: true, payload_length: self.fields_len() }.encode(out); - self.encode_fields(out); - } -} - /// An op-stack deposit transaction. /// See #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] @@ -443,23 +192,4 @@ mod tests { 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/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 78b52d494..a85862664 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -2903,7 +2903,7 @@ fn determine_base_gas_by_kind(request: &WithOtherFields) -> TxKind::Create => MIN_CREATE_GAS, }, TypedTransactionRequest::EIP4844(_) => MIN_TRANSACTION_GAS, - TypedTransactionRequest::Deposit(req) => match req.kind { + TypedTransactionRequest::Deposit(req) => match req.to { TxKind::Call(_) => MIN_TRANSACTION_GAS, TxKind::Create => MIN_CREATE_GAS, }, diff --git a/crates/anvil/src/eth/sign.rs b/crates/anvil/src/eth/sign.rs index d322e47d2..5f99ef9ca 100644 --- a/crates/anvil/src/eth/sign.rs +++ b/crates/anvil/src/eth/sign.rs @@ -2,13 +2,13 @@ use crate::eth::error::BlockchainError; use alloy_consensus::SignableTransaction; use alloy_dyn_abi::TypedData; use alloy_network::TxSignerSync; -use alloy_primitives::{map::AddressHashMap, Address, Signature, B256}; +use alloy_primitives::{map::AddressHashMap, Address, Signature, B256, U256}; use alloy_signer::Signer as AlloySigner; use alloy_signer_local::PrivateKeySigner; use anvil_core::eth::transaction::{ - optimism::{DepositTransaction, DepositTransactionRequest}, - TypedTransaction, TypedTransactionRequest, + optimism::DepositTransaction, TypedTransaction, TypedTransactionRequest, }; +use op_alloy_consensus::TxDeposit; /// A transaction signer #[async_trait::async_trait] @@ -105,7 +105,9 @@ impl Signer for DevSigner { TypedTransactionRequest::EIP2930(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?), TypedTransactionRequest::EIP1559(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?), TypedTransactionRequest::EIP4844(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?), - TypedTransactionRequest::Deposit(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?), + TypedTransactionRequest::Deposit(_) => { + unreachable!("op deposit txs should not be signed") + } } } } @@ -131,26 +133,26 @@ pub fn build_typed_transaction( TypedTransaction::EIP4844(tx.into_signed(signature)) } TypedTransactionRequest::Deposit(tx) => { - let DepositTransactionRequest { + let TxDeposit { from, gas_limit, - kind, + to, value, input, source_hash, mint, - is_system_tx, + is_system_transaction, .. } = tx; TypedTransaction::Deposit(DepositTransaction { from, gas_limit, - kind, + kind: to, value, input, source_hash, - mint, - is_system_tx, + mint: mint.map_or(U256::ZERO, U256::from), + is_system_tx: is_system_transaction, nonce: 0, }) } From 1465e39f853a7c7a151609cb3abe5dc19c52a94b Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Wed, 9 Oct 2024 16:34:31 +0200 Subject: [PATCH 079/100] fix: redact RPC URLs in traces if URL is passed in directly (#9077) redact RPC urls if string is a URL, not an alias --- crates/evm/traces/src/decoder/mod.rs | 294 ++++++++++++++++++++++++++- 1 file changed, 293 insertions(+), 1 deletion(-) diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index 25d9f4f2b..de3a0c02f 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -516,6 +516,24 @@ impl CallTraceDecoder { Some(decoded.iter().map(format_token).collect()) } } + "createFork" | + "createSelectFork" | + "rpc" => { + let mut decoded = func.abi_decode_input(&data[SELECTOR_LEN..], false).ok()?; + + // Redact RPC URL except if referenced by an alias + if !decoded.is_empty() && func.inputs[0].ty == "string" { + let url_or_alias = decoded[0].as_str().unwrap_or_default(); + + if url_or_alias.starts_with("http") || url_or_alias.starts_with("ws") { + decoded[0] = DynSolValue::String("".to_string()); + } + } else { + return None; + } + + Some(decoded.iter().map(format_token).collect()) + } _ => None, } } @@ -558,6 +576,7 @@ impl CallTraceDecoder { "promptSecret" | "promptSecretUint" => Some(""), "parseJson" if self.verbosity < 5 => Some(""), "readFile" if self.verbosity < 5 => Some(""), + "rpcUrl" | "rpcUrls" | "rpcUrlStructs" => Some(""), _ => None, } .map(Into::into) @@ -670,7 +689,7 @@ mod tests { use alloy_primitives::hex; #[test] - fn test_should_redact_pk() { + fn test_should_redact() { let decoder = CallTraceDecoder::new(); // [function_signature, data, expected] @@ -726,6 +745,275 @@ mod tests { .to_string(), ]), ), + ( + // cast calldata "createFork(string)" "https://eth-mainnet.g.alchemy.com/v2/api_key" + "createFork(string)", + hex!( + " + 31ba3498 + 0000000000000000000000000000000000000000000000000000000000000020 + 000000000000000000000000000000000000000000000000000000000000002c + 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f + 6d2f76322f6170695f6b65790000000000000000000000000000000000000000 + " + ) + .to_vec(), + Some(vec!["\"\"".to_string()]), + ), + ( + // cast calldata "createFork(string)" "wss://eth-mainnet.g.alchemy.com/v2/api_key" + "createFork(string)", + hex!( + " + 31ba3498 + 0000000000000000000000000000000000000000000000000000000000000020 + 000000000000000000000000000000000000000000000000000000000000002a + 7773733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f6d2f + 76322f6170695f6b657900000000000000000000000000000000000000000000 + " + ) + .to_vec(), + Some(vec!["\"\"".to_string()]), + ), + ( + // cast calldata "createFork(string)" "mainnet" + "createFork(string)", + hex!( + " + 31ba3498 + 0000000000000000000000000000000000000000000000000000000000000020 + 0000000000000000000000000000000000000000000000000000000000000007 + 6d61696e6e657400000000000000000000000000000000000000000000000000 + " + ) + .to_vec(), + Some(vec!["\"mainnet\"".to_string()]), + ), + ( + // cast calldata "createFork(string,uint256)" "https://eth-mainnet.g.alchemy.com/v2/api_key" 1 + "createFork(string,uint256)", + hex!( + " + 6ba3ba2b + 0000000000000000000000000000000000000000000000000000000000000040 + 0000000000000000000000000000000000000000000000000000000000000001 + 000000000000000000000000000000000000000000000000000000000000002c + 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f + 6d2f76322f6170695f6b65790000000000000000000000000000000000000000 + " + ) + .to_vec(), + Some(vec!["\"\"".to_string(), "1".to_string()]), + ), + ( + // cast calldata "createFork(string,uint256)" "mainnet" 1 + "createFork(string,uint256)", + hex!( + " + 6ba3ba2b + 0000000000000000000000000000000000000000000000000000000000000040 + 0000000000000000000000000000000000000000000000000000000000000001 + 0000000000000000000000000000000000000000000000000000000000000007 + 6d61696e6e657400000000000000000000000000000000000000000000000000 + " + ) + .to_vec(), + Some(vec!["\"mainnet\"".to_string(), "1".to_string()]), + ), + ( + // cast calldata "createFork(string,bytes32)" "https://eth-mainnet.g.alchemy.com/v2/api_key" 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + "createFork(string,bytes32)", + hex!( + " + 7ca29682 + 0000000000000000000000000000000000000000000000000000000000000040 + ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + 000000000000000000000000000000000000000000000000000000000000002c + 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f + 6d2f76322f6170695f6b65790000000000000000000000000000000000000000 + " + ) + .to_vec(), + Some(vec![ + "\"\"".to_string(), + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + .to_string(), + ]), + ), + ( + // cast calldata "createFork(string,bytes32)" "mainnet" + // 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + "createFork(string,bytes32)", + hex!( + " + 7ca29682 + 0000000000000000000000000000000000000000000000000000000000000040 + ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + 0000000000000000000000000000000000000000000000000000000000000007 + 6d61696e6e657400000000000000000000000000000000000000000000000000 + " + ) + .to_vec(), + Some(vec![ + "\"mainnet\"".to_string(), + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + .to_string(), + ]), + ), + ( + // cast calldata "createSelectFork(string)" "https://eth-mainnet.g.alchemy.com/v2/api_key" + "createSelectFork(string)", + hex!( + " + 98680034 + 0000000000000000000000000000000000000000000000000000000000000020 + 000000000000000000000000000000000000000000000000000000000000002c + 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f + 6d2f76322f6170695f6b65790000000000000000000000000000000000000000 + " + ) + .to_vec(), + Some(vec!["\"\"".to_string()]), + ), + ( + // cast calldata "createSelectFork(string)" "mainnet" + "createSelectFork(string)", + hex!( + " + 98680034 + 0000000000000000000000000000000000000000000000000000000000000020 + 0000000000000000000000000000000000000000000000000000000000000007 + 6d61696e6e657400000000000000000000000000000000000000000000000000 + " + ) + .to_vec(), + Some(vec!["\"mainnet\"".to_string()]), + ), + ( + // cast calldata "createSelectFork(string,uint256)" "https://eth-mainnet.g.alchemy.com/v2/api_key" 1 + "createSelectFork(string,uint256)", + hex!( + " + 71ee464d + 0000000000000000000000000000000000000000000000000000000000000040 + 0000000000000000000000000000000000000000000000000000000000000001 + 000000000000000000000000000000000000000000000000000000000000002c + 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f + 6d2f76322f6170695f6b65790000000000000000000000000000000000000000 + " + ) + .to_vec(), + Some(vec!["\"\"".to_string(), "1".to_string()]), + ), + ( + // cast calldata "createSelectFork(string,uint256)" "mainnet" 1 + "createSelectFork(string,uint256)", + hex!( + " + 71ee464d + 0000000000000000000000000000000000000000000000000000000000000040 + 0000000000000000000000000000000000000000000000000000000000000001 + 0000000000000000000000000000000000000000000000000000000000000007 + 6d61696e6e657400000000000000000000000000000000000000000000000000 + " + ) + .to_vec(), + Some(vec!["\"mainnet\"".to_string(), "1".to_string()]), + ), + ( + // cast calldata "createSelectFork(string,bytes32)" "https://eth-mainnet.g.alchemy.com/v2/api_key" 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + "createSelectFork(string,bytes32)", + hex!( + " + 84d52b7a + 0000000000000000000000000000000000000000000000000000000000000040 + ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + 000000000000000000000000000000000000000000000000000000000000002c + 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f + 6d2f76322f6170695f6b65790000000000000000000000000000000000000000 + " + ) + .to_vec(), + Some(vec![ + "\"\"".to_string(), + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + .to_string(), + ]), + ), + ( + // cast calldata "createSelectFork(string,bytes32)" "mainnet" + // 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + "createSelectFork(string,bytes32)", + hex!( + " + 84d52b7a + 0000000000000000000000000000000000000000000000000000000000000040 + ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + 0000000000000000000000000000000000000000000000000000000000000007 + 6d61696e6e657400000000000000000000000000000000000000000000000000 + " + ) + .to_vec(), + Some(vec![ + "\"mainnet\"".to_string(), + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + .to_string(), + ]), + ), + ( + // cast calldata "rpc(string,string,string)" "https://eth-mainnet.g.alchemy.com/v2/api_key" "eth_getBalance" "[\"0x551e7784778ef8e048e495df49f2614f84a4f1dc\",\"0x0\"]" + "rpc(string,string,string)", + hex!( + " + 0199a220 + 0000000000000000000000000000000000000000000000000000000000000060 + 00000000000000000000000000000000000000000000000000000000000000c0 + 0000000000000000000000000000000000000000000000000000000000000100 + 000000000000000000000000000000000000000000000000000000000000002c + 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f + 6d2f76322f6170695f6b65790000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000000e + 6574685f67657442616c616e6365000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000034 + 5b22307835353165373738343737386566386530343865343935646634396632 + 363134663834613466316463222c22307830225d000000000000000000000000 + " + ) + .to_vec(), + Some(vec![ + "\"\"".to_string(), + "\"eth_getBalance\"".to_string(), + "\"[\\\"0x551e7784778ef8e048e495df49f2614f84a4f1dc\\\",\\\"0x0\\\"]\"" + .to_string(), + ]), + ), + ( + // cast calldata "rpc(string,string,string)" "mainnet" "eth_getBalance" + // "[\"0x551e7784778ef8e048e495df49f2614f84a4f1dc\",\"0x0\"]" + "rpc(string,string,string)", + hex!( + " + 0199a220 + 0000000000000000000000000000000000000000000000000000000000000060 + 00000000000000000000000000000000000000000000000000000000000000a0 + 00000000000000000000000000000000000000000000000000000000000000e0 + 0000000000000000000000000000000000000000000000000000000000000007 + 6d61696e6e657400000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000000e + 6574685f67657442616c616e6365000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000034 + 5b22307835353165373738343737386566386530343865343935646634396632 + 363134663834613466316463222c22307830225d000000000000000000000000 + " + ) + .to_vec(), + Some(vec![ + "\"mainnet\"".to_string(), + "\"eth_getBalance\"".to_string(), + "\"[\\\"0x551e7784778ef8e048e495df49f2614f84a4f1dc\\\",\\\"0x0\\\"]\"" + .to_string(), + ]), + ), ]; // [function_signature, expected] @@ -733,6 +1021,10 @@ mod tests { // Should redact private key on output in all cases: ("createWallet(string)", Some("".to_string())), ("deriveKey(string,uint32)", Some("".to_string())), + // Should redact RPC URL if defined, except if referenced by an alias: + ("rpcUrl(string)", Some("".to_string())), + ("rpcUrls()", Some("".to_string())), + ("rpcUrlStructs()", Some("".to_string())), ]; for (function_signature, data, expected) in cheatcode_input_test_cases { From 5101a32b50a71741741730d351834cb190927b51 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 9 Oct 2024 16:50:49 +0200 Subject: [PATCH 080/100] test: relax pragmas (#9078) * test: relax pragmas * test: update rust tests too --- crates/forge/tests/cli/script.rs | 39 ++++++++++++------- crates/forge/tests/cli/test_cmd.rs | 3 -- crates/linking/src/lib.rs | 21 ++++++---- crates/test-utils/src/util.rs | 5 ++- testdata/default/cheats/Addr.t.sol | 2 +- .../default/cheats/ArbitraryStorage.t.sol | 2 +- testdata/default/cheats/Assert.t.sol | 2 +- testdata/default/cheats/Assume.t.sol | 2 +- testdata/default/cheats/Bank.t.sol | 2 +- testdata/default/cheats/Base64.t.sol | 2 +- testdata/default/cheats/Broadcast.t.sol | 2 +- .../cheats/BroadcastRawTransaction.t.sol | 2 +- testdata/default/cheats/ChainId.t.sol | 2 +- testdata/default/cheats/CloneAccount.t.sol | 2 +- testdata/default/cheats/Cool.t.sol | 2 +- testdata/default/cheats/CopyStorage.t.sol | 2 +- testdata/default/cheats/Deal.t.sol | 2 +- testdata/default/cheats/DeployCode.t.sol | 2 +- testdata/default/cheats/Derive.t.sol | 2 +- testdata/default/cheats/EnsNamehash.t.sol | 2 +- testdata/default/cheats/Env.t.sol | 2 +- testdata/default/cheats/Etch.t.sol | 2 +- testdata/default/cheats/ExpectCall.t.sol | 2 +- testdata/default/cheats/ExpectEmit.t.sol | 2 +- testdata/default/cheats/ExpectRevert.t.sol | 2 +- testdata/default/cheats/Fee.t.sol | 2 +- testdata/default/cheats/Ffi.t.sol | 2 +- testdata/default/cheats/Fork.t.sol | 2 +- testdata/default/cheats/Fork2.t.sol | 2 +- testdata/default/cheats/Fs.t.sol | 2 +- testdata/default/cheats/GasMetering.t.sol | 2 +- testdata/default/cheats/GasSnapshots.t.sol | 2 +- testdata/default/cheats/GetArtifactPath.t.sol | 2 +- .../default/cheats/GetBlockTimestamp.t.sol | 2 +- testdata/default/cheats/GetCode.t.sol | 2 +- testdata/default/cheats/GetDeployedCode.t.sol | 2 +- .../default/cheats/GetFoundryVersion.t.sol | 2 +- testdata/default/cheats/GetLabel.t.sol | 2 +- testdata/default/cheats/GetNonce.t.sol | 2 +- testdata/default/cheats/Json.t.sol | 2 +- testdata/default/cheats/Label.t.sol | 2 +- testdata/default/cheats/LastCallGas.t.sol | 2 +- testdata/default/cheats/Load.t.sol | 2 +- testdata/default/cheats/Mapping.t.sol | 2 +- testdata/default/cheats/MemSafety.t.sol | 2 +- testdata/default/cheats/MockCall.t.sol | 2 +- testdata/default/cheats/MockCalls.t.sol | 2 +- testdata/default/cheats/MockFunction.t.sol | 2 +- testdata/default/cheats/Nonce.t.sol | 2 +- testdata/default/cheats/Parse.t.sol | 2 +- testdata/default/cheats/Prank.t.sol | 2 +- testdata/default/cheats/Prevrandao.t.sol | 2 +- testdata/default/cheats/ProjectRoot.t.sol | 2 +- testdata/default/cheats/Prompt.t.sol | 2 +- testdata/default/cheats/RandomAddress.t.sol | 2 +- testdata/default/cheats/RandomBytes.t.sol | 2 +- .../default/cheats/RandomCheatcodes.t.sol | 2 +- testdata/default/cheats/RandomUint.t.sol | 2 +- testdata/default/cheats/ReadCallers.t.sol | 2 +- testdata/default/cheats/Record.t.sol | 2 +- .../cheats/RecordAccountAccesses.t.sol | 2 +- testdata/default/cheats/RecordLogs.t.sol | 2 +- testdata/default/cheats/Remember.t.sol | 2 +- testdata/default/cheats/ResetNonce.t.sol | 2 +- testdata/default/cheats/Roll.t.sol | 2 +- testdata/default/cheats/RpcUrls.t.sol | 2 +- testdata/default/cheats/SetBlockhash.t.sol | 2 +- testdata/default/cheats/SetNonce.t.sol | 2 +- testdata/default/cheats/SetNonceUnsafe.t.sol | 2 +- testdata/default/cheats/Setup.t.sol | 2 +- testdata/default/cheats/Sign.t.sol | 2 +- testdata/default/cheats/SignP256.t.sol | 2 +- testdata/default/cheats/Skip.t.sol | 2 +- testdata/default/cheats/Sleep.t.sol | 2 +- testdata/default/cheats/StateSnapshots.t.sol | 2 +- testdata/default/cheats/Store.t.sol | 2 +- testdata/default/cheats/StringUtils.t.sol | 2 +- testdata/default/cheats/ToString.t.sol | 2 +- testdata/default/cheats/Toml.t.sol | 2 +- testdata/default/cheats/Travel.t.sol | 2 +- testdata/default/cheats/UnixTime.t.sol | 2 +- testdata/default/cheats/Wallet.t.sol | 2 +- testdata/default/cheats/Warp.t.sol | 2 +- testdata/default/cheats/dumpState.t.sol | 2 +- testdata/default/cheats/getBlockNumber.t.sol | 2 +- testdata/default/cheats/loadAllocs.t.sol | 2 +- testdata/default/core/Abstract.t.sol | 2 +- .../default/core/BadSigAfterInvariant.t.sol | 2 +- .../default/core/ContractEnvironment.t.sol | 2 +- testdata/default/core/DSStyle.t.sol | 2 +- testdata/default/core/FailingSetup.t.sol | 2 +- .../core/FailingTestAfterFailedSetup.t.sol | 2 +- testdata/default/core/LegacyAssertions.t.sol | 2 +- .../default/core/MultipleAfterInvariant.t.sol | 2 +- testdata/default/core/MultipleSetup.t.sol | 2 +- testdata/default/core/PaymentFailure.t.sol | 2 +- testdata/default/core/Reverting.t.sol | 2 +- testdata/default/core/SetupConsistency.t.sol | 2 +- testdata/default/fork/ForkSame_1.t.sol | 2 +- testdata/default/fork/ForkSame_2.t.sol | 2 +- testdata/default/fork/Transact.t.sol | 2 +- testdata/default/fs/Default.t.sol | 2 +- testdata/default/fs/Disabled.t.sol | 2 +- testdata/default/fuzz/Fuzz.t.sol | 2 +- testdata/default/fuzz/FuzzCollection.t.sol | 2 +- .../default/fuzz/FuzzFailurePersist.t.sol | 2 +- testdata/default/fuzz/FuzzInt.t.sol | 2 +- testdata/default/fuzz/FuzzPositive.t.sol | 2 +- testdata/default/fuzz/FuzzUint.t.sol | 2 +- .../common/InvariantCalldataDictionary.t.sol | 2 +- .../common/InvariantInnerContract.t.sol | 2 +- .../common/InvariantPreserveState.t.sol | 2 +- .../common/InvariantReentrancy.t.sol | 2 +- .../invariant/common/InvariantTest1.t.sol | 2 +- .../invariant/target/ExcludeContracts.t.sol | 2 +- .../invariant/target/ExcludeSelectors.t.sol | 2 +- .../invariant/target/ExcludeSenders.t.sol | 2 +- .../target/FuzzedTargetContracts.t.sol | 2 +- .../invariant/target/TargetContracts.t.sol | 2 +- .../invariant/target/TargetInterfaces.t.sol | 2 +- .../invariant/target/TargetSelectors.t.sol | 2 +- .../fuzz/invariant/target/TargetSenders.t.sol | 2 +- .../targetAbi/ExcludeArtifacts.t.sol | 2 +- .../targetAbi/TargetArtifactSelectors.t.sol | 2 +- .../targetAbi/TargetArtifactSelectors2.t.sol | 2 +- .../invariant/targetAbi/TargetArtifacts.t.sol | 2 +- testdata/default/inline/FuzzInlineConf.t.sol | 2 +- .../default/inline/InvariantInlineConf.t.sol | 2 +- testdata/default/linking/cycle/Cycle.t.sol | 2 +- .../default/linking/duplicate/Duplicate.t.sol | 2 +- testdata/default/linking/nested/Nested.t.sol | 2 +- testdata/default/linking/simple/Simple.t.sol | 2 +- testdata/default/logs/DebugLogs.t.sol | 2 +- testdata/default/logs/HardhatLogs.t.sol | 2 +- testdata/default/logs/console.sol | 2 +- testdata/default/repros/Issue1543.t.sol | 2 +- testdata/default/repros/Issue2623.t.sol | 2 +- testdata/default/repros/Issue2629.t.sol | 2 +- testdata/default/repros/Issue2723.t.sol | 2 +- testdata/default/repros/Issue2898.t.sol | 2 +- testdata/default/repros/Issue2956.t.sol | 2 +- testdata/default/repros/Issue2984.t.sol | 2 +- testdata/default/repros/Issue3055.t.sol | 2 +- testdata/default/repros/Issue3077.t.sol | 2 +- testdata/default/repros/Issue3110.t.sol | 2 +- testdata/default/repros/Issue3119.t.sol | 2 +- testdata/default/repros/Issue3189.t.sol | 2 +- testdata/default/repros/Issue3190.t.sol | 2 +- testdata/default/repros/Issue3192.t.sol | 2 +- testdata/default/repros/Issue3220.t.sol | 2 +- testdata/default/repros/Issue3221.t.sol | 2 +- testdata/default/repros/Issue3223.t.sol | 2 +- testdata/default/repros/Issue3347.t.sol | 2 +- testdata/default/repros/Issue3596.t.sol | 2 +- testdata/default/repros/Issue3653.t.sol | 2 +- testdata/default/repros/Issue3661.t.sol | 2 +- testdata/default/repros/Issue3674.t.sol | 2 +- testdata/default/repros/Issue3685.t.sol | 2 +- testdata/default/repros/Issue3703.t.sol | 2 +- testdata/default/repros/Issue3708.t.sol | 2 +- testdata/default/repros/Issue3753.t.sol | 2 +- testdata/default/repros/Issue3792.t.sol | 2 +- testdata/default/repros/Issue4402.t.sol | 2 +- testdata/default/repros/Issue4586.t.sol | 2 +- testdata/default/repros/Issue4630.t.sol | 2 +- testdata/default/repros/Issue4640.t.sol | 2 +- testdata/default/repros/Issue5038.t.sol | 2 +- testdata/default/repros/Issue5739.t.sol | 2 +- testdata/default/repros/Issue5808.t.sol | 2 +- testdata/default/repros/Issue5929.t.sol | 2 +- testdata/default/repros/Issue5935.t.sol | 2 +- testdata/default/repros/Issue5948.t.sol | 2 +- testdata/default/repros/Issue6006.t.sol | 2 +- testdata/default/repros/Issue6032.t.sol | 2 +- testdata/default/repros/Issue6070.t.sol | 2 +- testdata/default/repros/Issue6115.t.sol | 2 +- testdata/default/repros/Issue6170.t.sol | 2 +- testdata/default/repros/Issue6180.t.sol | 2 +- testdata/default/repros/Issue6293.t.sol | 2 +- testdata/default/repros/Issue6355.t.sol | 2 +- testdata/default/repros/Issue6437.t.sol | 2 +- testdata/default/repros/Issue6501.t.sol | 2 +- testdata/default/repros/Issue6538.t.sol | 2 +- testdata/default/repros/Issue6554.t.sol | 2 +- testdata/default/repros/Issue6616.t.sol | 2 +- testdata/default/repros/Issue6634.t.sol | 2 +- testdata/default/repros/Issue6643.t.sol | 2 +- testdata/default/repros/Issue6759.t.sol | 2 +- testdata/default/repros/Issue6966.t.sol | 2 +- testdata/default/repros/Issue7457.t.sol | 2 +- testdata/default/repros/Issue7481.t.sol | 2 +- testdata/default/repros/Issue8004.t.sol | 2 +- testdata/default/repros/Issue8006.t.sol | 2 +- testdata/default/repros/Issue8168.t.sol | 2 +- testdata/default/repros/Issue8277.t.sol | 2 +- testdata/default/repros/Issue8287.t.sol | 2 +- testdata/default/repros/Issue8383.t.sol | 2 +- testdata/default/repros/Issue8971.t.sol | 2 +- testdata/default/script/deploy.sol | 2 +- testdata/default/spec/ShanghaiCompat.t.sol | 2 +- .../default/trace/ConflictingSignatures.t.sol | 2 +- testdata/default/trace/Trace.t.sol | 2 +- .../fixtures/GetCode/UnlinkedContract.sol | 2 +- testdata/fixtures/GetCode/WorkingContract.sol | 2 +- testdata/multi-version/cheats/GetCode.t.sol | 2 +- testdata/multi-version/cheats/GetCode17.t.sol | 2 +- 206 files changed, 244 insertions(+), 228 deletions(-) diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 826d66282..e8f1466b9 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -3,7 +3,11 @@ use crate::constants::TEMPLATE_CONTRACT; use alloy_primitives::{hex, Address, Bytes}; use anvil::{spawn, NodeConfig}; -use foundry_test_utils::{rpc, ScriptOutcome, ScriptTester}; +use foundry_test_utils::{ + rpc, + util::{OTHER_SOLC_VERSION, SOLC_VERSION}, + ScriptOutcome, ScriptTester, +}; use regex::Regex; use serde_json::Value; use std::{env, path::PathBuf, str::FromStr}; @@ -1520,36 +1524,45 @@ forgetest_async!(can_detect_contract_when_multiple_versions, |prj, cmd| { prj.add_script( "A.sol", - r#"pragma solidity 0.8.20; + &format!( + r#" +pragma solidity {SOLC_VERSION}; import "./B.sol"; -contract ScriptA {} -"#, +contract ScriptA {{}} +"# + ), ) .unwrap(); prj.add_script( "B.sol", - r#"pragma solidity >=0.8.5 <=0.8.20; + &format!( + r#" +pragma solidity >={OTHER_SOLC_VERSION} <={SOLC_VERSION}; import 'forge-std/Script.sol'; -contract ScriptB is Script { - function run() external { +contract ScriptB is Script {{ + function run() external {{ vm.broadcast(); address(0).call(""); - } -} -"#, + }} +}} +"# + ), ) .unwrap(); prj.add_script( "C.sol", - r#"pragma solidity 0.8.5; + &format!( + r#" +pragma solidity {OTHER_SOLC_VERSION}; import "./B.sol"; -contract ScriptC {} -"#, +contract ScriptC {{}} +"# + ), ) .unwrap(); diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 30c7f9deb..ea3ce1911 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -275,8 +275,6 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) }); const SIMPLE_CONTRACT: &str = r#" -pragma solidity 0.8.18; - import "./test.sol"; import "./console.sol"; @@ -650,7 +648,6 @@ forgetest_init!(can_test_transient_storage_with_isolation, |prj, cmd| { prj.add_test( "Contract.t.sol", r#" -pragma solidity ^0.8.24; import {Test} from "forge-std/Test.sol"; contract TransientTester { diff --git a/crates/linking/src/lib.rs b/crates/linking/src/lib.rs index d0959ddb7..e44ee7748 100644 --- a/crates/linking/src/lib.rs +++ b/crates/linking/src/lib.rs @@ -284,7 +284,11 @@ impl<'a> Linker<'a> { mod tests { use super::*; use alloy_primitives::{fixed_bytes, map::HashMap}; - use foundry_compilers::{Project, ProjectCompileOutput, ProjectPathsConfig}; + use foundry_compilers::{ + multi::MultiCompiler, + solc::{Solc, SolcCompiler}, + Project, ProjectCompileOutput, ProjectPathsConfig, + }; struct LinkerTest { project: Project, @@ -303,11 +307,12 @@ mod tests { .build() .unwrap(); + let solc = Solc::find_or_install(&Version::new(0, 8, 18)).unwrap(); let project = Project::builder() .paths(paths) .ephemeral() .no_artifacts() - .build(Default::default()) + .build(MultiCompiler { solc: Some(SolcCompiler::Specific(solc)), vyper: None }) .unwrap(); let mut output = project.compile().unwrap(); @@ -393,7 +398,7 @@ mod tests { for (dep_identifier, address) in assertions { let (file, name) = dep_identifier.split_once(':').unwrap(); if let Some(lib_address) = - libraries.libs.get(&PathBuf::from(file)).and_then(|libs| libs.get(name)) + libraries.libs.get(Path::new(file)).and_then(|libs| libs.get(name)) { assert_eq!( *lib_address, @@ -637,7 +642,7 @@ mod tests { "default/linking/nested/Nested.t.sol:NestedLib".to_string(), vec![( "default/linking/nested/Nested.t.sol:Lib".to_string(), - Address::from_str("0xCD3864eB2D88521a5477691EE589D9994b796834").unwrap(), + Address::from_str("0xddb1Cd2497000DAeA687CEa3dc34Af44084BEa74").unwrap(), )], ) .assert_dependencies( @@ -647,12 +652,12 @@ mod tests { // have the same address and nonce. ( "default/linking/nested/Nested.t.sol:Lib".to_string(), - Address::from_str("0xCD3864eB2D88521a5477691EE589D9994b796834") + Address::from_str("0xddb1Cd2497000DAeA687CEa3dc34Af44084BEa74") .unwrap(), ), ( "default/linking/nested/Nested.t.sol:NestedLib".to_string(), - Address::from_str("0x023d9a6bfA39c45997572dC4F87b3E2713b6EBa4") + Address::from_str("0xfebE2F30641170642f317Ff6F644Cee60E7Ac369") .unwrap(), ), ], @@ -662,12 +667,12 @@ mod tests { vec![ ( "default/linking/nested/Nested.t.sol:Lib".to_string(), - Address::from_str("0xCD3864eB2D88521a5477691EE589D9994b796834") + Address::from_str("0xddb1Cd2497000DAeA687CEa3dc34Af44084BEa74") .unwrap(), ), ( "default/linking/nested/Nested.t.sol:NestedLib".to_string(), - Address::from_str("0x023d9a6bfA39c45997572dC4F87b3E2713b6EBa4") + Address::from_str("0xfebE2F30641170642f317Ff6F644Cee60E7Ac369") .unwrap(), ), ], diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 70db0a407..11757978a 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -49,8 +49,9 @@ static NEXT_ID: AtomicUsize = AtomicUsize::new(0); /// The default Solc version used when compiling tests. pub const SOLC_VERSION: &str = "0.8.27"; -/// Another Solc version used when compiling tests. Necessary to avoid downloading multiple -/// versions. +/// Another Solc version used when compiling tests. +/// +/// Necessary to avoid downloading multiple versions. pub const OTHER_SOLC_VERSION: &str = "0.8.26"; /// External test builder diff --git a/testdata/default/cheats/Addr.t.sol b/testdata/default/cheats/Addr.t.sol index 432c52e69..3f791583d 100644 --- a/testdata/default/cheats/Addr.t.sol +++ b/testdata/default/cheats/Addr.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/ArbitraryStorage.t.sol b/testdata/default/cheats/ArbitraryStorage.t.sol index f763ab7af..afac01087 100644 --- a/testdata/default/cheats/ArbitraryStorage.t.sol +++ b/testdata/default/cheats/ArbitraryStorage.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Assert.t.sol b/testdata/default/cheats/Assert.t.sol index 971bb5e27..d6765967c 100644 --- a/testdata/default/cheats/Assert.t.sol +++ b/testdata/default/cheats/Assert.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Assume.t.sol b/testdata/default/cheats/Assume.t.sol index de11d6644..14ed341c9 100644 --- a/testdata/default/cheats/Assume.t.sol +++ b/testdata/default/cheats/Assume.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Bank.t.sol b/testdata/default/cheats/Bank.t.sol index a02fe1667..166fbb16a 100644 --- a/testdata/default/cheats/Bank.t.sol +++ b/testdata/default/cheats/Bank.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Base64.t.sol b/testdata/default/cheats/Base64.t.sol index 0d2249395..fad7bbf4f 100644 --- a/testdata/default/cheats/Base64.t.sol +++ b/testdata/default/cheats/Base64.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Broadcast.t.sol b/testdata/default/cheats/Broadcast.t.sol index 6a099dc6e..bca8cc2ee 100644 --- a/testdata/default/cheats/Broadcast.t.sol +++ b/testdata/default/cheats/Broadcast.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/BroadcastRawTransaction.t.sol b/testdata/default/cheats/BroadcastRawTransaction.t.sol index 43e4ff632..5bd400a9f 100644 --- a/testdata/default/cheats/BroadcastRawTransaction.t.sol +++ b/testdata/default/cheats/BroadcastRawTransaction.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/ChainId.t.sol b/testdata/default/cheats/ChainId.t.sol index aa8fa0a13..31a6e5cdf 100644 --- a/testdata/default/cheats/ChainId.t.sol +++ b/testdata/default/cheats/ChainId.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/CloneAccount.t.sol b/testdata/default/cheats/CloneAccount.t.sol index 5342a92d3..d584c747c 100644 --- a/testdata/default/cheats/CloneAccount.t.sol +++ b/testdata/default/cheats/CloneAccount.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Cool.t.sol b/testdata/default/cheats/Cool.t.sol index 82212f1b1..d0750bebf 100644 --- a/testdata/default/cheats/Cool.t.sol +++ b/testdata/default/cheats/Cool.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "lib/ds-test/src/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/CopyStorage.t.sol b/testdata/default/cheats/CopyStorage.t.sol index 3ca5d8dab..e9195949e 100644 --- a/testdata/default/cheats/CopyStorage.t.sol +++ b/testdata/default/cheats/CopyStorage.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Deal.t.sol b/testdata/default/cheats/Deal.t.sol index ac4776435..a46d9e714 100644 --- a/testdata/default/cheats/Deal.t.sol +++ b/testdata/default/cheats/Deal.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/DeployCode.t.sol b/testdata/default/cheats/DeployCode.t.sol index 330e82651..edf4c78e6 100644 --- a/testdata/default/cheats/DeployCode.t.sol +++ b/testdata/default/cheats/DeployCode.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Derive.t.sol b/testdata/default/cheats/Derive.t.sol index fb1443333..c27456c6e 100644 --- a/testdata/default/cheats/Derive.t.sol +++ b/testdata/default/cheats/Derive.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/EnsNamehash.t.sol b/testdata/default/cheats/EnsNamehash.t.sol index 2d66beea4..965d50500 100644 --- a/testdata/default/cheats/EnsNamehash.t.sol +++ b/testdata/default/cheats/EnsNamehash.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Env.t.sol b/testdata/default/cheats/Env.t.sol index e325df2fa..7edb35dff 100644 --- a/testdata/default/cheats/Env.t.sol +++ b/testdata/default/cheats/Env.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Etch.t.sol b/testdata/default/cheats/Etch.t.sol index fbd0e62b9..33eaaf44e 100644 --- a/testdata/default/cheats/Etch.t.sol +++ b/testdata/default/cheats/Etch.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/ExpectCall.t.sol b/testdata/default/cheats/ExpectCall.t.sol index 7d757101a..f2624fd2e 100644 --- a/testdata/default/cheats/ExpectCall.t.sol +++ b/testdata/default/cheats/ExpectCall.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/ExpectEmit.t.sol b/testdata/default/cheats/ExpectEmit.t.sol index cad184355..b8fe5e458 100644 --- a/testdata/default/cheats/ExpectEmit.t.sol +++ b/testdata/default/cheats/ExpectEmit.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/ExpectRevert.t.sol b/testdata/default/cheats/ExpectRevert.t.sol index 60137adfa..18a90bac6 100644 --- a/testdata/default/cheats/ExpectRevert.t.sol +++ b/testdata/default/cheats/ExpectRevert.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Fee.t.sol b/testdata/default/cheats/Fee.t.sol index ad93fed6a..d258eaf13 100644 --- a/testdata/default/cheats/Fee.t.sol +++ b/testdata/default/cheats/Fee.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Ffi.t.sol b/testdata/default/cheats/Ffi.t.sol index 897783d7e..23ac54e6a 100644 --- a/testdata/default/cheats/Ffi.t.sol +++ b/testdata/default/cheats/Ffi.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Fork.t.sol b/testdata/default/cheats/Fork.t.sol index 873fbec13..2f2e627de 100644 --- a/testdata/default/cheats/Fork.t.sol +++ b/testdata/default/cheats/Fork.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Fork2.t.sol b/testdata/default/cheats/Fork2.t.sol index 3e8f68a6c..7b6b42759 100644 --- a/testdata/default/cheats/Fork2.t.sol +++ b/testdata/default/cheats/Fork2.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "../logs/console.sol"; diff --git a/testdata/default/cheats/Fs.t.sol b/testdata/default/cheats/Fs.t.sol index cb407641e..b48825259 100644 --- a/testdata/default/cheats/Fs.t.sol +++ b/testdata/default/cheats/Fs.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/GasMetering.t.sol b/testdata/default/cheats/GasMetering.t.sol index e439e05be..3cb105d23 100644 --- a/testdata/default/cheats/GasMetering.t.sol +++ b/testdata/default/cheats/GasMetering.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/GasSnapshots.t.sol b/testdata/default/cheats/GasSnapshots.t.sol index 1e64a073d..98abfa3e4 100644 --- a/testdata/default/cheats/GasSnapshots.t.sol +++ b/testdata/default/cheats/GasSnapshots.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/GetArtifactPath.t.sol b/testdata/default/cheats/GetArtifactPath.t.sol index d92d41e01..538c2e6c9 100644 --- a/testdata/default/cheats/GetArtifactPath.t.sol +++ b/testdata/default/cheats/GetArtifactPath.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity =0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/GetBlockTimestamp.t.sol b/testdata/default/cheats/GetBlockTimestamp.t.sol index 383bfa8b0..edeaa0de7 100644 --- a/testdata/default/cheats/GetBlockTimestamp.t.sol +++ b/testdata/default/cheats/GetBlockTimestamp.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/GetCode.t.sol b/testdata/default/cheats/GetCode.t.sol index b155a1873..b258b6271 100644 --- a/testdata/default/cheats/GetCode.t.sol +++ b/testdata/default/cheats/GetCode.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity =0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/GetDeployedCode.t.sol b/testdata/default/cheats/GetDeployedCode.t.sol index 220e3f3c8..295d2ae8f 100644 --- a/testdata/default/cheats/GetDeployedCode.t.sol +++ b/testdata/default/cheats/GetDeployedCode.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity =0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/GetFoundryVersion.t.sol b/testdata/default/cheats/GetFoundryVersion.t.sol index 6f850561d..34d9a74bb 100644 --- a/testdata/default/cheats/GetFoundryVersion.t.sol +++ b/testdata/default/cheats/GetFoundryVersion.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/GetLabel.t.sol b/testdata/default/cheats/GetLabel.t.sol index dcbe0812c..c5a5638d3 100644 --- a/testdata/default/cheats/GetLabel.t.sol +++ b/testdata/default/cheats/GetLabel.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity >=0.8.0; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/GetNonce.t.sol b/testdata/default/cheats/GetNonce.t.sol index 7eb53f205..d4043a599 100644 --- a/testdata/default/cheats/GetNonce.t.sol +++ b/testdata/default/cheats/GetNonce.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Json.t.sol b/testdata/default/cheats/Json.t.sol index 0604ef907..ff1b62c6e 100644 --- a/testdata/default/cheats/Json.t.sol +++ b/testdata/default/cheats/Json.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Label.t.sol b/testdata/default/cheats/Label.t.sol index d554f637d..4ff5d3860 100644 --- a/testdata/default/cheats/Label.t.sol +++ b/testdata/default/cheats/Label.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/LastCallGas.t.sol b/testdata/default/cheats/LastCallGas.t.sol index 0f5b65e35..bc7ac4263 100644 --- a/testdata/default/cheats/LastCallGas.t.sol +++ b/testdata/default/cheats/LastCallGas.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Load.t.sol b/testdata/default/cheats/Load.t.sol index 0ed1cbbc2..06f4b5bd5 100644 --- a/testdata/default/cheats/Load.t.sol +++ b/testdata/default/cheats/Load.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Mapping.t.sol b/testdata/default/cheats/Mapping.t.sol index 6cd141fa8..82477150a 100644 --- a/testdata/default/cheats/Mapping.t.sol +++ b/testdata/default/cheats/Mapping.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity >=0.8.0; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/MemSafety.t.sol b/testdata/default/cheats/MemSafety.t.sol index 096d8ac47..a5c0a5a4f 100644 --- a/testdata/default/cheats/MemSafety.t.sol +++ b/testdata/default/cheats/MemSafety.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/MockCall.t.sol b/testdata/default/cheats/MockCall.t.sol index df7ee89c7..940e5e78c 100644 --- a/testdata/default/cheats/MockCall.t.sol +++ b/testdata/default/cheats/MockCall.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/MockCalls.t.sol b/testdata/default/cheats/MockCalls.t.sol index 13e3cb78c..2bd4d8bd9 100644 --- a/testdata/default/cheats/MockCalls.t.sol +++ b/testdata/default/cheats/MockCalls.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/MockFunction.t.sol b/testdata/default/cheats/MockFunction.t.sol index 9cf1004ca..6d670024b 100644 --- a/testdata/default/cheats/MockFunction.t.sol +++ b/testdata/default/cheats/MockFunction.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Nonce.t.sol b/testdata/default/cheats/Nonce.t.sol index 5dd8b0c6a..312c2b4d7 100644 --- a/testdata/default/cheats/Nonce.t.sol +++ b/testdata/default/cheats/Nonce.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Parse.t.sol b/testdata/default/cheats/Parse.t.sol index 71d49af6f..65e7561d1 100644 --- a/testdata/default/cheats/Parse.t.sol +++ b/testdata/default/cheats/Parse.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Prank.t.sol b/testdata/default/cheats/Prank.t.sol index f7dd9b714..d833c0513 100644 --- a/testdata/default/cheats/Prank.t.sol +++ b/testdata/default/cheats/Prank.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Prevrandao.t.sol b/testdata/default/cheats/Prevrandao.t.sol index 75f2b2cc1..aab8e326c 100644 --- a/testdata/default/cheats/Prevrandao.t.sol +++ b/testdata/default/cheats/Prevrandao.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/ProjectRoot.t.sol b/testdata/default/cheats/ProjectRoot.t.sol index 31e68e105..cff3d8375 100644 --- a/testdata/default/cheats/ProjectRoot.t.sol +++ b/testdata/default/cheats/ProjectRoot.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Prompt.t.sol b/testdata/default/cheats/Prompt.t.sol index 33f83fea8..2e623a28e 100644 --- a/testdata/default/cheats/Prompt.t.sol +++ b/testdata/default/cheats/Prompt.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/RandomAddress.t.sol b/testdata/default/cheats/RandomAddress.t.sol index 74c5e2040..61510ed4e 100644 --- a/testdata/default/cheats/RandomAddress.t.sol +++ b/testdata/default/cheats/RandomAddress.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/RandomBytes.t.sol b/testdata/default/cheats/RandomBytes.t.sol index 8d298f43f..dbc03a6cc 100644 --- a/testdata/default/cheats/RandomBytes.t.sol +++ b/testdata/default/cheats/RandomBytes.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/RandomCheatcodes.t.sol b/testdata/default/cheats/RandomCheatcodes.t.sol index 4f7d559fa..beeee9862 100644 --- a/testdata/default/cheats/RandomCheatcodes.t.sol +++ b/testdata/default/cheats/RandomCheatcodes.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/RandomUint.t.sol b/testdata/default/cheats/RandomUint.t.sol index 68a65dc0f..c0021030d 100644 --- a/testdata/default/cheats/RandomUint.t.sol +++ b/testdata/default/cheats/RandomUint.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/ReadCallers.t.sol b/testdata/default/cheats/ReadCallers.t.sol index e0da8ed0d..dbd198a2d 100644 --- a/testdata/default/cheats/ReadCallers.t.sol +++ b/testdata/default/cheats/ReadCallers.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity >=0.8.0; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Record.t.sol b/testdata/default/cheats/Record.t.sol index 152a5ccb5..c2907ebb8 100644 --- a/testdata/default/cheats/Record.t.sol +++ b/testdata/default/cheats/Record.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/RecordAccountAccesses.t.sol b/testdata/default/cheats/RecordAccountAccesses.t.sol index a0aa2cb53..98b5843b2 100644 --- a/testdata/default/cheats/RecordAccountAccesses.t.sol +++ b/testdata/default/cheats/RecordAccountAccesses.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Unlicense -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/RecordLogs.t.sol b/testdata/default/cheats/RecordLogs.t.sol index 728acdb9b..14ca8dde3 100644 --- a/testdata/default/cheats/RecordLogs.t.sol +++ b/testdata/default/cheats/RecordLogs.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Remember.t.sol b/testdata/default/cheats/Remember.t.sol index b5487c369..b8dbe7e38 100644 --- a/testdata/default/cheats/Remember.t.sol +++ b/testdata/default/cheats/Remember.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/ResetNonce.t.sol b/testdata/default/cheats/ResetNonce.t.sol index 901433609..d8c911587 100644 --- a/testdata/default/cheats/ResetNonce.t.sol +++ b/testdata/default/cheats/ResetNonce.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity >=0.8.0; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Roll.t.sol b/testdata/default/cheats/Roll.t.sol index 820cd9887..87f909cdd 100644 --- a/testdata/default/cheats/Roll.t.sol +++ b/testdata/default/cheats/Roll.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/RpcUrls.t.sol b/testdata/default/cheats/RpcUrls.t.sol index f7b980860..7976fa572 100644 --- a/testdata/default/cheats/RpcUrls.t.sol +++ b/testdata/default/cheats/RpcUrls.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/SetBlockhash.t.sol b/testdata/default/cheats/SetBlockhash.t.sol index f6c2af5f6..1274620df 100644 --- a/testdata/default/cheats/SetBlockhash.t.sol +++ b/testdata/default/cheats/SetBlockhash.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/SetNonce.t.sol b/testdata/default/cheats/SetNonce.t.sol index 7f2e419b9..83e3830ab 100644 --- a/testdata/default/cheats/SetNonce.t.sol +++ b/testdata/default/cheats/SetNonce.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/SetNonceUnsafe.t.sol b/testdata/default/cheats/SetNonceUnsafe.t.sol index 723f66ae2..0caf2b4ce 100644 --- a/testdata/default/cheats/SetNonceUnsafe.t.sol +++ b/testdata/default/cheats/SetNonceUnsafe.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Setup.t.sol b/testdata/default/cheats/Setup.t.sol index d694fb2c1..4d6e5954b 100644 --- a/testdata/default/cheats/Setup.t.sol +++ b/testdata/default/cheats/Setup.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Sign.t.sol b/testdata/default/cheats/Sign.t.sol index a257d6291..937ebc00a 100644 --- a/testdata/default/cheats/Sign.t.sol +++ b/testdata/default/cheats/Sign.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/SignP256.t.sol b/testdata/default/cheats/SignP256.t.sol index f1b62fe78..b92588ce9 100644 --- a/testdata/default/cheats/SignP256.t.sol +++ b/testdata/default/cheats/SignP256.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Skip.t.sol b/testdata/default/cheats/Skip.t.sol index fb2deadb4..e2b0fc181 100644 --- a/testdata/default/cheats/Skip.t.sol +++ b/testdata/default/cheats/Skip.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Sleep.t.sol b/testdata/default/cheats/Sleep.t.sol index 448d34668..7af548e74 100644 --- a/testdata/default/cheats/Sleep.t.sol +++ b/testdata/default/cheats/Sleep.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/StateSnapshots.t.sol b/testdata/default/cheats/StateSnapshots.t.sol index 92ee38d26..8751a0409 100644 --- a/testdata/default/cheats/StateSnapshots.t.sol +++ b/testdata/default/cheats/StateSnapshots.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Store.t.sol b/testdata/default/cheats/Store.t.sol index 5159a4ab8..9a1ce6101 100644 --- a/testdata/default/cheats/Store.t.sol +++ b/testdata/default/cheats/Store.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/StringUtils.t.sol b/testdata/default/cheats/StringUtils.t.sol index 136164a41..b65346a7a 100644 --- a/testdata/default/cheats/StringUtils.t.sol +++ b/testdata/default/cheats/StringUtils.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/ToString.t.sol b/testdata/default/cheats/ToString.t.sol index 835c85242..f19110e3e 100644 --- a/testdata/default/cheats/ToString.t.sol +++ b/testdata/default/cheats/ToString.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Toml.t.sol b/testdata/default/cheats/Toml.t.sol index 4b1666d4f..5f0ef5b43 100644 --- a/testdata/default/cheats/Toml.t.sol +++ b/testdata/default/cheats/Toml.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Travel.t.sol b/testdata/default/cheats/Travel.t.sol index 733559b29..b46d2e7ad 100644 --- a/testdata/default/cheats/Travel.t.sol +++ b/testdata/default/cheats/Travel.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/UnixTime.t.sol b/testdata/default/cheats/UnixTime.t.sol index a6b683967..29d86699f 100644 --- a/testdata/default/cheats/UnixTime.t.sol +++ b/testdata/default/cheats/UnixTime.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Wallet.t.sol b/testdata/default/cheats/Wallet.t.sol index 5a7035876..d061b55ae 100644 --- a/testdata/default/cheats/Wallet.t.sol +++ b/testdata/default/cheats/Wallet.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/Warp.t.sol b/testdata/default/cheats/Warp.t.sol index 01ebc8e89..42f373c61 100644 --- a/testdata/default/cheats/Warp.t.sol +++ b/testdata/default/cheats/Warp.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/dumpState.t.sol b/testdata/default/cheats/dumpState.t.sol index 74ebd3071..8a8675ca5 100644 --- a/testdata/default/cheats/dumpState.t.sol +++ b/testdata/default/cheats/dumpState.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/getBlockNumber.t.sol b/testdata/default/cheats/getBlockNumber.t.sol index c874e5e2f..ebf240dd8 100644 --- a/testdata/default/cheats/getBlockNumber.t.sol +++ b/testdata/default/cheats/getBlockNumber.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/cheats/loadAllocs.t.sol b/testdata/default/cheats/loadAllocs.t.sol index a4b72af6b..94ce6804c 100644 --- a/testdata/default/cheats/loadAllocs.t.sol +++ b/testdata/default/cheats/loadAllocs.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/core/Abstract.t.sol b/testdata/default/core/Abstract.t.sol index fee06bbe6..d04d0ff77 100644 --- a/testdata/default/core/Abstract.t.sol +++ b/testdata/default/core/Abstract.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; contract TestFixture { function something() public pure returns (string memory) { diff --git a/testdata/default/core/BadSigAfterInvariant.t.sol b/testdata/default/core/BadSigAfterInvariant.t.sol index 6d303b04b..7b485e24f 100644 --- a/testdata/default/core/BadSigAfterInvariant.t.sol +++ b/testdata/default/core/BadSigAfterInvariant.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/core/ContractEnvironment.t.sol b/testdata/default/core/ContractEnvironment.t.sol index cc5facd92..452fa8802 100644 --- a/testdata/default/core/ContractEnvironment.t.sol +++ b/testdata/default/core/ContractEnvironment.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/core/DSStyle.t.sol b/testdata/default/core/DSStyle.t.sol index 400b707b6..1eaf83969 100644 --- a/testdata/default/core/DSStyle.t.sol +++ b/testdata/default/core/DSStyle.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/core/FailingSetup.t.sol b/testdata/default/core/FailingSetup.t.sol index 37a3b9ac2..d5e24e131 100644 --- a/testdata/default/core/FailingSetup.t.sol +++ b/testdata/default/core/FailingSetup.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/core/FailingTestAfterFailedSetup.t.sol b/testdata/default/core/FailingTestAfterFailedSetup.t.sol index eeb5c207e..c56f4ba5d 100644 --- a/testdata/default/core/FailingTestAfterFailedSetup.t.sol +++ b/testdata/default/core/FailingTestAfterFailedSetup.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/core/LegacyAssertions.t.sol b/testdata/default/core/LegacyAssertions.t.sol index 9bbc56e8e..c35a63417 100644 --- a/testdata/default/core/LegacyAssertions.t.sol +++ b/testdata/default/core/LegacyAssertions.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/core/MultipleAfterInvariant.t.sol b/testdata/default/core/MultipleAfterInvariant.t.sol index 446e76cbb..b23b0996a 100644 --- a/testdata/default/core/MultipleAfterInvariant.t.sol +++ b/testdata/default/core/MultipleAfterInvariant.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/core/MultipleSetup.t.sol b/testdata/default/core/MultipleSetup.t.sol index 412b3fb4c..73cbaf1a9 100644 --- a/testdata/default/core/MultipleSetup.t.sol +++ b/testdata/default/core/MultipleSetup.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/core/PaymentFailure.t.sol b/testdata/default/core/PaymentFailure.t.sol index d4751b2d5..52c42fd37 100644 --- a/testdata/default/core/PaymentFailure.t.sol +++ b/testdata/default/core/PaymentFailure.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/core/Reverting.t.sol b/testdata/default/core/Reverting.t.sol index 36fdd99bc..91022e6ad 100644 --- a/testdata/default/core/Reverting.t.sol +++ b/testdata/default/core/Reverting.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; contract RevertingTest { function testFailRevert() public pure { diff --git a/testdata/default/core/SetupConsistency.t.sol b/testdata/default/core/SetupConsistency.t.sol index dfc6a9ab4..08d766f0f 100644 --- a/testdata/default/core/SetupConsistency.t.sol +++ b/testdata/default/core/SetupConsistency.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/fork/ForkSame_1.t.sol b/testdata/default/fork/ForkSame_1.t.sol index bbb73fcaa..949c7ea9e 100644 --- a/testdata/default/fork/ForkSame_1.t.sol +++ b/testdata/default/fork/ForkSame_1.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/fork/ForkSame_2.t.sol b/testdata/default/fork/ForkSame_2.t.sol index bbb73fcaa..949c7ea9e 100644 --- a/testdata/default/fork/ForkSame_2.t.sol +++ b/testdata/default/fork/ForkSame_2.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/fork/Transact.t.sol b/testdata/default/fork/Transact.t.sol index 0e3d9c9cb..375658772 100644 --- a/testdata/default/fork/Transact.t.sol +++ b/testdata/default/fork/Transact.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/fs/Default.t.sol b/testdata/default/fs/Default.t.sol index 5e776e696..e1524963f 100644 --- a/testdata/default/fs/Default.t.sol +++ b/testdata/default/fs/Default.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/fs/Disabled.t.sol b/testdata/default/fs/Disabled.t.sol index 4efe9affc..36f05c211 100644 --- a/testdata/default/fs/Disabled.t.sol +++ b/testdata/default/fs/Disabled.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/fuzz/Fuzz.t.sol b/testdata/default/fuzz/Fuzz.t.sol index ef4465380..24c8d1750 100644 --- a/testdata/default/fuzz/Fuzz.t.sol +++ b/testdata/default/fuzz/Fuzz.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/fuzz/FuzzCollection.t.sol b/testdata/default/fuzz/FuzzCollection.t.sol index 9be09bea8..0c98ddc66 100644 --- a/testdata/default/fuzz/FuzzCollection.t.sol +++ b/testdata/default/fuzz/FuzzCollection.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.15; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/fuzz/FuzzFailurePersist.t.sol b/testdata/default/fuzz/FuzzFailurePersist.t.sol index 0823f29fb..378706041 100644 --- a/testdata/default/fuzz/FuzzFailurePersist.t.sol +++ b/testdata/default/fuzz/FuzzFailurePersist.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/fuzz/FuzzInt.t.sol b/testdata/default/fuzz/FuzzInt.t.sol index 071727f6d..a47ff2953 100644 --- a/testdata/default/fuzz/FuzzInt.t.sol +++ b/testdata/default/fuzz/FuzzInt.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/fuzz/FuzzPositive.t.sol b/testdata/default/fuzz/FuzzPositive.t.sol index 952a3b699..7d3639dfe 100644 --- a/testdata/default/fuzz/FuzzPositive.t.sol +++ b/testdata/default/fuzz/FuzzPositive.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/fuzz/FuzzUint.t.sol b/testdata/default/fuzz/FuzzUint.t.sol index 923c2980f..c0cbf6466 100644 --- a/testdata/default/fuzz/FuzzUint.t.sol +++ b/testdata/default/fuzz/FuzzUint.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/fuzz/invariant/common/InvariantCalldataDictionary.t.sol b/testdata/default/fuzz/invariant/common/InvariantCalldataDictionary.t.sol index 5387b020d..3d4c51eac 100644 --- a/testdata/default/fuzz/invariant/common/InvariantCalldataDictionary.t.sol +++ b/testdata/default/fuzz/invariant/common/InvariantCalldataDictionary.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/fuzz/invariant/common/InvariantInnerContract.t.sol b/testdata/default/fuzz/invariant/common/InvariantInnerContract.t.sol index b8d5dc416..f8330a33c 100644 --- a/testdata/default/fuzz/invariant/common/InvariantInnerContract.t.sol +++ b/testdata/default/fuzz/invariant/common/InvariantInnerContract.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/fuzz/invariant/common/InvariantPreserveState.t.sol b/testdata/default/fuzz/invariant/common/InvariantPreserveState.t.sol index 546980136..bd70dd3ae 100644 --- a/testdata/default/fuzz/invariant/common/InvariantPreserveState.t.sol +++ b/testdata/default/fuzz/invariant/common/InvariantPreserveState.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/fuzz/invariant/common/InvariantReentrancy.t.sol b/testdata/default/fuzz/invariant/common/InvariantReentrancy.t.sol index f439b8ce1..74a01f180 100644 --- a/testdata/default/fuzz/invariant/common/InvariantReentrancy.t.sol +++ b/testdata/default/fuzz/invariant/common/InvariantReentrancy.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/fuzz/invariant/common/InvariantTest1.t.sol b/testdata/default/fuzz/invariant/common/InvariantTest1.t.sol index 15deccf33..bb62f34c6 100644 --- a/testdata/default/fuzz/invariant/common/InvariantTest1.t.sol +++ b/testdata/default/fuzz/invariant/common/InvariantTest1.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/fuzz/invariant/target/ExcludeContracts.t.sol b/testdata/default/fuzz/invariant/target/ExcludeContracts.t.sol index 60aef60d9..e2e850e31 100644 --- a/testdata/default/fuzz/invariant/target/ExcludeContracts.t.sol +++ b/testdata/default/fuzz/invariant/target/ExcludeContracts.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/fuzz/invariant/target/ExcludeSelectors.t.sol b/testdata/default/fuzz/invariant/target/ExcludeSelectors.t.sol index 526da0c67..e2251f42c 100644 --- a/testdata/default/fuzz/invariant/target/ExcludeSelectors.t.sol +++ b/testdata/default/fuzz/invariant/target/ExcludeSelectors.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/fuzz/invariant/target/ExcludeSenders.t.sol b/testdata/default/fuzz/invariant/target/ExcludeSenders.t.sol index bd37bd9bb..dda07074d 100644 --- a/testdata/default/fuzz/invariant/target/ExcludeSenders.t.sol +++ b/testdata/default/fuzz/invariant/target/ExcludeSenders.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/fuzz/invariant/target/FuzzedTargetContracts.t.sol b/testdata/default/fuzz/invariant/target/FuzzedTargetContracts.t.sol index 7988d5c8a..759810611 100644 --- a/testdata/default/fuzz/invariant/target/FuzzedTargetContracts.t.sol +++ b/testdata/default/fuzz/invariant/target/FuzzedTargetContracts.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/fuzz/invariant/target/TargetContracts.t.sol b/testdata/default/fuzz/invariant/target/TargetContracts.t.sol index cdce69153..d24c7eb52 100644 --- a/testdata/default/fuzz/invariant/target/TargetContracts.t.sol +++ b/testdata/default/fuzz/invariant/target/TargetContracts.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/fuzz/invariant/target/TargetInterfaces.t.sol b/testdata/default/fuzz/invariant/target/TargetInterfaces.t.sol index 5cfa85ce1..30b4a05e3 100644 --- a/testdata/default/fuzz/invariant/target/TargetInterfaces.t.sol +++ b/testdata/default/fuzz/invariant/target/TargetInterfaces.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/fuzz/invariant/target/TargetSelectors.t.sol b/testdata/default/fuzz/invariant/target/TargetSelectors.t.sol index ef3af2142..c74ac7fa1 100644 --- a/testdata/default/fuzz/invariant/target/TargetSelectors.t.sol +++ b/testdata/default/fuzz/invariant/target/TargetSelectors.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/fuzz/invariant/target/TargetSenders.t.sol b/testdata/default/fuzz/invariant/target/TargetSenders.t.sol index 50f37c4e9..6fa4c9a63 100644 --- a/testdata/default/fuzz/invariant/target/TargetSenders.t.sol +++ b/testdata/default/fuzz/invariant/target/TargetSenders.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol b/testdata/default/fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol index bf457ab17..86ca6d543 100644 --- a/testdata/default/fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol +++ b/testdata/default/fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol b/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol index 2957c57de..440f6183f 100644 --- a/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol +++ b/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol b/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol index c12cae74f..162d9cc2e 100644 --- a/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol +++ b/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/fuzz/invariant/targetAbi/TargetArtifacts.t.sol b/testdata/default/fuzz/invariant/targetAbi/TargetArtifacts.t.sol index ea86ab135..28fa14605 100644 --- a/testdata/default/fuzz/invariant/targetAbi/TargetArtifacts.t.sol +++ b/testdata/default/fuzz/invariant/targetAbi/TargetArtifacts.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/inline/FuzzInlineConf.t.sol b/testdata/default/inline/FuzzInlineConf.t.sol index 378931312..73d2de2fc 100644 --- a/testdata/default/inline/FuzzInlineConf.t.sol +++ b/testdata/default/inline/FuzzInlineConf.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity >=0.8.0; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/inline/InvariantInlineConf.t.sol b/testdata/default/inline/InvariantInlineConf.t.sol index c032911ec..5ac81755e 100644 --- a/testdata/default/inline/InvariantInlineConf.t.sol +++ b/testdata/default/inline/InvariantInlineConf.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity >=0.8.0; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/linking/cycle/Cycle.t.sol b/testdata/default/linking/cycle/Cycle.t.sol index 424bc001f..010f55ac3 100644 --- a/testdata/default/linking/cycle/Cycle.t.sol +++ b/testdata/default/linking/cycle/Cycle.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; library Foo { function foo() external { diff --git a/testdata/default/linking/duplicate/Duplicate.t.sol b/testdata/default/linking/duplicate/Duplicate.t.sol index a09c81e4f..d1d0f3278 100644 --- a/testdata/default/linking/duplicate/Duplicate.t.sol +++ b/testdata/default/linking/duplicate/Duplicate.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/linking/nested/Nested.t.sol b/testdata/default/linking/nested/Nested.t.sol index 90100c889..136cb3647 100644 --- a/testdata/default/linking/nested/Nested.t.sol +++ b/testdata/default/linking/nested/Nested.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/linking/simple/Simple.t.sol b/testdata/default/linking/simple/Simple.t.sol index a6a636b6c..85be791fd 100644 --- a/testdata/default/linking/simple/Simple.t.sol +++ b/testdata/default/linking/simple/Simple.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/logs/DebugLogs.t.sol b/testdata/default/logs/DebugLogs.t.sol index 59bb2a188..3e307d173 100644 --- a/testdata/default/logs/DebugLogs.t.sol +++ b/testdata/default/logs/DebugLogs.t.sol @@ -1,4 +1,4 @@ -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/logs/HardhatLogs.t.sol b/testdata/default/logs/HardhatLogs.t.sol index 842a390a4..cc2f2b785 100644 --- a/testdata/default/logs/HardhatLogs.t.sol +++ b/testdata/default/logs/HardhatLogs.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "./console.sol"; diff --git a/testdata/default/logs/console.sol b/testdata/default/logs/console.sol index f67c10bfa..feed58fb3 100644 --- a/testdata/default/logs/console.sol +++ b/testdata/default/logs/console.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity 0.8; library console { address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67); diff --git a/testdata/default/repros/Issue1543.t.sol b/testdata/default/repros/Issue1543.t.sol index e8b4806ed..e58f331c4 100644 --- a/testdata/default/repros/Issue1543.t.sol +++ b/testdata/default/repros/Issue1543.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/repros/Issue2623.t.sol b/testdata/default/repros/Issue2623.t.sol index 1d1c2b35b..31252cae3 100644 --- a/testdata/default/repros/Issue2623.t.sol +++ b/testdata/default/repros/Issue2623.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue2629.t.sol b/testdata/default/repros/Issue2629.t.sol index ffff50722..d46868903 100644 --- a/testdata/default/repros/Issue2629.t.sol +++ b/testdata/default/repros/Issue2629.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue2723.t.sol b/testdata/default/repros/Issue2723.t.sol index 70e522296..b7678df45 100644 --- a/testdata/default/repros/Issue2723.t.sol +++ b/testdata/default/repros/Issue2723.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue2898.t.sol b/testdata/default/repros/Issue2898.t.sol index 23de35bcd..a16adf5a3 100644 --- a/testdata/default/repros/Issue2898.t.sol +++ b/testdata/default/repros/Issue2898.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue2956.t.sol b/testdata/default/repros/Issue2956.t.sol index b69d17fb3..c57b46cc1 100644 --- a/testdata/default/repros/Issue2956.t.sol +++ b/testdata/default/repros/Issue2956.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue2984.t.sol b/testdata/default/repros/Issue2984.t.sol index 5f0203369..fbcd1ab8c 100644 --- a/testdata/default/repros/Issue2984.t.sol +++ b/testdata/default/repros/Issue2984.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue3055.t.sol b/testdata/default/repros/Issue3055.t.sol index 72461a6c0..90ac8c3b0 100644 --- a/testdata/default/repros/Issue3055.t.sol +++ b/testdata/default/repros/Issue3055.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue3077.t.sol b/testdata/default/repros/Issue3077.t.sol index b67316294..3b5e4257a 100644 --- a/testdata/default/repros/Issue3077.t.sol +++ b/testdata/default/repros/Issue3077.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue3110.t.sol b/testdata/default/repros/Issue3110.t.sol index f9ca984bd..9f1da8d03 100644 --- a/testdata/default/repros/Issue3110.t.sol +++ b/testdata/default/repros/Issue3110.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue3119.t.sol b/testdata/default/repros/Issue3119.t.sol index 3e82985c2..6c0ceb429 100644 --- a/testdata/default/repros/Issue3119.t.sol +++ b/testdata/default/repros/Issue3119.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue3189.t.sol b/testdata/default/repros/Issue3189.t.sol index 27ea0ac51..0bcf5ddce 100644 --- a/testdata/default/repros/Issue3189.t.sol +++ b/testdata/default/repros/Issue3189.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue3190.t.sol b/testdata/default/repros/Issue3190.t.sol index 4a9add5f5..ede3e50e2 100644 --- a/testdata/default/repros/Issue3190.t.sol +++ b/testdata/default/repros/Issue3190.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue3192.t.sol b/testdata/default/repros/Issue3192.t.sol index 1f693b1aa..9c5be8d89 100644 --- a/testdata/default/repros/Issue3192.t.sol +++ b/testdata/default/repros/Issue3192.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue3220.t.sol b/testdata/default/repros/Issue3220.t.sol index 30e75027a..5235e44c7 100644 --- a/testdata/default/repros/Issue3220.t.sol +++ b/testdata/default/repros/Issue3220.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue3221.t.sol b/testdata/default/repros/Issue3221.t.sol index 628f9d0e1..81398c41f 100644 --- a/testdata/default/repros/Issue3221.t.sol +++ b/testdata/default/repros/Issue3221.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue3223.t.sol b/testdata/default/repros/Issue3223.t.sol index cd43cb8ef..6c21b7b3d 100644 --- a/testdata/default/repros/Issue3223.t.sol +++ b/testdata/default/repros/Issue3223.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue3347.t.sol b/testdata/default/repros/Issue3347.t.sol index ed9be5f36..e48c1305d 100644 --- a/testdata/default/repros/Issue3347.t.sol +++ b/testdata/default/repros/Issue3347.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue3596.t.sol b/testdata/default/repros/Issue3596.t.sol index 04ee470d7..b0c678587 100644 --- a/testdata/default/repros/Issue3596.t.sol +++ b/testdata/default/repros/Issue3596.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue3653.t.sol b/testdata/default/repros/Issue3653.t.sol index 8cfcc2d0e..26eb38e4a 100644 --- a/testdata/default/repros/Issue3653.t.sol +++ b/testdata/default/repros/Issue3653.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue3661.t.sol b/testdata/default/repros/Issue3661.t.sol index f141a19ab..76b55a222 100644 --- a/testdata/default/repros/Issue3661.t.sol +++ b/testdata/default/repros/Issue3661.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/repros/Issue3674.t.sol b/testdata/default/repros/Issue3674.t.sol index 0b680342a..de5a96005 100644 --- a/testdata/default/repros/Issue3674.t.sol +++ b/testdata/default/repros/Issue3674.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue3685.t.sol b/testdata/default/repros/Issue3685.t.sol index 7e8f886d8..f1da5bf69 100644 --- a/testdata/default/repros/Issue3685.t.sol +++ b/testdata/default/repros/Issue3685.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue3703.t.sol b/testdata/default/repros/Issue3703.t.sol index b6dc39f26..48651be24 100644 --- a/testdata/default/repros/Issue3703.t.sol +++ b/testdata/default/repros/Issue3703.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue3708.t.sol b/testdata/default/repros/Issue3708.t.sol index f5bdf48bf..1e9a337f1 100644 --- a/testdata/default/repros/Issue3708.t.sol +++ b/testdata/default/repros/Issue3708.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue3753.t.sol b/testdata/default/repros/Issue3753.t.sol index 7af774baf..2c927c823 100644 --- a/testdata/default/repros/Issue3753.t.sol +++ b/testdata/default/repros/Issue3753.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue3792.t.sol b/testdata/default/repros/Issue3792.t.sol index 1adeb88af..37f62bc61 100644 --- a/testdata/default/repros/Issue3792.t.sol +++ b/testdata/default/repros/Issue3792.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue4402.t.sol b/testdata/default/repros/Issue4402.t.sol index 3bf0f33fb..830b2926e 100644 --- a/testdata/default/repros/Issue4402.t.sol +++ b/testdata/default/repros/Issue4402.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue4586.t.sol b/testdata/default/repros/Issue4586.t.sol index 29284ee1b..c904af1e4 100644 --- a/testdata/default/repros/Issue4586.t.sol +++ b/testdata/default/repros/Issue4586.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue4630.t.sol b/testdata/default/repros/Issue4630.t.sol index 4b9fe9c9b..01eb62650 100644 --- a/testdata/default/repros/Issue4630.t.sol +++ b/testdata/default/repros/Issue4630.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue4640.t.sol b/testdata/default/repros/Issue4640.t.sol index eaa87c12c..1e7d887a9 100644 --- a/testdata/default/repros/Issue4640.t.sol +++ b/testdata/default/repros/Issue4640.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue5038.t.sol b/testdata/default/repros/Issue5038.t.sol index 834f82783..51a90bca1 100644 --- a/testdata/default/repros/Issue5038.t.sol +++ b/testdata/default/repros/Issue5038.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue5739.t.sol b/testdata/default/repros/Issue5739.t.sol index eafbabd93..6f3494b7e 100644 --- a/testdata/default/repros/Issue5739.t.sol +++ b/testdata/default/repros/Issue5739.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue5808.t.sol b/testdata/default/repros/Issue5808.t.sol index 66ea82b30..40efe65a9 100644 --- a/testdata/default/repros/Issue5808.t.sol +++ b/testdata/default/repros/Issue5808.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue5929.t.sol b/testdata/default/repros/Issue5929.t.sol index cce676d25..70c5a4f4f 100644 --- a/testdata/default/repros/Issue5929.t.sol +++ b/testdata/default/repros/Issue5929.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue5935.t.sol b/testdata/default/repros/Issue5935.t.sol index f783d12da..8ef724412 100644 --- a/testdata/default/repros/Issue5935.t.sol +++ b/testdata/default/repros/Issue5935.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.0 <0.9.0; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue5948.t.sol b/testdata/default/repros/Issue5948.t.sol index 992099fb1..ae6ee9d50 100644 --- a/testdata/default/repros/Issue5948.t.sol +++ b/testdata/default/repros/Issue5948.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue6006.t.sol b/testdata/default/repros/Issue6006.t.sol index dac37cd24..54f0d1137 100644 --- a/testdata/default/repros/Issue6006.t.sol +++ b/testdata/default/repros/Issue6006.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue6032.t.sol b/testdata/default/repros/Issue6032.t.sol index 75002a136..2fa05222d 100644 --- a/testdata/default/repros/Issue6032.t.sol +++ b/testdata/default/repros/Issue6032.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue6070.t.sol b/testdata/default/repros/Issue6070.t.sol index e699f5ca9..ebf3c7ab1 100644 --- a/testdata/default/repros/Issue6070.t.sol +++ b/testdata/default/repros/Issue6070.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue6115.t.sol b/testdata/default/repros/Issue6115.t.sol index 65c5cdaa7..ae65a7dae 100644 --- a/testdata/default/repros/Issue6115.t.sol +++ b/testdata/default/repros/Issue6115.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/repros/Issue6170.t.sol b/testdata/default/repros/Issue6170.t.sol index 543ca3142..78511f4a2 100644 --- a/testdata/default/repros/Issue6170.t.sol +++ b/testdata/default/repros/Issue6170.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue6180.t.sol b/testdata/default/repros/Issue6180.t.sol index 591c60bdf..3d08ccbeb 100644 --- a/testdata/default/repros/Issue6180.t.sol +++ b/testdata/default/repros/Issue6180.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue6293.t.sol b/testdata/default/repros/Issue6293.t.sol index 303e8fbbe..6d57d1385 100644 --- a/testdata/default/repros/Issue6293.t.sol +++ b/testdata/default/repros/Issue6293.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Unlicense -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue6355.t.sol b/testdata/default/repros/Issue6355.t.sol index f002b2a87..bbc3a4a98 100644 --- a/testdata/default/repros/Issue6355.t.sol +++ b/testdata/default/repros/Issue6355.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue6437.t.sol b/testdata/default/repros/Issue6437.t.sol index c18af2dfd..4cf27be7b 100644 --- a/testdata/default/repros/Issue6437.t.sol +++ b/testdata/default/repros/Issue6437.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue6501.t.sol b/testdata/default/repros/Issue6501.t.sol index 392f0f1ab..5d631cbe3 100644 --- a/testdata/default/repros/Issue6501.t.sol +++ b/testdata/default/repros/Issue6501.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "../logs/console.sol"; diff --git a/testdata/default/repros/Issue6538.t.sol b/testdata/default/repros/Issue6538.t.sol index 2b8beb578..5b318a04c 100644 --- a/testdata/default/repros/Issue6538.t.sol +++ b/testdata/default/repros/Issue6538.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue6554.t.sol b/testdata/default/repros/Issue6554.t.sol index c13ebc4a7..7a5fe7879 100644 --- a/testdata/default/repros/Issue6554.t.sol +++ b/testdata/default/repros/Issue6554.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue6616.t.sol b/testdata/default/repros/Issue6616.t.sol index f31a79ee6..262721d86 100644 --- a/testdata/default/repros/Issue6616.t.sol +++ b/testdata/default/repros/Issue6616.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue6634.t.sol b/testdata/default/repros/Issue6634.t.sol index 64d92e9d1..aa94922dd 100644 --- a/testdata/default/repros/Issue6634.t.sol +++ b/testdata/default/repros/Issue6634.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue6643.t.sol b/testdata/default/repros/Issue6643.t.sol index 36b684c13..5c7e1c483 100644 --- a/testdata/default/repros/Issue6643.t.sol +++ b/testdata/default/repros/Issue6643.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue6759.t.sol b/testdata/default/repros/Issue6759.t.sol index e528c63e1..ffdcb8893 100644 --- a/testdata/default/repros/Issue6759.t.sol +++ b/testdata/default/repros/Issue6759.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue6966.t.sol b/testdata/default/repros/Issue6966.t.sol index f7a8db700..7e35a869e 100644 --- a/testdata/default/repros/Issue6966.t.sol +++ b/testdata/default/repros/Issue6966.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; diff --git a/testdata/default/repros/Issue7457.t.sol b/testdata/default/repros/Issue7457.t.sol index 3c4080df2..1836c4825 100644 --- a/testdata/default/repros/Issue7457.t.sol +++ b/testdata/default/repros/Issue7457.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue7481.t.sol b/testdata/default/repros/Issue7481.t.sol index 441758b4f..46b24b1d5 100644 --- a/testdata/default/repros/Issue7481.t.sol +++ b/testdata/default/repros/Issue7481.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue8004.t.sol b/testdata/default/repros/Issue8004.t.sol index 229dde5ad..278aa1212 100644 --- a/testdata/default/repros/Issue8004.t.sol +++ b/testdata/default/repros/Issue8004.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue8006.t.sol b/testdata/default/repros/Issue8006.t.sol index 078117d40..95b16e6f6 100644 --- a/testdata/default/repros/Issue8006.t.sol +++ b/testdata/default/repros/Issue8006.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue8168.t.sol b/testdata/default/repros/Issue8168.t.sol index f11cef7bc..9a072ce4b 100644 --- a/testdata/default/repros/Issue8168.t.sol +++ b/testdata/default/repros/Issue8168.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue8277.t.sol b/testdata/default/repros/Issue8277.t.sol index aebdbd8ff..48a089575 100644 --- a/testdata/default/repros/Issue8277.t.sol +++ b/testdata/default/repros/Issue8277.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue8287.t.sol b/testdata/default/repros/Issue8287.t.sol index 86901c0fd..d1e372bda 100644 --- a/testdata/default/repros/Issue8287.t.sol +++ b/testdata/default/repros/Issue8287.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue8383.t.sol b/testdata/default/repros/Issue8383.t.sol index a002b4b3d..339f5b518 100644 --- a/testdata/default/repros/Issue8383.t.sol +++ b/testdata/default/repros/Issue8383.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/repros/Issue8971.t.sol b/testdata/default/repros/Issue8971.t.sol index d61e04ae6..37861b483 100644 --- a/testdata/default/repros/Issue8971.t.sol +++ b/testdata/default/repros/Issue8971.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/script/deploy.sol b/testdata/default/script/deploy.sol index 013e009d3..7570c706a 100644 --- a/testdata/default/script/deploy.sol +++ b/testdata/default/script/deploy.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import {DSTest} from "lib/ds-test/src/test.sol"; import {Vm} from "cheats/Vm.sol"; diff --git a/testdata/default/spec/ShanghaiCompat.t.sol b/testdata/default/spec/ShanghaiCompat.t.sol index 02856a88f..fd7213b3d 100644 --- a/testdata/default/spec/ShanghaiCompat.t.sol +++ b/testdata/default/spec/ShanghaiCompat.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.20; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/trace/ConflictingSignatures.t.sol b/testdata/default/trace/ConflictingSignatures.t.sol index 67dfd5d3a..c8b7066c7 100644 --- a/testdata/default/trace/ConflictingSignatures.t.sol +++ b/testdata/default/trace/ConflictingSignatures.t.sol @@ -1,4 +1,4 @@ -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/default/trace/Trace.t.sol b/testdata/default/trace/Trace.t.sol index d513e8637..19af6dd7c 100644 --- a/testdata/default/trace/Trace.t.sol +++ b/testdata/default/trace/Trace.t.sol @@ -1,4 +1,4 @@ -pragma solidity 0.8.18; +pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/fixtures/GetCode/UnlinkedContract.sol b/testdata/fixtures/GetCode/UnlinkedContract.sol index 41f0b0d76..93c6a8e26 100644 --- a/testdata/fixtures/GetCode/UnlinkedContract.sol +++ b/testdata/fixtures/GetCode/UnlinkedContract.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; library SmolLibrary { function add(uint256 a, uint256 b) public pure returns (uint256 c) { diff --git a/testdata/fixtures/GetCode/WorkingContract.sol b/testdata/fixtures/GetCode/WorkingContract.sol index 3ea502055..7f4cb79af 100644 --- a/testdata/fixtures/GetCode/WorkingContract.sol +++ b/testdata/fixtures/GetCode/WorkingContract.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity ^0.8.18; contract WorkingContract { uint256 public constant secret = 42; diff --git a/testdata/multi-version/cheats/GetCode.t.sol b/testdata/multi-version/cheats/GetCode.t.sol index e4a7bd14a..72dae24e6 100644 --- a/testdata/multi-version/cheats/GetCode.t.sol +++ b/testdata/multi-version/cheats/GetCode.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.18; +pragma solidity =0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; diff --git a/testdata/multi-version/cheats/GetCode17.t.sol b/testdata/multi-version/cheats/GetCode17.t.sol index 068a910cf..f8bf4bb2a 100644 --- a/testdata/multi-version/cheats/GetCode17.t.sol +++ b/testdata/multi-version/cheats/GetCode17.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.17; +pragma solidity =0.8.17; import "ds-test/test.sol"; import "cheats/Vm.sol"; From 0c659f07e1a3c1710ca5bc7c587f86620c2b1f8b Mon Sep 17 00:00:00 2001 From: boolafish Date: Wed, 9 Oct 2024 23:57:38 +0900 Subject: [PATCH 081/100] feat(cheatcode): `startDebugTraceRecording` and `stopDebugTraceRecording` for ERC4337 testing (#8571) * feat: add record opcode cheat code feat: capture stack inputs as part of the opcode feat: record opcode -> record debug trace fix: memory OOG, need to only use needed stack, mem input fix: missing op code, instruction results fix: accessing out-of-bound idx memory When running on some project, we noticed that it sometimes try to access memory with out of bound index and panics. This commit fix it by: 1. Enfore reset to Nonce after stopDebugTraceRecording(), this ensures the `some(..) = ...` part will not be triggered 2. Change how opcode_utils.rs accesses memory. Return empty vector if trying access out-of-bound memory. * test: add DebugTrace.t.sol for the debug trace cheatcode * fix: rebase errors * feat: use tracer for debug trace instead of recording during inspector This commit also cleans up the previous implementaiton on inspector. And then change the cheatcode interface to be of three steps: 1. start recording debug trace 2. stop recording 3. get the debug trace by index The reason is to avoid out-of-memory issue by returning the whole traces at once. * fix: rebase duplication * feat: replace instruction result with isOutOfGas * fix: CI issues * fix: remove DebugTrace wrapper in inspector * fix: revert to original tracer config when stops * chore: reuse existing opcode functions * chore: refactor, fmt, clippy run * chore: use ref instead of clone, returning Error when not able to access * chore: move buffer to evm_core from debugger * fix: disable dummy tracer by default, return explicit error Since enabling dummy tracer still come with performance impact, remove the auto dummy tracer initiation. The cheatcode will return explicit error and require the test to be run in -vvv mode to have the tracer enabled by default. * fix: return all traces, turn on necessary tracer config There was OOM concern but using the get-by-index style, despite improved, does not solve the root cause. The main issue is that the tracer config did not turn off after the stop recording cheatcode being called. It seems too much burden for the tracer to record the returned traces inside forge tests as the tests will also pass around the debug traces, causing memory boost. This commit also only turns on necessary tracer config instead of using all(). * chore: cleanup comments, typo * fix: use bytes for memory, remove flattern function, fix get_slice_from_memory * fix: style fmt * fix: ensure steps in the order of node when flatten A node can have steps that calls to another node, so the child node's step might occur before some steps of its parent node. This introduce the flatten_call_trace function back using recursive call to ensure the steps are in correct order despite not in the same order of the node index. see PR comment: https://github.com/foundry-rs/foundry/pull/8571#discussion_r1785386322 * doc: remove legacy comment in test * style: reuse empty initialized var on return val --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --- Cargo.lock | 2 + crates/cheatcodes/Cargo.toml | 1 + crates/cheatcodes/assets/cheatcodes.json | 76 ++++++++ crates/cheatcodes/spec/src/lib.rs | 1 + crates/cheatcodes/spec/src/vm.rs | 33 ++++ crates/cheatcodes/src/evm.rs | 67 ++++++- .../cheatcodes/src/evm/record_debug_step.rs | 144 +++++++++++++++ crates/cheatcodes/src/inspector.rs | 14 +- crates/debugger/Cargo.toml | 1 + crates/debugger/src/tui/context.rs | 29 +-- crates/debugger/src/tui/draw.rs | 90 +--------- crates/evm/core/src/buffer.rs | 117 ++++++++++++ crates/evm/core/src/lib.rs | 1 + testdata/cheats/Vm.sol | 3 + .../default/cheats/RecordDebugTrace.t.sol | 166 ++++++++++++++++++ 15 files changed, 626 insertions(+), 119 deletions(-) create mode 100644 crates/cheatcodes/src/evm/record_debug_step.rs create mode 100644 crates/evm/core/src/buffer.rs create mode 100644 testdata/default/cheats/RecordDebugTrace.t.sol diff --git a/Cargo.lock b/Cargo.lock index 381ed5a75..7438ab7f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3634,6 +3634,7 @@ dependencies = [ "proptest", "rand", "revm", + "revm-inspectors", "semver 1.0.23", "serde_json", "thiserror", @@ -3908,6 +3909,7 @@ dependencies = [ "eyre", "foundry-common", "foundry-compilers", + "foundry-evm-core", "foundry-evm-traces", "ratatui", "revm", diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index 7f990a8e5..adce79b21 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -58,6 +58,7 @@ p256 = "0.13.2" ecdsa = "0.16" rand = "0.8" revm.workspace = true +revm-inspectors.workspace = true semver.workspace = true serde_json.workspace = true thiserror.workspace = true diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 06ddba5bd..d64df4860 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -488,6 +488,42 @@ "description": "The amount of gas remaining." } ] + }, + { + "name": "DebugStep", + "description": "The result of the `stopDebugTraceRecording` call", + "fields": [ + { + "name": "stack", + "ty": "uint256[]", + "description": "The stack before executing the step of the run.\n stack\\[0\\] represents the top of the stack.\n and only stack data relevant to the opcode execution is contained." + }, + { + "name": "memoryInput", + "ty": "bytes", + "description": "The memory input data before executing the step of the run.\n only input data relevant to the opcode execution is contained.\n e.g. for MLOAD, it will have memory\\[offset:offset+32\\] copied here.\n the offset value can be get by the stack data." + }, + { + "name": "opcode", + "ty": "uint8", + "description": "The opcode that was accessed." + }, + { + "name": "depth", + "ty": "uint64", + "description": "The call depth of the step." + }, + { + "name": "isOutOfGas", + "ty": "bool", + "description": "Whether the call end up with out of gas error." + }, + { + "name": "contractAddr", + "ty": "address", + "description": "The contract address where the opcode is running" + } + ] } ], "cheatcodes": [ @@ -8863,6 +8899,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "startDebugTraceRecording", + "description": "Records the debug trace during the run.", + "declaration": "function startDebugTraceRecording() external;", + "visibility": "external", + "mutability": "", + "signature": "startDebugTraceRecording()", + "selector": "0x419c8832", + "selectorBytes": [ + 65, + 156, + 136, + 50 + ] + }, + "group": "evm", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "startMappingRecording", @@ -8983,6 +9039,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "stopAndReturnDebugTraceRecording", + "description": "Stop debug trace recording and returns the recorded debug trace.", + "declaration": "function stopAndReturnDebugTraceRecording() external returns (DebugStep[] memory step);", + "visibility": "external", + "mutability": "", + "signature": "stopAndReturnDebugTraceRecording()", + "selector": "0xced398a2", + "selectorBytes": [ + 206, + 211, + 152, + 162 + ] + }, + "group": "evm", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "stopAndReturnStateDiff", diff --git a/crates/cheatcodes/spec/src/lib.rs b/crates/cheatcodes/spec/src/lib.rs index fffc146a9..662853e9e 100644 --- a/crates/cheatcodes/spec/src/lib.rs +++ b/crates/cheatcodes/spec/src/lib.rs @@ -85,6 +85,7 @@ impl Cheatcodes<'static> { Vm::AccountAccess::STRUCT.clone(), Vm::StorageAccess::STRUCT.clone(), Vm::Gas::STRUCT.clone(), + Vm::DebugStep::STRUCT.clone(), ]), enums: Cow::Owned(vec![ Vm::CallerMode::ENUM.clone(), diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index e73755de1..761d64e9b 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -261,6 +261,28 @@ interface Vm { uint64 depth; } + /// The result of the `stopDebugTraceRecording` call + struct DebugStep { + /// The stack before executing the step of the run. + /// stack\[0\] represents the top of the stack. + /// and only stack data relevant to the opcode execution is contained. + uint256[] stack; + /// The memory input data before executing the step of the run. + /// only input data relevant to the opcode execution is contained. + /// + /// e.g. for MLOAD, it will have memory\[offset:offset+32\] copied here. + /// the offset value can be get by the stack data. + bytes memoryInput; + /// The opcode that was accessed. + uint8 opcode; + /// The call depth of the step. + uint64 depth; + /// Whether the call end up with out of gas error. + bool isOutOfGas; + /// The contract address where the opcode is running + address contractAddr; + } + // ======== EVM ======== /// Gets the address for a given private key. @@ -287,6 +309,17 @@ interface Vm { #[cheatcode(group = Evm, safety = Unsafe)] function loadAllocs(string calldata pathToAllocsJson) external; + // -------- Record Debug Traces -------- + + /// Records the debug trace during the run. + #[cheatcode(group = Evm, safety = Safe)] + function startDebugTraceRecording() external; + + /// Stop debug trace recording and returns the recorded debug trace. + #[cheatcode(group = Evm, safety = Safe)] + function stopAndReturnDebugTraceRecording() external returns (DebugStep[] memory step); + + /// Clones a source account code, state, balance and nonce to a target account and updates in-memory EVM state. #[cheatcode(group = Evm, safety = Unsafe)] function cloneAccount(address source, address target) external; diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index b9a3d7047..acc349be1 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -1,8 +1,9 @@ //! Implementations of [`Evm`](spec::Group::Evm) cheatcodes. use crate::{ - inspector::InnerEcx, BroadcastableTransaction, Cheatcode, Cheatcodes, CheatcodesExecutor, - CheatsCtxt, Result, Vm::*, + inspector::{InnerEcx, RecordDebugStepInfo}, + BroadcastableTransaction, Cheatcode, Cheatcodes, CheatcodesExecutor, CheatsCtxt, Error, Result, + Vm::*, }; use alloy_consensus::TxEnvelope; use alloy_genesis::{Genesis, GenesisAccount}; @@ -14,10 +15,14 @@ use foundry_evm_core::{ backend::{DatabaseExt, RevertStateSnapshotAction}, constants::{CALLER, CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, TEST_CONTRACT_ADDRESS}, }; +use foundry_evm_traces::StackSnapshotType; use rand::Rng; use revm::primitives::{Account, Bytecode, SpecId, KECCAK_EMPTY}; use std::{collections::BTreeMap, path::Path}; +mod record_debug_step; +use record_debug_step::{convert_call_trace_to_debug_step, flatten_call_trace}; + mod fork; pub(crate) mod mapping; pub(crate) mod mock; @@ -715,6 +720,64 @@ impl Cheatcode for setBlockhashCall { } } +impl Cheatcode for startDebugTraceRecordingCall { + fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { + let Some(tracer) = executor.tracing_inspector().and_then(|t| t.as_mut()) else { + return Err(Error::from("no tracer initiated, consider adding -vvv flag")) + }; + + let mut info = RecordDebugStepInfo { + // will be updated later + start_node_idx: 0, + // keep the original config to revert back later + original_tracer_config: *tracer.config(), + }; + + // turn on tracer configuration for recording + tracer.update_config(|config| { + config + .set_steps(true) + .set_memory_snapshots(true) + .set_stack_snapshots(StackSnapshotType::Full) + }); + + // track where the recording starts + if let Some(last_node) = tracer.traces().nodes().last() { + info.start_node_idx = last_node.idx; + } + + ccx.state.record_debug_steps_info = Some(info); + Ok(Default::default()) + } +} + +impl Cheatcode for stopAndReturnDebugTraceRecordingCall { + fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { + let Some(tracer) = executor.tracing_inspector().and_then(|t| t.as_mut()) else { + return Err(Error::from("no tracer initiated, consider adding -vvv flag")) + }; + + let Some(record_info) = ccx.state.record_debug_steps_info else { + return Err(Error::from("nothing recorded")) + }; + + // Revert the tracer config to the one before recording + tracer.update_config(|_config| record_info.original_tracer_config); + + // Use the trace nodes to flatten the call trace + let root = tracer.traces(); + let steps = flatten_call_trace(0, root, record_info.start_node_idx); + + let debug_steps: Vec = + steps.iter().map(|&step| convert_call_trace_to_debug_step(step)).collect(); + + // Clean up the recording info + ccx.state.record_debug_steps_info = None; + + Ok(debug_steps.abi_encode()) + } +} + pub(super) fn get_nonce(ccx: &mut CheatsCtxt, address: &Address) -> Result { 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/record_debug_step.rs b/crates/cheatcodes/src/evm/record_debug_step.rs new file mode 100644 index 000000000..b9f0f89cb --- /dev/null +++ b/crates/cheatcodes/src/evm/record_debug_step.rs @@ -0,0 +1,144 @@ +use alloy_primitives::{Bytes, U256}; + +use foundry_evm_traces::CallTraceArena; +use revm::interpreter::{InstructionResult, OpCode}; + +use foundry_evm_core::buffer::{get_buffer_accesses, BufferKind}; +use revm_inspectors::tracing::types::{CallTraceStep, RecordedMemory, TraceMemberOrder}; +use spec::Vm::DebugStep; + +// Do a depth first traverse of the nodes and steps and return steps +// that are after `node_start_idx` +pub(crate) fn flatten_call_trace( + root: usize, + arena: &CallTraceArena, + node_start_idx: usize, +) -> Vec<&CallTraceStep> { + let mut steps = Vec::new(); + let mut record_started = false; + + // Start the recursion from the root node + recursive_flatten_call_trace(root, arena, node_start_idx, &mut record_started, &mut steps); + steps +} + +// Inner recursive function to process nodes. +// This implementation directly mutates `record_started` and `flatten_steps`. +// So the recursive call can change the `record_started` flag even for the parent +// unfinished processing, and append steps to the `flatten_steps` as the final result. +fn recursive_flatten_call_trace<'a>( + node_idx: usize, + arena: &'a CallTraceArena, + node_start_idx: usize, + record_started: &mut bool, + flatten_steps: &mut Vec<&'a CallTraceStep>, +) { + // Once node_idx exceeds node_start_idx, start recording steps + // for all the recursive processing. + if !*record_started && node_idx >= node_start_idx { + *record_started = true; + } + + let node = &arena.nodes()[node_idx]; + + for order in node.ordering.iter() { + match order { + TraceMemberOrder::Step(step_idx) => { + if *record_started { + let step = &node.trace.steps[*step_idx]; + flatten_steps.push(step); + } + } + TraceMemberOrder::Call(call_idx) => { + let child_node_idx = node.children[*call_idx]; + recursive_flatten_call_trace( + child_node_idx, + arena, + node_start_idx, + record_started, + flatten_steps, + ); + } + _ => {} + } + } +} + +// Function to convert CallTraceStep to DebugStep +pub(crate) fn convert_call_trace_to_debug_step(step: &CallTraceStep) -> DebugStep { + let opcode = step.op.get(); + let stack = get_stack_inputs_for_opcode(opcode, step.stack.as_ref()); + + let memory = get_memory_input_for_opcode(opcode, step.stack.as_ref(), step.memory.as_ref()); + + let is_out_of_gas = step.status == InstructionResult::OutOfGas || + step.status == InstructionResult::MemoryOOG || + step.status == InstructionResult::MemoryLimitOOG || + step.status == InstructionResult::PrecompileOOG || + step.status == InstructionResult::InvalidOperandOOG; + + DebugStep { + stack, + memoryInput: memory, + opcode: step.op.get(), + depth: step.depth, + isOutOfGas: is_out_of_gas, + contractAddr: step.contract, + } +} + +// The expected `stack` here is from the trace stack, where the top of the stack +// is the last value of the vector +fn get_memory_input_for_opcode( + opcode: u8, + stack: Option<&Vec>, + memory: Option<&RecordedMemory>, +) -> Bytes { + let mut memory_input = Bytes::new(); + let Some(stack_data) = stack else { return memory_input }; + let Some(memory_data) = memory else { return memory_input }; + + if let Some(accesses) = get_buffer_accesses(opcode, stack_data) { + if let Some((BufferKind::Memory, access)) = accesses.read { + memory_input = get_slice_from_memory(memory_data.as_bytes(), access.offset, access.len); + } + }; + + memory_input +} + +// The expected `stack` here is from the trace stack, where the top of the stack +// is the last value of the vector +fn get_stack_inputs_for_opcode(opcode: u8, stack: Option<&Vec>) -> Vec { + let mut inputs = Vec::new(); + + let Some(op) = OpCode::new(opcode) else { return inputs }; + let Some(stack_data) = stack else { return inputs }; + + let stack_input_size = op.inputs() as usize; + for i in 0..stack_input_size { + inputs.push(stack_data[stack_data.len() - 1 - i]); + } + inputs +} + +fn get_slice_from_memory(memory: &Bytes, start_index: usize, size: usize) -> Bytes { + let memory_len = memory.len(); + + let end_bound = start_index + size; + + // Return the bytes if data is within the range. + if start_index < memory_len && end_bound <= memory_len { + return memory.slice(start_index..end_bound); + } + + // Pad zero bytes if attempting to load memory partially out of range. + if start_index < memory_len && end_bound > memory_len { + let mut result = memory.slice(start_index..memory_len).to_vec(); + result.resize(size, 0u8); + return Bytes::from(result); + } + + // Return empty bytes with the size if not in range at all. + Bytes::from(vec![0u8; size]) +} diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 9a40c3cb1..acb86681f 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -36,7 +36,7 @@ use foundry_evm_core::{ utils::new_evm_with_existing_context, InspectorExt, }; -use foundry_evm_traces::TracingInspector; +use foundry_evm_traces::{TracingInspector, TracingInspectorConfig}; use itertools::Itertools; use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner}; use rand::Rng; @@ -219,6 +219,14 @@ pub struct BroadcastableTransaction { pub transaction: TransactionMaybeSigned, } +#[derive(Clone, Debug, Copy)] +pub struct RecordDebugStepInfo { + /// The debug trace node index when the recording starts. + pub start_node_idx: usize, + /// The original tracer config when the recording starts. + pub original_tracer_config: TracingInspectorConfig, +} + /// Holds gas metering state. #[derive(Clone, Debug, Default)] pub struct GasMetering { @@ -396,6 +404,9 @@ pub struct Cheatcodes { /// merged into the previous vector. pub recorded_account_diffs_stack: Option>>, + /// The information of the debug step recording. + pub record_debug_steps_info: Option, + /// Recorded logs pub recorded_logs: Option>, @@ -492,6 +503,7 @@ impl Cheatcodes { accesses: Default::default(), recorded_account_diffs_stack: Default::default(), recorded_logs: Default::default(), + record_debug_steps_info: Default::default(), mocked_calls: Default::default(), mocked_functions: Default::default(), expected_calls: Default::default(), diff --git a/crates/debugger/Cargo.toml b/crates/debugger/Cargo.toml index 6ccb630ca..4fb417db5 100644 --- a/crates/debugger/Cargo.toml +++ b/crates/debugger/Cargo.toml @@ -16,6 +16,7 @@ workspace = true foundry-common.workspace = true foundry-compilers.workspace = true foundry-evm-traces.workspace = true +foundry-evm-core.workspace = true revm-inspectors.workspace = true alloy-primitives.workspace = true diff --git a/crates/debugger/src/tui/context.rs b/crates/debugger/src/tui/context.rs index 6792145fe..c3645e31b 100644 --- a/crates/debugger/src/tui/context.rs +++ b/crates/debugger/src/tui/context.rs @@ -3,6 +3,7 @@ use crate::{DebugNode, Debugger, ExitReason}; use alloy_primitives::{hex, Address}; use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseEvent, MouseEventKind}; +use foundry_evm_core::buffer::BufferKind; use revm::interpreter::OpCode; use revm_inspectors::tracing::types::{CallKind, CallTraceStep}; use std::ops::ControlFlow; @@ -15,34 +16,6 @@ pub(crate) struct DrawMemory { pub(crate) current_stack_startline: usize, } -/// Used to keep track of which buffer is currently active to be drawn by the debugger. -#[derive(Debug, PartialEq)] -pub(crate) enum BufferKind { - Memory, - Calldata, - Returndata, -} - -impl BufferKind { - /// Helper to cycle through the active buffers. - pub(crate) fn next(&self) -> Self { - match self { - Self::Memory => Self::Calldata, - Self::Calldata => Self::Returndata, - Self::Returndata => Self::Memory, - } - } - - /// Helper to format the title of the active buffer pane - pub(crate) fn title(&self, size: usize) -> String { - match self { - Self::Memory => format!("Memory (max expansion: {size} bytes)"), - Self::Calldata => format!("Calldata (size: {size} bytes)"), - Self::Returndata => format!("Returndata (size: {size} bytes)"), - } - } -} - pub(crate) struct DebuggerContext<'a> { pub(crate) debugger: &'a mut Debugger, diff --git a/crates/debugger/src/tui/draw.rs b/crates/debugger/src/tui/draw.rs index 0f2399a20..55e4834f5 100644 --- a/crates/debugger/src/tui/draw.rs +++ b/crates/debugger/src/tui/draw.rs @@ -1,9 +1,9 @@ //! TUI draw implementation. -use super::context::{BufferKind, DebuggerContext}; +use super::context::DebuggerContext; use crate::op::OpcodeParam; -use alloy_primitives::U256; use foundry_compilers::artifacts::sourcemap::SourceElement; +use foundry_evm_core::buffer::{get_buffer_accesses, BufferKind}; use foundry_evm_traces::debug::SourceData; use ratatui::{ layout::{Alignment, Constraint, Direction, Layout, Rect}, @@ -12,7 +12,6 @@ use ratatui::{ widgets::{Block, Borders, List, ListItem, ListState, Paragraph, Wrap}, Frame, }; -use revm::interpreter::opcode; use revm_inspectors::tracing::types::CallKind; use std::{collections::VecDeque, fmt::Write, io}; @@ -624,91 +623,6 @@ impl<'a> SourceLines<'a> { } } -/// Container for buffer access information. -struct BufferAccess { - offset: usize, - len: usize, -} - -/// Container for read and write buffer access information. -struct BufferAccesses { - /// The read buffer kind and access information. - read: Option<(BufferKind, BufferAccess)>, - /// The only mutable buffer is the memory buffer, so don't store the buffer kind. - write: Option, -} - -/// The memory_access variable stores the index on the stack that indicates the buffer -/// 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 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 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 => { - (Some((BufferKind::Memory, 1, 2)), None) - } - opcode::CALLDATACOPY => (Some((BufferKind::Calldata, 2, 3)), Some((1, 3))), - opcode::RETURNDATACOPY => (Some((BufferKind::Returndata, 2, 3)), Some((1, 3))), - opcode::CALLDATALOAD => (Some((BufferKind::Calldata, 1, -1)), None), - opcode::CODECOPY => (None, Some((1, 3))), - opcode::EXTCODECOPY => (None, Some((2, 4))), - opcode::MLOAD => (Some((BufferKind::Memory, 1, -1)), None), - opcode::MSTORE => (None, Some((1, -1))), - opcode::MSTORE8 => (None, Some((1, -2))), - opcode::LOG0 | opcode::LOG1 | opcode::LOG2 | opcode::LOG3 | opcode::LOG4 => { - (Some((BufferKind::Memory, 1, 2)), None) - } - opcode::CREATE | opcode::CREATE2 => (Some((BufferKind::Memory, 2, 3)), None), - opcode::CALL | opcode::CALLCODE => (Some((BufferKind::Memory, 4, 5)), None), - opcode::DELEGATECALL | opcode::STATICCALL => (Some((BufferKind::Memory, 3, 4)), None), - opcode::MCOPY => (Some((BufferKind::Memory, 2, 3)), Some((1, 3))), - opcode::RETURNDATALOAD => (Some((BufferKind::Returndata, 1, -1)), None), - opcode::EOFCREATE => (Some((BufferKind::Memory, 3, 4)), None), - opcode::RETURNCONTRACT => (Some((BufferKind::Memory, 1, 2)), None), - opcode::DATACOPY => (None, Some((1, 3))), - opcode::EXTCALL | opcode::EXTSTATICCALL | opcode::EXTDELEGATECALL => { - (Some((BufferKind::Memory, 2, 3)), None) - } - _ => Default::default(), - }; - - let stack_len = stack.len(); - let get_size = |stack_index| match stack_index { - -2 => Some(1), - -1 => Some(32), - 0 => None, - 1.. => { - if (stack_index as usize) <= stack_len { - Some(stack[stack_len - stack_index as usize].saturating_to()) - } else { - None - } - } - _ => panic!("invalid stack index"), - }; - - 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, len) = b; - Some((buffer, BufferAccess { offset: get_size(offset)?, len: get_size(len)? })) - }); - let write_access = write.and_then(|b| { - let (offset, len) = b; - Some(BufferAccess { offset: get_size(offset)?, len: get_size(len)? }) - }); - Some(BufferAccesses { read: read_access, write: write_access }) - } else { - None - } -} - fn hex_bytes_spans(bytes: &[u8], spans: &mut Vec>, f: impl Fn(usize, u8) -> Style) { for (i, &byte) in bytes.iter().enumerate() { if i > 0 { diff --git a/crates/evm/core/src/buffer.rs b/crates/evm/core/src/buffer.rs new file mode 100644 index 000000000..1db7420d7 --- /dev/null +++ b/crates/evm/core/src/buffer.rs @@ -0,0 +1,117 @@ +use alloy_primitives::U256; +use revm::interpreter::opcode; + +/// Used to keep track of which buffer is currently active to be drawn by the debugger. +#[derive(Debug, PartialEq)] +pub enum BufferKind { + Memory, + Calldata, + Returndata, +} + +impl BufferKind { + /// Helper to cycle through the active buffers. + pub fn next(&self) -> Self { + match self { + Self::Memory => Self::Calldata, + Self::Calldata => Self::Returndata, + Self::Returndata => Self::Memory, + } + } + + /// Helper to format the title of the active buffer pane + pub fn title(&self, size: usize) -> String { + match self { + Self::Memory => format!("Memory (max expansion: {size} bytes)"), + Self::Calldata => format!("Calldata (size: {size} bytes)"), + Self::Returndata => format!("Returndata (size: {size} bytes)"), + } + } +} + +/// Container for buffer access information. +pub struct BufferAccess { + pub offset: usize, + pub len: usize, +} + +/// Container for read and write buffer access information. +pub struct BufferAccesses { + /// The read buffer kind and access information. + pub read: Option<(BufferKind, BufferAccess)>, + /// The only mutable buffer is the memory buffer, so don't store the buffer kind. + pub write: Option, +} + +/// A utility function to get the buffer access. +/// +/// The memory_access variable stores the index on the stack that indicates the buffer +/// 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 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 len, write memory offset, write memory len) +pub fn get_buffer_accesses(op: u8, stack: &[U256]) -> Option { + let buffer_access = match op { + opcode::KECCAK256 | opcode::RETURN | opcode::REVERT => { + (Some((BufferKind::Memory, 1, 2)), None) + } + opcode::CALLDATACOPY => (Some((BufferKind::Calldata, 2, 3)), Some((1, 3))), + opcode::RETURNDATACOPY => (Some((BufferKind::Returndata, 2, 3)), Some((1, 3))), + opcode::CALLDATALOAD => (Some((BufferKind::Calldata, 1, -1)), None), + opcode::CODECOPY => (None, Some((1, 3))), + opcode::EXTCODECOPY => (None, Some((2, 4))), + opcode::MLOAD => (Some((BufferKind::Memory, 1, -1)), None), + opcode::MSTORE => (None, Some((1, -1))), + opcode::MSTORE8 => (None, Some((1, -2))), + opcode::LOG0 | opcode::LOG1 | opcode::LOG2 | opcode::LOG3 | opcode::LOG4 => { + (Some((BufferKind::Memory, 1, 2)), None) + } + opcode::CREATE | opcode::CREATE2 => (Some((BufferKind::Memory, 2, 3)), None), + opcode::CALL | opcode::CALLCODE => (Some((BufferKind::Memory, 4, 5)), None), + opcode::DELEGATECALL | opcode::STATICCALL => (Some((BufferKind::Memory, 3, 4)), None), + opcode::MCOPY => (Some((BufferKind::Memory, 2, 3)), Some((1, 3))), + opcode::RETURNDATALOAD => (Some((BufferKind::Returndata, 1, -1)), None), + opcode::EOFCREATE => (Some((BufferKind::Memory, 3, 4)), None), + opcode::RETURNCONTRACT => (Some((BufferKind::Memory, 1, 2)), None), + opcode::DATACOPY => (None, Some((1, 3))), + opcode::EXTCALL | opcode::EXTSTATICCALL | opcode::EXTDELEGATECALL => { + (Some((BufferKind::Memory, 2, 3)), None) + } + _ => Default::default(), + }; + + let stack_len = stack.len(); + let get_size = |stack_index| match stack_index { + -2 => Some(1), + -1 => Some(32), + 0 => None, + 1.. => { + if (stack_index as usize) <= stack_len { + Some(stack[stack_len - stack_index as usize].saturating_to()) + } else { + None + } + } + _ => panic!("invalid stack index"), + }; + + 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, len) = b; + Some((buffer, BufferAccess { offset: get_size(offset)?, len: get_size(len)? })) + }); + let write_access = write.and_then(|b| { + let (offset, len) = b; + Some(BufferAccess { offset: get_size(offset)?, len: get_size(len)? }) + }); + Some(BufferAccesses { read: read_access, write: write_access }) + } else { + None + } +} diff --git a/crates/evm/core/src/lib.rs b/crates/evm/core/src/lib.rs index b6da4b49a..1a2ac4c4a 100644 --- a/crates/evm/core/src/lib.rs +++ b/crates/evm/core/src/lib.rs @@ -21,6 +21,7 @@ pub mod abi { mod ic; pub mod backend; +pub mod buffer; pub mod constants; pub mod decode; pub mod fork; diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 1458e3e46..257210897 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -20,6 +20,7 @@ interface Vm { struct AccountAccess { ChainInfo chainInfo; AccountAccessKind kind; address account; address accessor; bool initialized; uint256 oldBalance; uint256 newBalance; bytes deployedCode; uint256 value; bytes data; bool reverted; StorageAccess[] storageAccesses; uint64 depth; } struct StorageAccess { address account; bytes32 slot; bool isWrite; bytes32 previousValue; bytes32 newValue; bool reverted; } struct Gas { uint64 gasLimit; uint64 gasTotalUsed; uint64 gasMemoryUsed; int64 gasRefunded; uint64 gasRemaining; } + struct DebugStep { uint256[] stack; bytes memoryInput; uint8 opcode; uint64 depth; bool isOutOfGas; address contractAddr; } function _expectCheatcodeRevert() external; function _expectCheatcodeRevert(bytes4 revertData) external; function _expectCheatcodeRevert(bytes calldata revertData) external; @@ -438,12 +439,14 @@ interface Vm { function startBroadcast() external; function startBroadcast(address signer) external; function startBroadcast(uint256 privateKey) external; + function startDebugTraceRecording() external; function startMappingRecording() external; function startPrank(address msgSender) external; function startPrank(address msgSender, address txOrigin) external; function startSnapshotGas(string calldata name) external; function startSnapshotGas(string calldata group, string calldata name) external; function startStateDiffRecording() external; + function stopAndReturnDebugTraceRecording() external returns (DebugStep[] memory step); function stopAndReturnStateDiff() external returns (AccountAccess[] memory accountAccesses); function stopBroadcast() external; function stopExpectSafeMemory() external; diff --git a/testdata/default/cheats/RecordDebugTrace.t.sol b/testdata/default/cheats/RecordDebugTrace.t.sol new file mode 100644 index 000000000..ade2e7aaf --- /dev/null +++ b/testdata/default/cheats/RecordDebugTrace.t.sol @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract MStoreAndMLoadCaller { + uint256 public constant expectedValueInMemory = 999; + + uint256 public memPtr; // the memory pointer being used + + function storeAndLoadValueFromMemory() public returns (uint256) { + uint256 mPtr; + assembly { + mPtr := mload(0x40) // load free pointer + mstore(mPtr, expectedValueInMemory) + mstore(0x40, add(mPtr, 0x20)) + } + + // record & expose the memory pointer location + memPtr = mPtr; + + uint256 result = 123; + assembly { + // override with `expectedValueInMemory` + result := mload(mPtr) + } + return result; + } +} + +contract FirstLayer { + SecondLayer secondLayer; + + constructor(SecondLayer _secondLayer) { + secondLayer = _secondLayer; + } + + function callSecondLayer() public view returns (uint256) { + return secondLayer.endHere(); + } +} + +contract SecondLayer { + uint256 public constant endNumber = 123; + + function endHere() public view returns (uint256) { + return endNumber; + } +} + +contract OutOfGas { + uint256 dummyVal = 0; + + function consumeGas() public { + dummyVal += 1; + } + + function triggerOOG() public { + bytes memory encodedFunctionCall = abi.encodeWithSignature("consumeGas()", ""); + uint256 notEnoughGas = 50; + (bool success,) = address(this).call{gas: notEnoughGas}(encodedFunctionCall); + require(!success, "it should error out of gas"); + } +} + +contract RecordDebugTraceTest is DSTest { + Vm constant cheats = Vm(HEVM_ADDRESS); + /** + * The goal of this test is to ensure the debug steps provide the correct OPCODE with its stack + * and memory input used. The test checke MSTORE and MLOAD and ensure it records the expected + * stack and memory inputs. + */ + + function testDebugTraceCanRecordOpcodeWithStackAndMemoryData() public { + MStoreAndMLoadCaller testContract = new MStoreAndMLoadCaller(); + + cheats.startDebugTraceRecording(); + + uint256 val = testContract.storeAndLoadValueFromMemory(); + assertTrue(val == testContract.expectedValueInMemory()); + + Vm.DebugStep[] memory steps = cheats.stopAndReturnDebugTraceRecording(); + + bool mstoreCalled = false; + bool mloadCalled = false; + + for (uint256 i = 0; i < steps.length; i++) { + Vm.DebugStep memory step = steps[i]; + if ( + step.opcode == 0x52 /*MSTORE*/ && step.stack[0] == testContract.memPtr() // MSTORE offset + && step.stack[1] == testContract.expectedValueInMemory() // MSTORE val + ) { + mstoreCalled = true; + } + + if ( + step.opcode == 0x51 /*MLOAD*/ && step.stack[0] == testContract.memPtr() // MLOAD offset + && step.memoryInput.length == 32 // MLOAD should always load 32 bytes + && uint256(bytes32(step.memoryInput)) == testContract.expectedValueInMemory() // MLOAD value + ) { + mloadCalled = true; + } + } + + assertTrue(mstoreCalled); + assertTrue(mloadCalled); + } + + /** + * This test tests that the cheatcode can correctly record the depth of the debug steps. + * This is test by test -> FirstLayer -> SecondLayer and check that the + * depth of the FirstLayer and SecondLayer are all as expected. + */ + function testDebugTraceCanRecordDepth() public { + SecondLayer second = new SecondLayer(); + FirstLayer first = new FirstLayer(second); + + cheats.startDebugTraceRecording(); + + first.callSecondLayer(); + + Vm.DebugStep[] memory steps = cheats.stopAndReturnDebugTraceRecording(); + + bool goToDepthTwo = false; + bool goToDepthThree = false; + for (uint256 i = 0; i < steps.length; i++) { + Vm.DebugStep memory step = steps[i]; + + if (step.depth == 2) { + assertTrue(step.contractAddr == address(first), "must be first layer on depth 2"); + goToDepthTwo = true; + } + + if (step.depth == 3) { + assertTrue(step.contractAddr == address(second), "must be second layer on depth 3"); + goToDepthThree = true; + } + } + assertTrue(goToDepthTwo && goToDepthThree, "must have been to both first and second layer"); + } + + /** + * The goal of this test is to ensure it can return expected `isOutOfGas` flag. + * It is tested with out of gas result here. + */ + function testDebugTraceCanRecordOutOfGas() public { + OutOfGas testContract = new OutOfGas(); + + cheats.startDebugTraceRecording(); + + testContract.triggerOOG(); + + Vm.DebugStep[] memory steps = cheats.stopAndReturnDebugTraceRecording(); + + bool isOOG = false; + for (uint256 i = 0; i < steps.length; i++) { + Vm.DebugStep memory step = steps[i]; + + if (step.isOutOfGas) { + isOOG = true; + } + } + assertTrue(isOOG, "should OOG"); + } +} From 92702e9c0db4e76ddd7917fae4f74427a7e728f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Vincent?= <28714795+leovct@users.noreply.github.com> Date: Wed, 9 Oct 2024 18:05:56 +0200 Subject: [PATCH 082/100] feat(`forge doc`): include @custom natspec (#9075) * feat(`forge doc`): include @custom natspec * chore: make clippy happy * test: implement test for `is_custom` * chore: make rustfmt happy * doc: nit * chore: format custom tags --- crates/doc/src/parser/comment.rs | 44 +++++++++++++++++++++++++++++--- crates/doc/src/writer/as_doc.rs | 26 ++++++++++++++----- 2 files changed, 61 insertions(+), 9 deletions(-) diff --git a/crates/doc/src/parser/comment.rs b/crates/doc/src/parser/comment.rs index 13184112e..4954cd7cd 100644 --- a/crates/doc/src/parser/comment.rs +++ b/crates/doc/src/parser/comment.rs @@ -96,6 +96,11 @@ impl Comment { }, ) } + + /// Check if this comment is a custom tag. + pub fn is_custom(&self) -> bool { + matches!(self.tag, CommentTag::Custom(_)) + } } /// The collection of natspec [Comment] items. @@ -157,18 +162,18 @@ impl From> for Comments { pub struct CommentsRef<'a>(Vec<&'a Comment>); impl<'a> CommentsRef<'a> { - /// Filter a collection of comments and return only those that match a provided tag + /// Filter a collection of comments and return only those that match a provided tag. pub fn include_tag(&self, tag: CommentTag) -> Self { self.include_tags(&[tag]) } - /// Filter a collection of comments and return only those that match provided tags + /// Filter a collection of comments and return only those that match provided tags. pub fn include_tags(&self, tags: &[CommentTag]) -> Self { // Cloning only references here CommentsRef(self.iter().cloned().filter(|c| tags.contains(&c.tag)).collect()) } - /// Filter a collection of comments and return only those that do not match provided tags + /// Filter a collection of comments and return only those that do not match provided tags. pub fn exclude_tags(&self, tags: &[CommentTag]) -> Self { // Cloning only references here CommentsRef(self.iter().cloned().filter(|c| !tags.contains(&c.tag)).collect()) @@ -192,6 +197,11 @@ impl<'a> CommentsRef<'a> { .find(|c| matches!(c.tag, CommentTag::Inheritdoc)) .and_then(|c| c.value.split_whitespace().next()) } + + /// Filter a collection of comments and only return the custom tags. + pub fn get_custom_tags(&self) -> Self { + CommentsRef(self.iter().cloned().filter(|c| c.is_custom()).collect()) + } } impl<'a> From<&'a Comments> for CommentsRef<'a> { @@ -228,4 +238,32 @@ mod tests { assert_eq!(CommentTag::from_str("custom"), None); assert_eq!(CommentTag::from_str("sometag"), None); } + + #[test] + fn test_is_custom() { + // Test custom tag. + let custom_comment = Comment::new( + CommentTag::from_str("custom:test").unwrap(), + "dummy custom tag".to_owned(), + ); + assert!(custom_comment.is_custom(), "Custom tag should return true for is_custom"); + + // Test non-custom tags. + let non_custom_tags = [ + CommentTag::Title, + CommentTag::Author, + CommentTag::Notice, + CommentTag::Dev, + CommentTag::Param, + CommentTag::Return, + CommentTag::Inheritdoc, + ]; + for tag in non_custom_tags { + let comment = Comment::new(tag.clone(), "Non-custom comment".to_string()); + assert!( + !comment.is_custom(), + "Non-custom tag {tag:?} should return false for is_custom" + ); + } + } } diff --git a/crates/doc/src/writer/as_doc.rs b/crates/doc/src/writer/as_doc.rs index 50a847beb..d68f7a8e5 100644 --- a/crates/doc/src/writer/as_doc.rs +++ b/crates/doc/src/writer/as_doc.rs @@ -46,18 +46,32 @@ impl AsDoc for CommentsRef<'_> { // Write notice tags let notices = self.include_tag(CommentTag::Notice); - for notice in notices.iter() { - writer.writeln_raw(¬ice.value)?; + for n in notices.iter() { + writer.writeln_raw(&n.value)?; writer.writeln()?; } // Write dev tags let devs = self.include_tag(CommentTag::Dev); - for dev in devs.iter() { - writer.write_italic(&dev.value)?; + for d in devs.iter() { + writer.write_italic(&d.value)?; writer.writeln()?; } + // Write custom tags + let customs = self.get_custom_tags(); + if !customs.is_empty() { + writer.write_bold(&format!("Note{}:", if customs.len() == 1 { "" } else { "s" }))?; + for c in customs.iter() { + writer.writeln_raw(format!( + "{}{}", + if customs.len() == 1 { "" } else { "- " }, + &c.value + ))?; + writer.writeln()?; + } + } + Ok(writer.finish()) } } @@ -234,7 +248,7 @@ impl AsDoc for Document { func.params.iter().filter_map(|p| p.1.as_ref()).collect::>(); writer.try_write_param_table(CommentTag::Param, ¶ms, &item.comments)?; - // Write function parameter comments in a table + // Write function return parameter comments in a table let returns = func.returns.iter().filter_map(|p| p.1.as_ref()).collect::>(); writer.try_write_param_table( @@ -303,7 +317,7 @@ impl Document { let params = func.params.iter().filter_map(|p| p.1.as_ref()).collect::>(); writer.try_write_param_table(CommentTag::Param, ¶ms, &comments)?; - // Write function parameter comments in a table + // Write function return parameter comments in a table let returns = func.returns.iter().filter_map(|p| p.1.as_ref()).collect::>(); writer.try_write_param_table(CommentTag::Return, &returns, &comments)?; From f79c53c4e41958809ee1f3473466f184bb34c195 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 9 Oct 2024 20:25:35 +0400 Subject: [PATCH 083/100] chore: add alias (#9082) add alias --- crates/anvil/src/cmd.rs | 2 +- crates/cast/bin/cmd/call.rs | 2 +- crates/cast/bin/cmd/run.rs | 2 +- crates/common/src/evm.rs | 2 +- crates/config/src/lib.rs | 4 ++++ 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 76eb0510a..13182686a 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -581,7 +581,7 @@ pub struct AnvilEvmArgs { pub memory_limit: Option, /// Enable Alphanet features - #[arg(long, visible_alias = "alphanet")] + #[arg(long, visible_alias = "odyssey")] pub alphanet: bool, } diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index 9afc7ff5a..fe0a244df 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -74,7 +74,7 @@ pub struct CallArgs { json: bool, /// Enable Alphanet features. - #[arg(long)] + #[arg(long, alias = "odyssey")] pub alphanet: bool, #[command(subcommand)] diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index 7d7c922b4..9d04ed1e2 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -84,7 +84,7 @@ pub struct RunArgs { pub no_rate_limit: bool, /// Enables Alphanet features. - #[arg(long)] + #[arg(long, alias = "odyssey")] pub alphanet: bool, } diff --git a/crates/common/src/evm.rs b/crates/common/src/evm.rs index 30bcd4d09..e738cc6dd 100644 --- a/crates/common/src/evm.rs +++ b/crates/common/src/evm.rs @@ -147,7 +147,7 @@ pub struct EvmArgs { pub isolate: bool, /// Whether to enable Alphanet features. - #[arg(long)] + #[arg(long, alias = "odyssey")] #[serde(skip)] pub alphanet: bool, } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 648d40e66..d70a8dab4 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -2447,6 +2447,10 @@ impl Provider for BackwardsCompatTomlProvider

{ dict.insert("solc".to_string(), v); } } + + if let Some(v) = dict.remove("odyssey") { + dict.insert("alphanet".to_string(), v); + } map.insert(profile, dict); } Ok(map) From 4065d38cec998608a3e3042a7c577f72fb586ed4 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 10 Oct 2024 06:47:52 +0300 Subject: [PATCH 084/100] fix(chisel): final statement & fetch err with complex type fixes (#9081) * fix(chisel): consider assembly block return as final statement * Fix 4938 * Start from first assembly block when checking for return statement * Fix 6618 --- crates/chisel/src/dispatcher.rs | 19 +++++++++++++++++-- crates/chisel/src/executor.rs | 31 +++++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/crates/chisel/src/dispatcher.rs b/crates/chisel/src/dispatcher.rs index 0f2b7afe3..22ad242d0 100644 --- a/crates/chisel/src/dispatcher.rs +++ b/crates/chisel/src/dispatcher.rs @@ -10,7 +10,7 @@ use crate::{ }, session_source::SessionSource, }; -use alloy_json_abi::JsonAbi; +use alloy_json_abi::{InternalType, JsonAbi}; use alloy_primitives::{hex, Address}; use forge_fmt::FormatterConfig; use foundry_config::{Config, RpcEndpoint}; @@ -528,7 +528,22 @@ impl ChiselDispatcher { err.name, err.inputs .iter() - .map(|input| format_param!(input)) + .map(|input| { + let mut param_type = &input.ty; + // If complex type then add the name of custom type. + // see . + if input.is_complex_type() { + if let Some( + InternalType::Enum { contract: _, ty } | + InternalType::Struct { contract: _, ty } | + InternalType::Other { contract: _, ty }, + ) = &input.internal_type + { + param_type = ty; + } + } + format!("{} {}", param_type, input.name) + }) .collect::>() .join(",") )); diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index 16387a69b..b63ebe3e4 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -49,6 +49,22 @@ impl SessionSource { // Fetch the run function's body statement let run_func_statements = compiled.intermediate.run_func_body()?; + // Record loc of first yul block return statement (if any). + // This is used to decide which is the final statement within the `run()` method. + // see . + let last_yul_return = run_func_statements.iter().find_map(|statement| { + if let pt::Statement::Assembly { loc: _, dialect: _, flags: _, block } = statement { + if let Some(statement) = block.statements.last() { + if let pt::YulStatement::FunctionCall(yul_call) = statement { + if yul_call.id.name == "return" { + return Some(statement.loc()) + } + } + } + } + None + }); + // Find the last statement within the "run()" method and get the program // counter via the source map. if let Some(final_statement) = run_func_statements.last() { @@ -58,9 +74,13 @@ impl SessionSource { // // There is some code duplication within the arms due to the difference between // the [pt::Statement] type and the [pt::YulStatement] types. - let source_loc = match final_statement { + let mut source_loc = match final_statement { pt::Statement::Assembly { loc: _, dialect: _, flags: _, block } => { - if let Some(statement) = block.statements.last() { + // Select last non variable declaration statement, see . + let last_statement = block.statements.iter().rev().find(|statement| { + !matches!(statement, pt::YulStatement::VariableDeclaration(_, _, _)) + }); + if let Some(statement) = last_statement { statement.loc() } else { // In the case where the block is empty, attempt to grab the statement @@ -88,6 +108,13 @@ impl SessionSource { _ => final_statement.loc(), }; + // Consider yul return statement as final statement (if it's loc is lower) . + if let Some(yul_return) = last_yul_return { + if yul_return.end() < source_loc.start() { + source_loc = yul_return; + } + } + // Map the source location of the final statement of the `run()` function to its // corresponding runtime program counter let final_pc = { From 15fdb2a19ee2a038f7e72523c6a0b0c3cdc6c3e4 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 10 Oct 2024 13:40:55 +0300 Subject: [PATCH 085/100] feat(chisel): add eval command (#9086) --- crates/chisel/bin/main.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/crates/chisel/bin/main.rs b/crates/chisel/bin/main.rs index ba4d7e9bc..704252794 100644 --- a/crates/chisel/bin/main.rs +++ b/crates/chisel/bin/main.rs @@ -92,6 +92,12 @@ pub enum ChiselSubcommand { /// Clear all cached chisel sessions from the cache directory ClearCache, + + /// Simple evaluation of a command without entering the REPL + Eval { + /// The command to be evaluated. + command: String, + }, } fn main() -> eyre::Result<()> { @@ -168,6 +174,10 @@ async fn main_args(args: Chisel) -> eyre::Result<()> { } return Ok(()) } + Some(ChiselSubcommand::Eval { command }) => { + dispatch_repl_line(&mut dispatcher, command).await; + return Ok(()) + } None => { /* No chisel subcommand present; Continue */ } } From 0ec018d34dc43600201d07386eaed41f97887028 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Thu, 10 Oct 2024 19:06:07 +0200 Subject: [PATCH 086/100] feat: make `--gas-report` JSON output compatible (#9063) * add gas report generation in JSON * skip junit for now * add json formatted tests, trailing space and invalid formatting * avoid redundant modifications for calls count * replace existing tests with snapbox * clean up snapbox tests * merge in master * calls -> frames * use .is_jsonlines() --- crates/forge/bin/cmd/test/mod.rs | 71 +++-- crates/forge/src/gas_report.rs | 43 ++- crates/forge/tests/cli/cmd.rs | 523 +++++++++++++++++-------------- 3 files changed, 367 insertions(+), 270 deletions(-) diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index f83d336ed..7038fc0b5 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -5,7 +5,7 @@ use clap::{Parser, ValueHint}; use eyre::{Context, OptionExt, Result}; use forge::{ decode::decode_console_logs, - gas_report::GasReport, + gas_report::{GasReport, GasReportKind}, multi_runner::matches_contract, result::{SuiteResult, TestOutcome, TestStatus}, traces::{ @@ -112,7 +112,7 @@ pub struct TestArgs { json: bool, /// Output test results as JUnit XML report. - #[arg(long, conflicts_with = "json", help_heading = "Display options")] + #[arg(long, conflicts_with_all(["json", "gas_report"]), help_heading = "Display options")] junit: bool, /// Stop running tests after the first failure. @@ -474,6 +474,9 @@ impl TestArgs { trace!(target: "forge::test", "running all tests"); + // If we need to render to a serialized format, we should not print anything else to stdout. + let silent = self.gas_report && self.json; + let num_filtered = runner.matching_test_functions(filter).count(); if num_filtered != 1 && (self.debug.is_some() || self.flamegraph || self.flamechart) { let action = if self.flamegraph { @@ -500,7 +503,7 @@ impl TestArgs { } // Run tests in a non-streaming fashion and collect results for serialization. - if self.json { + if !self.gas_report && self.json { let mut results = runner.test_collect(filter); results.values_mut().for_each(|suite_result| { for test_result in suite_result.test_results.values_mut() { @@ -565,9 +568,13 @@ impl TestArgs { } let mut decoder = builder.build(); - let mut gas_report = self - .gas_report - .then(|| GasReport::new(config.gas_reports.clone(), config.gas_reports_ignore.clone())); + let mut gas_report = self.gas_report.then(|| { + GasReport::new( + config.gas_reports.clone(), + config.gas_reports_ignore.clone(), + if self.json { GasReportKind::JSON } else { GasReportKind::Markdown }, + ) + }); let mut gas_snapshots = BTreeMap::>::new(); @@ -588,30 +595,34 @@ impl TestArgs { self.flamechart; // Print suite header. - println!(); - for warning in suite_result.warnings.iter() { - eprintln!("{} {warning}", "Warning:".yellow().bold()); - } - if !tests.is_empty() { - let len = tests.len(); - let tests = if len > 1 { "tests" } else { "test" }; - println!("Ran {len} {tests} for {contract_name}"); + if !silent { + println!(); + for warning in suite_result.warnings.iter() { + eprintln!("{} {warning}", "Warning:".yellow().bold()); + } + if !tests.is_empty() { + let len = tests.len(); + let tests = if len > 1 { "tests" } else { "test" }; + println!("Ran {len} {tests} for {contract_name}"); + } } // Process individual test results, printing logs and traces when necessary. for (name, result) in tests { - shell::println(result.short_result(name))?; - - // We only display logs at level 2 and above - if verbosity >= 2 { - // We only decode logs from Hardhat and DS-style console events - let console_logs = decode_console_logs(&result.logs); - if !console_logs.is_empty() { - println!("Logs:"); - for log in console_logs { - println!(" {log}"); + if !silent { + shell::println(result.short_result(name))?; + + // We only display logs at level 2 and above + if verbosity >= 2 { + // We only decode logs from Hardhat and DS-style console events + let console_logs = decode_console_logs(&result.logs); + if !console_logs.is_empty() { + println!("Logs:"); + for log in console_logs { + println!(" {log}"); + } + println!(); } - println!(); } } @@ -653,7 +664,7 @@ impl TestArgs { } } - if !decoded_traces.is_empty() { + if !silent && !decoded_traces.is_empty() { shell::println("Traces:")?; for trace in &decoded_traces { shell::println(trace)?; @@ -760,7 +771,9 @@ impl TestArgs { } // Print suite summary. - shell::println(suite_result.summary())?; + if !silent { + shell::println(suite_result.summary())?; + } // Add the suite result to the outcome. outcome.results.insert(contract_name, suite_result); @@ -781,7 +794,7 @@ impl TestArgs { outcome.gas_report = Some(finalized); } - if !outcome.results.is_empty() { + if !silent && !outcome.results.is_empty() { shell::println(outcome.summary(duration))?; if self.summary { @@ -1063,7 +1076,7 @@ contract FooBarTest is DSTest { let call_cnts = gas_report .contracts .values() - .flat_map(|c| c.functions.values().flat_map(|f| f.values().map(|v| v.calls.len()))) + .flat_map(|c| c.functions.values().flat_map(|f| f.values().map(|v| v.frames.len()))) .collect::>(); // assert that all functions were called at least 100 times assert!(call_cnts.iter().all(|c| *c > 100)); diff --git a/crates/forge/src/gas_report.rs b/crates/forge/src/gas_report.rs index ff7628bd5..59c417b97 100644 --- a/crates/forge/src/gas_report.rs +++ b/crates/forge/src/gas_report.rs @@ -12,11 +12,25 @@ use serde::{Deserialize, Serialize}; use std::{collections::BTreeMap, fmt::Display}; use yansi::Paint; +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub enum GasReportKind { + Markdown, + JSON, +} + +impl Default for GasReportKind { + fn default() -> Self { + Self::Markdown + } +} + /// Represents the gas report for a set of contracts. #[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct GasReport { /// Whether to report any contracts. report_any: bool, + /// What kind of report to generate. + report_type: GasReportKind, /// Contracts to generate the report for. report_for: HashSet, /// Contracts to ignore when generating the report. @@ -30,11 +44,13 @@ impl GasReport { pub fn new( report_for: impl IntoIterator, ignore: impl IntoIterator, + report_kind: GasReportKind, ) -> Self { let report_for = report_for.into_iter().collect::>(); let ignore = ignore.into_iter().collect::>(); let report_any = report_for.is_empty() || report_for.contains("*"); - Self { report_any, report_for, ignore, ..Default::default() } + let report_type = report_kind; + Self { report_any, report_type, report_for, ignore, ..Default::default() } } /// Whether the given contract should be reported. @@ -113,7 +129,7 @@ impl GasReport { .or_default() .entry(signature.clone()) .or_default(); - gas_info.calls.push(trace.gas_used); + gas_info.frames.push(trace.gas_used); } } } @@ -125,11 +141,12 @@ impl GasReport { for contract in self.contracts.values_mut() { for sigs in contract.functions.values_mut() { for func in sigs.values_mut() { - func.calls.sort_unstable(); - func.min = func.calls.first().copied().unwrap_or_default(); - func.max = func.calls.last().copied().unwrap_or_default(); - func.mean = calc::mean(&func.calls); - func.median = calc::median_sorted(&func.calls); + func.frames.sort_unstable(); + func.min = func.frames.first().copied().unwrap_or_default(); + func.max = func.frames.last().copied().unwrap_or_default(); + func.mean = calc::mean(&func.frames); + func.median = calc::median_sorted(&func.frames); + func.calls = func.frames.len() as u64; } } } @@ -145,6 +162,11 @@ impl Display for GasReport { continue; } + if self.report_type == GasReportKind::JSON { + writeln!(f, "{}", serde_json::to_string(&contract).unwrap())?; + continue; + } + let mut table = Table::new(); table.load_preset(ASCII_MARKDOWN); table.set_header([Cell::new(format!("{name} contract")) @@ -176,7 +198,7 @@ impl Display for GasReport { Cell::new(gas_info.mean.to_string()).fg(Color::Yellow), Cell::new(gas_info.median.to_string()).fg(Color::Yellow), Cell::new(gas_info.max.to_string()).fg(Color::Red), - Cell::new(gas_info.calls.len().to_string()), + Cell::new(gas_info.calls.to_string()), ]); }) }); @@ -197,9 +219,12 @@ pub struct ContractInfo { #[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct GasInfo { - pub calls: Vec, + pub calls: u64, pub min: u64, pub mean: u64, pub median: u64, pub max: u64, + + #[serde(skip)] + pub frames: Vec, } diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 6c8bab928..61a5aeab8 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -8,6 +8,7 @@ use foundry_config::{ use foundry_test_utils::{ foundry_compilers::PathStyle, rpc::next_mainnet_etherscan_api_key, + snapbox::IntoData, util::{pretty_err, read_string, OutputExt, TestCommand}, }; use semver::Version; @@ -1434,11 +1435,7 @@ Compiler run successful! } ); -forgetest!(gas_report_all_contracts, |prj, cmd| { - prj.insert_ds_test(); - prj.add_source( - "Contracts.sol", - r#" +const GAS_REPORT_CONTRACTS: &str = r#" //SPDX-license-identifier: MIT import "./test.sol"; @@ -1521,9 +1518,11 @@ contract ContractThreeTest is DSTest { c3.baz(); } } - "#, - ) - .unwrap(); +"#; + +forgetest!(gas_report_all_contracts, |prj, cmd| { + prj.insert_ds_test(); + prj.add_source("Contracts.sol", GAS_REPORT_CONTRACTS).unwrap(); // report for all prj.write_config(Config { @@ -1532,34 +1531,121 @@ contract ContractThreeTest is DSTest { ..Default::default() }); - let first_out = cmd - .forge_fuse() + cmd.forge_fuse().arg("test").arg("--gas-report").assert_success().stdout_eq(str![[r#" +... +| src/Contracts.sol:ContractOne contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| foo | 45387 | 45387 | 45387 | 45387 | 1 | + + +| src/Contracts.sol:ContractThree contract | | | | | | +|------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103591 | 256 | | | | | +| Function Name | min | avg | median | max | # calls | +| baz | 260712 | 260712 | 260712 | 260712 | 1 | + + +| src/Contracts.sol:ContractTwo contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| bar | 64984 | 64984 | 64984 | 64984 | 1 | +... + +"#]]); + cmd.forge_fuse() .arg("test") .arg("--gas-report") + .arg("--json") .assert_success() - .get_output() - .stdout_lossy(); - assert!(first_out.contains("foo") && first_out.contains("bar") && first_out.contains("baz")); + .stdout_eq(str![[r#" +{"gas":103375,"size":255,"functions":{"foo":{"foo()":{"calls":1,"min":45387,"mean":45387,"median":45387,"max":45387}}}} +{"gas":103591,"size":256,"functions":{"baz":{"baz()":{"calls":1,"min":260712,"mean":260712,"median":260712,"max":260712}}}} +{"gas":103375,"size":255,"functions":{"bar":{"bar()":{"calls":1,"min":64984,"mean":64984,"median":64984,"max":64984}}}} +"#]].is_jsonlines()); prj.write_config(Config { gas_reports: (vec![]), ..Default::default() }); - let second_out = cmd - .forge_fuse() + cmd.forge_fuse().arg("test").arg("--gas-report").assert_success().stdout_eq(str![[r#" +... +| src/Contracts.sol:ContractOne contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| foo | 45387 | 45387 | 45387 | 45387 | 1 | + + +| src/Contracts.sol:ContractThree contract | | | | | | +|------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103591 | 256 | | | | | +| Function Name | min | avg | median | max | # calls | +| baz | 260712 | 260712 | 260712 | 260712 | 1 | + + +| src/Contracts.sol:ContractTwo contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| bar | 64984 | 64984 | 64984 | 64984 | 1 | +... + +"#]]); + cmd.forge_fuse() .arg("test") .arg("--gas-report") + .arg("--json") .assert_success() - .get_output() - .stdout_lossy(); - assert!(second_out.contains("foo") && second_out.contains("bar") && second_out.contains("baz")); + .stdout_eq(str![[r#" +{"gas":103375,"size":255,"functions":{"foo":{"foo()":{"calls":1,"min":45387,"mean":45387,"median":45387,"max":45387}}}} +{"gas":103591,"size":256,"functions":{"baz":{"baz()":{"calls":1,"min":260712,"mean":260712,"median":260712,"max":260712}}}} +{"gas":103375,"size":255,"functions":{"bar":{"bar()":{"calls":1,"min":64984,"mean":64984,"median":64984,"max":64984}}}} +"#]].is_jsonlines()); prj.write_config(Config { gas_reports: (vec!["*".to_string()]), ..Default::default() }); - let third_out = cmd - .forge_fuse() + cmd.forge_fuse().arg("test").arg("--gas-report").assert_success().stdout_eq(str![[r#" +... +| src/Contracts.sol:ContractOne contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| foo | 45387 | 45387 | 45387 | 45387 | 1 | + + +| src/Contracts.sol:ContractThree contract | | | | | | +|------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103591 | 256 | | | | | +| Function Name | min | avg | median | max | # calls | +| baz | 260712 | 260712 | 260712 | 260712 | 1 | + + +| src/Contracts.sol:ContractTwo contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| bar | 64984 | 64984 | 64984 | 64984 | 1 | +... + +"#]]); + cmd.forge_fuse() .arg("test") .arg("--gas-report") + .arg("--json") .assert_success() - .get_output() - .stdout_lossy(); - assert!(third_out.contains("foo") && third_out.contains("bar") && third_out.contains("baz")); + .stdout_eq(str![[r#" +{"gas":103375,"size":255,"functions":{"foo":{"foo()":{"calls":1,"min":45387,"mean":45387,"median":45387,"max":45387}}}} +{"gas":103591,"size":256,"functions":{"baz":{"baz()":{"calls":1,"min":260712,"mean":260712,"median":260712,"max":260712}}}} +{"gas":103375,"size":255,"functions":{"bar":{"bar()":{"calls":1,"min":64984,"mean":64984,"median":64984,"max":64984}}}} +"#]].is_jsonlines()); prj.write_config(Config { gas_reports: (vec![ @@ -1569,125 +1655,90 @@ contract ContractThreeTest is DSTest { ]), ..Default::default() }); - let fourth_out = cmd - .forge_fuse() + cmd.forge_fuse().arg("test").arg("--gas-report").assert_success().stdout_eq(str![[r#" +... +| src/Contracts.sol:ContractOne contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| foo | 45387 | 45387 | 45387 | 45387 | 1 | + + +| src/Contracts.sol:ContractThree contract | | | | | | +|------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103591 | 256 | | | | | +| Function Name | min | avg | median | max | # calls | +| baz | 260712 | 260712 | 260712 | 260712 | 1 | + + +| src/Contracts.sol:ContractTwo contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| bar | 64984 | 64984 | 64984 | 64984 | 1 | +... + +"#]]); + cmd.forge_fuse() .arg("test") .arg("--gas-report") + .arg("--json") .assert_success() - .get_output() - .stdout_lossy(); - assert!(fourth_out.contains("foo") && fourth_out.contains("bar") && fourth_out.contains("baz")); + .stdout_eq(str![[r#" +{"gas":103375,"size":255,"functions":{"foo":{"foo()":{"calls":1,"min":45387,"mean":45387,"median":45387,"max":45387}}}} +{"gas":103591,"size":256,"functions":{"baz":{"baz()":{"calls":1,"min":260712,"mean":260712,"median":260712,"max":260712}}}} +{"gas":103375,"size":255,"functions":{"bar":{"bar()":{"calls":1,"min":64984,"mean":64984,"median":64984,"max":64984}}}} +"#]].is_jsonlines()); }); forgetest!(gas_report_some_contracts, |prj, cmd| { prj.insert_ds_test(); - prj.add_source( - "Contracts.sol", - r#" -//SPDX-license-identifier: MIT - -import "./test.sol"; - -contract ContractOne { - int public i; - - constructor() { - i = 0; - } - - function foo() public{ - while(i<5){ - i++; - } - } -} - -contract ContractOneTest is DSTest { - ContractOne c1; - - function setUp() public { - c1 = new ContractOne(); - } - - function testFoo() public { - c1.foo(); - } -} - - -contract ContractTwo { - int public i; - - constructor() { - i = 0; - } - - function bar() public{ - while(i<50){ - i++; - } - } -} - -contract ContractTwoTest is DSTest { - ContractTwo c2; - - function setUp() public { - c2 = new ContractTwo(); - } - - function testBar() public { - c2.bar(); - } -} - -contract ContractThree { - int public i; - - constructor() { - i = 0; - } - - function baz() public{ - while(i<500){ - i++; - } - } -} - -contract ContractThreeTest is DSTest { - ContractThree c3; - - function setUp() public { - c3 = new ContractThree(); - } - - function testBaz() public { - c3.baz(); - } -} - "#, - ) - .unwrap(); + prj.add_source("Contracts.sol", GAS_REPORT_CONTRACTS).unwrap(); // 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").assert_success().get_output().stdout_lossy(); - assert!( - first_out.contains("foo") && !first_out.contains("bar") && !first_out.contains("baz"), - "foo:\n{first_out}" - ); + cmd.arg("test").arg("--gas-report").assert_success().stdout_eq(str![[r#" +... +| src/Contracts.sol:ContractOne contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| foo | 45387 | 45387 | 45387 | 45387 | 1 | +... + +"#]]); + cmd.forge_fuse() + .arg("test") + .arg("--gas-report") + .arg("--json") + .assert_success() + .stdout_eq(str![[r#" +{"gas":103375,"size":255,"functions":{"foo":{"foo()":{"calls":1,"min":45387,"mean":45387,"median":45387,"max":45387}}}} +"#]].is_jsonlines()); // 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").assert_success().get_output().stdout_lossy(); - assert!( - !second_out.contains("foo") && second_out.contains("bar") && !second_out.contains("baz"), - "bar:\n{second_out}" + cmd.arg("test").arg("--gas-report").assert_success().stdout_eq(str![[r#" +... +| src/Contracts.sol:ContractTwo contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| bar | 64984 | 64984 | 64984 | 64984 | 1 | +... + +"#]]); + cmd.forge_fuse().arg("test").arg("--gas-report").arg("--json").assert_success().stdout_eq( + str![[r#" +{"gas":103375,"size":255,"functions":{"bar":{"bar()":{"calls":1,"min":64984,"mean":64984,"median":64984,"max":64984}}}} +"#]].is_jsonlines(), ); // report for Three @@ -1696,104 +1747,30 @@ contract ContractThreeTest is DSTest { ..Default::default() }); cmd.forge_fuse(); - 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}" - ); + cmd.arg("test").arg("--gas-report").assert_success().stdout_eq(str![[r#" +... +| src/Contracts.sol:ContractThree contract | | | | | | +|------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103591 | 256 | | | | | +| Function Name | min | avg | median | max | # calls | +| baz | 260712 | 260712 | 260712 | 260712 | 1 | +... + +"#]]); + cmd.forge_fuse() + .arg("test") + .arg("--gas-report") + .arg("--json") + .assert_success() + .stdout_eq(str![[r#" +{"gas":103591,"size":256,"functions":{"baz":{"baz()":{"calls":1,"min":260712,"mean":260712,"median":260712,"max":260712}}}} +"#]].is_jsonlines()); }); forgetest!(gas_ignore_some_contracts, |prj, cmd| { prj.insert_ds_test(); - prj.add_source( - "Contracts.sol", - r#" -//SPDX-license-identifier: MIT - -import "./test.sol"; - -contract ContractOne { - int public i; - - constructor() { - i = 0; - } - - function foo() public{ - while(i<5){ - i++; - } - } -} - -contract ContractOneTest is DSTest { - ContractOne c1; - - function setUp() public { - c1 = new ContractOne(); - } - - function testFoo() public { - c1.foo(); - } -} - - -contract ContractTwo { - int public i; - - constructor() { - i = 0; - } - - function bar() public{ - while(i<50){ - i++; - } - } -} - -contract ContractTwoTest is DSTest { - ContractTwo c2; - - function setUp() public { - c2 = new ContractTwo(); - } - - function testBar() public { - c2.bar(); - } -} - -contract ContractThree { - int public i; - - constructor() { - i = 0; - } - - function baz() public{ - while(i<500){ - i++; - } - } -} - -contract ContractThreeTest is DSTest { - ContractThree c3; - - function setUp() public { - c3 = new ContractThree(); - } - - function testBaz() public { - c3.baz(); - } -} - "#, - ) - .unwrap(); + prj.add_source("Contracts.sol", GAS_REPORT_CONTRACTS).unwrap(); // ignore ContractOne prj.write_config(Config { @@ -1802,9 +1779,34 @@ contract ContractThreeTest is DSTest { ..Default::default() }); cmd.forge_fuse(); - 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")); + cmd.arg("test").arg("--gas-report").assert_success().stdout_eq(str![[r#" +... +| src/Contracts.sol:ContractThree contract | | | | | | +|------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103591 | 256 | | | | | +| Function Name | min | avg | median | max | # calls | +| baz | 260712 | 260712 | 260712 | 260712 | 1 | + + +| src/Contracts.sol:ContractTwo contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| bar | 64984 | 64984 | 64984 | 64984 | 1 | +... + +"#]]); + cmd.forge_fuse() + .arg("test") + .arg("--gas-report") + .arg("--json") + .assert_success() + .stdout_eq(str![[r#" +{"gas":103591,"size":256,"functions":{"baz":{"baz()":{"calls":1,"min":260712,"mean":260712,"median":260712,"max":260712}}}} +{"gas":103375,"size":255,"functions":{"bar":{"bar()":{"calls":1,"min":64984,"mean":64984,"median":64984,"max":64984}}}} +"#]].is_jsonlines()); // ignore ContractTwo cmd.forge_fuse(); @@ -1814,11 +1816,34 @@ contract ContractThreeTest is DSTest { ..Default::default() }); cmd.forge_fuse(); - 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") - ); + cmd.arg("test").arg("--gas-report").assert_success().stdout_eq(str![[r#" +... +| src/Contracts.sol:ContractOne contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| foo | 45387 | 45387 | 45387 | 45387 | 1 | + + +| src/Contracts.sol:ContractThree contract | | | | | | +|------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103591 | 256 | | | | | +| Function Name | min | avg | median | max | # calls | +| baz | 260712 | 260712 | 260712 | 260712 | 1 | +... + +"#]]); + cmd.forge_fuse() + .arg("test") + .arg("--gas-report") + .arg("--json") + .assert_success() + .stdout_eq(str![[r#" +{"gas":103375,"size":255,"functions":{"foo":{"foo()":{"calls":1,"min":45387,"mean":45387,"median":45387,"max":45387}}}} +{"gas":103591,"size":256,"functions":{"baz":{"baz()":{"calls":1,"min":260712,"mean":260712,"median":260712,"max":260712}}}} +"#]].is_jsonlines()); // ignore ContractThree cmd.forge_fuse(); @@ -1832,9 +1857,43 @@ contract ContractThreeTest is DSTest { ..Default::default() }); cmd.forge_fuse(); - 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")); + cmd.arg("test").arg("--gas-report").assert_success().stdout_eq(str![[r#" +... +| src/Contracts.sol:ContractOne contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| foo | 45387 | 45387 | 45387 | 45387 | 1 | + + +| src/Contracts.sol:ContractThree contract | | | | | | +|------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103591 | 256 | | | | | +| Function Name | min | avg | median | max | # calls | +| baz | 260712 | 260712 | 260712 | 260712 | 1 | + + +| src/Contracts.sol:ContractTwo contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| bar | 64984 | 64984 | 64984 | 64984 | 1 | +... + +"#]]); + cmd.forge_fuse() + .arg("test") + .arg("--gas-report") + .arg("--json") + .assert_success() + .stdout_eq(str![[r#" +{"gas":103375,"size":255,"functions":{"foo":{"foo()":{"calls":1,"min":45387,"mean":45387,"median":45387,"max":45387}}}} +{"gas":103591,"size":256,"functions":{"baz":{"baz()":{"calls":1,"min":260712,"mean":260712,"median":260712,"max":260712}}}} +{"gas":103375,"size":255,"functions":{"bar":{"bar()":{"calls":1,"min":64984,"mean":64984,"median":64984,"max":64984}}}} +"#]].is_jsonlines()); }); forgetest_init!(can_use_absolute_imports, |prj, cmd| { From da947073203cdf78fc9e27db12d850ac92dfabe5 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 10 Oct 2024 22:46:46 +0200 Subject: [PATCH 087/100] chore: replace criterion with divan (#9080) --- Cargo.lock | 237 +++++++++--------------- Cargo.toml | 52 +++--- Dockerfile | 2 +- crates/cast/Cargo.toml | 5 +- crates/cast/benches/vanity.rs | 53 ++---- crates/chisel/Cargo.toml | 5 - crates/chisel/benches/session_source.rs | 84 --------- crates/forge/Cargo.toml | 5 - crates/forge/benches/test.rs | 29 --- 9 files changed, 130 insertions(+), 342 deletions(-) delete mode 100644 crates/chisel/benches/session_source.rs delete mode 100644 crates/forge/benches/test.rs diff --git a/Cargo.lock b/Cargo.lock index 7438ab7f1..ff25d3ac6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -792,12 +792,6 @@ dependencies = [ "libc", ] -[[package]] -name = "anes" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" - [[package]] name = "anstream" version = "0.6.15" @@ -1929,9 +1923,61 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" [[package]] name = "cast" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" +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", + "divan", + "dunce", + "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 = "castaway" @@ -1978,7 +2024,6 @@ dependencies = [ "alloy-primitives", "alloy-rpc-types", "clap", - "criterion", "dirs 5.0.1", "eyre", "forge-fmt", @@ -2297,6 +2342,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "condtype" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf0a07a401f374238ab8e2f11a104d2851bf9ce711ec69804834de8af45c7af" + [[package]] name = "console" version = "0.15.8" @@ -2382,44 +2433,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "criterion" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" -dependencies = [ - "anes", - "cast", - "ciborium", - "clap", - "criterion-plot", - "futures", - "is-terminal", - "itertools 0.10.5", - "num-traits", - "once_cell", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_derive", - "serde_json", - "tinytemplate", - "tokio", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" -dependencies = [ - "cast", - "itertools 0.10.5", -] - [[package]] name = "crossbeam-channel" version = "0.5.13" @@ -2818,6 +2831,31 @@ dependencies = [ "syn 2.0.79", ] +[[package]] +name = "divan" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d567df2c9c2870a43f3f2bd65aaeb18dbce1c18f217c3e564b4fbaeb3ee56c" +dependencies = [ + "cfg-if", + "clap", + "condtype", + "divan-macros", + "libc", + "regex-lite", +] + +[[package]] +name = "divan-macros" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27540baf49be0d484d8f0130d7d8da3011c32a44d4fc873368154f1510e574a2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.79", +] + [[package]] name = "doctest-file" version = "1.0.0" @@ -3317,7 +3355,6 @@ dependencies = [ "clap_complete_fig", "clearscreen", "comfy-table", - "criterion", "dialoguer", "dunce", "ethers-contract-abigen", @@ -3540,64 +3577,6 @@ 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", - "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" @@ -6050,12 +6029,6 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" -[[package]] -name = "oorandom" -version = "11.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" - [[package]] name = "op-alloy-consensus" version = "0.3.3" @@ -6544,34 +6517,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" -[[package]] -name = "plotters" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" - -[[package]] -name = "plotters-svg" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" -dependencies = [ - "plotters-backend", -] - [[package]] name = "portable-atomic" version = "1.9.0" @@ -8604,16 +8549,6 @@ dependencies = [ "crunchy", ] -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - [[package]] name = "tinyvec" version = "1.8.0" diff --git a/Cargo.toml b/Cargo.toml index 9b660adbb..02b364e83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -223,13 +223,20 @@ alloy-trie = "0.6.0" op-alloy-rpc-types = "0.3.3" op-alloy-consensus = "0.3.3" -## misc -async-trait = "0.1" -auto_impl = "1" -walkdir = "2" +# macros proc-macro2 = "1.0.82" quote = "1.0" syn = "2.0" +async-trait = "0.1" +derive_more = { version = "1.0", features = ["full"] } +thiserror = "1" + +# bench +divan = "0.1" + +# misc +auto_impl = "1" +walkdir = "2" prettyplease = "0.2.20" ahash = "0.8" base64 = "0.22" @@ -237,47 +244,44 @@ chrono = { version = "0.4", default-features = false, features = [ "clock", "std", ] } +axum = "0.7" color-eyre = "0.6" -derive_more = { version = "1.0", features = ["full"] } +comfy-table = "7" dunce = "1" evm-disassembler = "0.5" +evmole = "0.5" eyre = "0.6" figment = "0.10" futures = "0.3" +hyper = "1.0" +indexmap = "2.2" itertools = "0.13" jsonpath_lib = "0.3" k256 = "0.13" -parking_lot = "0.12" mesc = "0.3" +num-format = "0.4.4" +parking_lot = "0.12" +proptest = "1" rand = "0.8" +rayon = "1" +reqwest = { version = "0.12", default-features = false } semver = "1" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["arbitrary_precision"] } similar-asserts = "1.5" +soldeer-commands = "=0.4.0" strum = "0.26" -thiserror = "1" +tempfile = "3.10" +tikv-jemallocator = "0.6" +tokio = "1" toml = "0.8" +tower = "0.4" +tower-http = "0.5" tracing = "0.1" tracing-subscriber = "0.3" -vergen = { version = "8", default-features = false } -indexmap = "2.2" -tikv-jemallocator = "0.6" url = "2" -num-format = "0.4.4" +vergen = { version = "8", default-features = false } 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 } -tower = "0.4" -tower-http = "0.5" -# soldeer -soldeer-commands = "=0.4.0" -proptest = "1" -comfy-table = "7" [patch.crates-io] ## alloy-core diff --git a/Dockerfile b/Dockerfile index 87e52b708..14415acbc 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 foundry-cast/aws-kms,forge/aws-kms \ + source $HOME/.profile && cargo build --release --features cast/aws-kms,forge/aws-kms \ && mkdir out \ && mv target/release/forge out/forge \ && mv target/release/cast out/cast \ diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 9f0b6758c..5fadb4967 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "foundry-cast" +name = "cast" description = "Command-line tool for performing Ethereum RPC calls" version.workspace = true @@ -89,8 +89,9 @@ tikv-jemallocator = { workspace = true, optional = true } [dev-dependencies] anvil.workspace = true foundry-test-utils.workspace = true + async-trait.workspace = true -criterion = "0.5" +divan.workspace = true [features] default = ["rustls", "jemalloc"] diff --git a/crates/cast/benches/vanity.rs b/crates/cast/benches/vanity.rs index 4311b5283..a39e4d8bc 100644 --- a/crates/cast/benches/vanity.rs +++ b/crates/cast/benches/vanity.rs @@ -1,51 +1,22 @@ -use criterion::{criterion_group, criterion_main, Criterion}; use rayon::prelude::*; -use std::{hint::black_box, time::Duration}; +use std::hint::black_box; #[path = "../bin/cmd/wallet/mod.rs"] #[allow(unused)] mod wallet; use wallet::vanity::*; -/// Benches `cast wallet vanity` -/// -/// Left or right matchers, with or without nonce do not change the outcome. -/// -/// Regex matchers get optimised away even with a black_box. -fn vanity(c: &mut Criterion) { - let mut g = c.benchmark_group("vanity"); - - g.sample_size(500); - g.noise_threshold(0.04); - g.measurement_time(Duration::from_secs(30)); - - g.bench_function("wallet generator", |b| b.iter(|| black_box(generate_wallet()))); - - // 1 - - g.sample_size(100); - g.noise_threshold(0.02); - - g.bench_function("match 1", |b| { - let m = LeftHexMatcher { left: vec![0] }; - let matcher = create_matcher(m); - b.iter(|| wallet_generator().find_any(|x| black_box(matcher(x)))) - }); - - // 2 - - g.sample_size(10); - g.noise_threshold(0.01); - g.measurement_time(Duration::from_secs(60)); - - g.bench_function("match 2", |b| { - let m = LeftHexMatcher { left: vec![0, 0] }; - let matcher = create_matcher(m); - b.iter(|| wallet_generator().find_any(|x| black_box(matcher(x)))) - }); +#[divan::bench] +fn vanity_wallet_generator() -> GeneratedWallet { + generate_wallet() +} - g.finish(); +#[divan::bench(args = [&[0][..]])] +fn vanity_match(bencher: divan::Bencher<'_, '_>, arg: &[u8]) { + let matcher = create_matcher(LeftHexMatcher { left: arg.to_vec() }); + bencher.bench_local(|| wallet_generator().find_any(|x| black_box(matcher(x)))); } -criterion_group!(vanity_benches, vanity); -criterion_main!(vanity_benches); +fn main() { + divan::main(); +} diff --git a/crates/chisel/Cargo.toml b/crates/chisel/Cargo.toml index 5d8ad4d2c..7868137d8 100644 --- a/crates/chisel/Cargo.toml +++ b/crates/chisel/Cargo.toml @@ -68,7 +68,6 @@ tracing.workspace = true tikv-jemallocator = { workspace = true, optional = true } [dev-dependencies] -criterion = { version = "0.5", features = ["async_tokio"] } serial_test = "3" tracing-subscriber.workspace = true @@ -78,7 +77,3 @@ rustls = ["reqwest/rustls-tls", "reqwest/rustls-tls-native-roots"] openssl = ["foundry-compilers/openssl", "reqwest/default-tls"] asm-keccak = ["alloy-primitives/asm-keccak"] jemalloc = ["dep:tikv-jemallocator"] - -[[bench]] -name = "session_source" -harness = false diff --git a/crates/chisel/benches/session_source.rs b/crates/chisel/benches/session_source.rs deleted file mode 100644 index 49d127c47..000000000 --- a/crates/chisel/benches/session_source.rs +++ /dev/null @@ -1,84 +0,0 @@ -use chisel::session_source::{SessionSource, SessionSourceConfig}; -use criterion::{criterion_group, Criterion}; -use foundry_compilers::solc::Solc; -use semver::Version; -use std::{hint::black_box, sync::LazyLock}; -use tokio::runtime::Runtime; - -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) { - let mut g = c.benchmark_group("session_source"); - - // Grab an empty session source - g.bench_function("clone_with_new_line", |b| { - b.iter(|| { - let session_source = get_empty_session_source(); - let new_line = String::from("uint a = 1"); - black_box(session_source.clone_with_new_line(new_line).unwrap()); - }) - }); -} - -/// Benchmark for the `build` function in [SessionSource] -fn build(c: &mut Criterion) { - let mut g = c.benchmark_group("session_source"); - - g.bench_function("build", |b| { - b.iter(|| { - // Grab an empty session source - let mut session_source = get_empty_session_source(); - black_box(session_source.build().unwrap()) - }) - }); -} - -/// Benchmark for the `execute` function in [SessionSource] -fn execute(c: &mut Criterion) { - let mut g = c.benchmark_group("session_source"); - - g.bench_function("execute", |b| { - b.to_async(rt()).iter(|| async { - // Grab an empty session source - let mut session_source = get_empty_session_source(); - black_box(session_source.execute().await.unwrap()) - }) - }); -} - -/// Benchmark for the `inspect` function in [SessionSource] -fn inspect(c: &mut Criterion) { - let mut g = c.benchmark_group("session_source"); - - g.bench_function("inspect", |b| { - b.to_async(rt()).iter(|| async { - // Grab an empty session source - let mut session_source = get_empty_session_source(); - // Add a uint named "a" with value 1 to the session source - session_source.with_run_code("uint a = 1"); - black_box(session_source.inspect("a").await.unwrap()) - }) - }); -} - -/// Helper function for getting an empty [SessionSource] with default configuration -fn get_empty_session_source() -> SessionSource { - SessionSource::new(SOLC.clone(), SessionSourceConfig::default()) -} - -fn rt() -> Runtime { - Runtime::new().unwrap() -} - -fn main() { - // Install before benches if not present - let _ = LazyLock::force(&SOLC); - - session_source_benches(); - - Criterion::default().configure_from_args().final_summary() -} - -criterion_group!(session_source_benches, clone_with_new_line, build, execute, inspect); diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 0e359191a..1b716497a 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -117,7 +117,6 @@ anvil.workspace = true foundry-test-utils.workspace = true mockall = "0.13" -criterion = "0.5" globset = "0.4" paste = "1.0" path-slash = "0.2" @@ -143,7 +142,3 @@ asm-keccak = ["alloy-primitives/asm-keccak"] jemalloc = ["dep:tikv-jemallocator"] aws-kms = ["foundry-wallets/aws-kms"] isolate-by-default = ["foundry-config/isolate-by-default"] - -[[bench]] -name = "test" -harness = false diff --git a/crates/forge/benches/test.rs b/crates/forge/benches/test.rs deleted file mode 100644 index 593bce3d3..000000000 --- a/crates/forge/benches/test.rs +++ /dev/null @@ -1,29 +0,0 @@ -use criterion::{criterion_group, criterion_main, Criterion}; -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) { - setup_forge_remote("transmissions11/solmate") -} - -fn forge_test_benchmark(c: &mut Criterion) { - let (prj, _) = built_solmate(); - - let mut group = c.benchmark_group("forge test"); - group.sample_size(10); - group.bench_function("solmate", |b| { - let mut cmd = prj.forge_command(); - cmd.arg("test"); - b.iter(|| { - let output = cmd.execute(); - println!("stdout:\n{}", lossy_string(&output.stdout)); - println!("\nstderr:\n{}", lossy_string(&output.stderr)); - }); - }); -} - -criterion_group!(benches, forge_test_benchmark); -criterion_main!(benches); From 0ed43cefa9c9cb93fa9f9b18ff73d04c1a07cf56 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 10 Oct 2024 22:58:28 +0200 Subject: [PATCH 088/100] chore: reduce length of a common error message (#9089) --- crates/cli/src/utils/mod.rs | 5 +---- crates/forge/tests/cli/cmd.rs | 3 --- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index 736793e67..af3c5e0a5 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -463,10 +463,7 @@ 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" +ignore them in the `.gitignore` file, or run this command again with the `--no-commit` flag." )) } } diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 61a5aeab8..d82600b62 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -261,9 +261,6 @@ 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 From 44b2d754122c7ae98c03539e43b51efea6986c03 Mon Sep 17 00:00:00 2001 From: m4rio <92288535+mario-eth@users.noreply.github.com> Date: Fri, 11 Oct 2024 11:12:07 +0300 Subject: [PATCH 089/100] feat: update to Soldeer v0.4.1 (#9092) Hotfix #212 --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ff25d3ac6..b121e8b96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8129,9 +8129,9 @@ dependencies = [ [[package]] name = "soldeer-commands" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "236ae9bfdac074b0cf30caef23e59265b8f609334be3941727c1babcbb04c9cf" +checksum = "8daf7e07f2b6002f8410811915a2f6142f2d1084764dd88cba3f4ebf22232975" dependencies = [ "clap", "cliclack", @@ -8143,9 +8143,9 @@ dependencies = [ [[package]] name = "soldeer-core" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d84a27730c18b4ae2bce01a81bec17397009e3dd739e26f9c10ed8807a97d93e" +checksum = "ea249d0281f3755c3c2b095ad94a554a782cc29138f46c407b8080cfd3918996" dependencies = [ "bon", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 02b364e83..322152f87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -269,7 +269,7 @@ semver = "1" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["arbitrary_precision"] } similar-asserts = "1.5" -soldeer-commands = "=0.4.0" +soldeer-commands = "=0.4.1" strum = "0.26" tempfile = "3.10" tikv-jemallocator = "0.6" From 7a9ebf9ccbce2957762ef1b3f4623efb76ef0306 Mon Sep 17 00:00:00 2001 From: Giovanni Napoli Date: Fri, 11 Oct 2024 16:55:02 +0200 Subject: [PATCH 090/100] feat: bump alpine to `3.20.3` (#9094) * feat: bump alpine to `3.20.3` * feat: alpine v`3.20` --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 14415acbc..b425f6ef4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.4 -FROM alpine:3.18 as build-environment +FROM alpine:3.20 as build-environment ARG TARGETARCH WORKDIR /opt @@ -30,7 +30,7 @@ RUN --mount=type=cache,target=/root/.cargo/registry --mount=type=cache,target=/r && strip out/chisel \ && strip out/anvil; -FROM alpine:3.18 as foundry-client +FROM alpine:3.20 as foundry-client RUN apk add --no-cache linux-headers git gcompat libstdc++ From 88e18ef8bb096345ba19165da73c752215d4f19b Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 11 Oct 2024 17:08:10 +0200 Subject: [PATCH 091/100] chore: update chains (#9097) --- Cargo.lock | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b121e8b96..0aec92430 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,10 +74,11 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-chains" -version = "0.1.36" +version = "0.1.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94c225801d42099570d0674701dddd4142f0ef715282aeb5985042e2ec962df7" +checksum = "156bfc5dcd52ef9a5f33381701fa03310317e14c65093a9430d3e3557b08dcd3" dependencies = [ + "alloy-primitives", "num_enum", "serde", "strum", From bcacf39e43812e50a124e3ba60d1becd9866534d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Vincent?= <28714795+leovct@users.noreply.github.com> Date: Fri, 11 Oct 2024 20:44:32 +0200 Subject: [PATCH 092/100] feat(cheatcodes): implement new cheatcode to check if a string contains another string (#9085) * feat: implement new cheatcode to check if a string contains another string * chore: make clippy and rustfmt happy * chore: vm.contains should return a boolean * Update testdata/cheats/Vm.sol Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Update crates/cheatcodes/spec/src/vm.rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: update `cheatcodes.json` * chore: update var names * chore: rename to `vm.contains` * Update crates/cheatcodes/spec/src/vm.rs Co-authored-by: Matt Solomon * Update crates/cheatcodes/spec/src/vm.rs Co-authored-by: Matt Solomon * chore: address PR comments --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Matt Solomon --- crates/cheatcodes/assets/cheatcodes.json | 20 ++++++++++++++++++++ crates/cheatcodes/spec/src/vm.rs | 3 +++ crates/cheatcodes/src/string.rs | 8 ++++++++ testdata/cheats/Vm.sol | 1 + testdata/default/cheats/StringUtils.t.sol | 6 ++++++ 5 files changed, 38 insertions(+) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index d64df4860..b41f73667 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -3347,6 +3347,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "contains", + "description": "Returns true if `search` is found in `subject`, false otherwise.", + "declaration": "function contains(string calldata subject, string calldata search) external returns (bool result);", + "visibility": "external", + "mutability": "", + "signature": "contains(string,string)", + "selector": "0x3fb18aec", + "selectorBytes": [ + 63, + 177, + 138, + 236 + ] + }, + "group": "string", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "cool", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 761d64e9b..2a342a044 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1976,6 +1976,9 @@ interface Vm { /// Returns 0 in case of an empty `key`. #[cheatcode(group = String)] function indexOf(string calldata input, string calldata key) external pure returns (uint256); + /// Returns true if `search` is found in `subject`, false otherwise. + #[cheatcode(group = String)] + function contains(string calldata subject, string calldata search) external returns (bool result); // ======== JSON Parsing and Manipulation ======== diff --git a/crates/cheatcodes/src/string.rs b/crates/cheatcodes/src/string.rs index e7435d541..a4c06eef6 100644 --- a/crates/cheatcodes/src/string.rs +++ b/crates/cheatcodes/src/string.rs @@ -144,6 +144,14 @@ impl Cheatcode for indexOfCall { } } +// contains +impl Cheatcode for containsCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { subject, search } = self; + Ok(subject.contains(search).abi_encode()) + } +} + pub(super) fn parse(s: &str, ty: &DynSolType) -> Result { parse_value(s, ty).map(|v| v.abi_encode()) } diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 257210897..e47a79cc6 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -162,6 +162,7 @@ interface Vm { function computeCreate2Address(bytes32 salt, bytes32 initCodeHash, address deployer) external pure returns (address); function computeCreate2Address(bytes32 salt, bytes32 initCodeHash) external pure returns (address); function computeCreateAddress(address deployer, uint256 nonce) external pure returns (address); + function contains(string calldata subject, string calldata search) external returns (bool result); function cool(address target) external; function copyFile(string calldata from, string calldata to) external returns (uint64 copied); function copyStorage(address from, address to) external; diff --git a/testdata/default/cheats/StringUtils.t.sol b/testdata/default/cheats/StringUtils.t.sol index b65346a7a..256d65302 100644 --- a/testdata/default/cheats/StringUtils.t.sol +++ b/testdata/default/cheats/StringUtils.t.sol @@ -51,4 +51,10 @@ contract StringManipulationTest is DSTest { assertEq(vm.indexOf(input, key3), 0); assertEq(vm.indexOf(input, key4), type(uint256).max); } + + function testContains() public { + string memory subject = "this is a test"; + assert(vm.contains(subject, "test")); + assert(!vm.contains(subject, "foundry")); + } } From fd565286372b42a19b22d6a756e59a1b60a4135c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 13 Oct 2024 03:12:03 +0000 Subject: [PATCH 093/100] chore(deps): weekly `cargo update` (#9100) Locking 35 packages to latest compatible versions Updating alloy-dyn-abi v0.8.6 -> v0.8.7 Updating alloy-json-abi v0.8.6 -> v0.8.7 Updating alloy-primitives v0.8.6 -> v0.8.7 Updating alloy-sol-macro v0.8.6 -> v0.8.7 Updating alloy-sol-macro-expander v0.8.6 -> v0.8.7 Updating alloy-sol-macro-input v0.8.6 -> v0.8.7 Updating alloy-sol-type-parser v0.8.6 -> v0.8.7 Updating alloy-sol-types v0.8.6 -> v0.8.7 Updating async-compression v0.4.13 -> v0.4.14 Updating aws-sdk-kms v1.46.0 -> v1.47.0 Updating aws-sdk-sso v1.45.0 -> v1.46.0 Updating aws-sdk-ssooidc v1.46.0 -> v1.47.0 Updating aws-sdk-sts v1.45.0 -> v1.46.0 Updating aws-smithy-runtime v1.7.1 -> v1.7.2 Updating cc v1.1.28 -> v1.1.30 Updating clap v4.5.19 -> v4.5.20 Updating clap_builder v4.5.19 -> v4.5.20 Updating clap_complete v4.5.32 -> v4.5.33 Updating derive_builder v0.20.1 -> v0.20.2 Updating derive_builder_core v0.20.1 -> v0.20.2 Updating derive_builder_macro v0.20.1 -> v0.20.2 Updating js-sys v0.3.70 -> v0.3.72 Updating lru v0.12.4 -> v0.12.5 Updating newtype-uuid v1.1.0 -> v1.1.2 Updating proc-macro2 v1.0.86 -> v1.0.87 Updating scc v2.2.0 -> v2.2.1 Updating sdd v3.0.3 -> v3.0.4 Updating syn-solidity v0.8.6 -> v0.8.7 Updating wasm-bindgen v0.2.93 -> v0.2.95 Updating wasm-bindgen-backend v0.2.93 -> v0.2.95 Updating wasm-bindgen-futures v0.4.43 -> v0.4.45 Updating wasm-bindgen-macro v0.2.93 -> v0.2.95 Updating wasm-bindgen-macro-support v0.2.93 -> v0.2.95 Updating wasm-bindgen-shared v0.2.93 -> v0.2.95 Updating web-sys v0.3.70 -> v0.3.72 note: pass `--verbose` to see 10 unchanged dependencies behind latest Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> --- Cargo.lock | 148 +++---- .../fixtures/SimpleContractTestVerbose.json | 416 +++++++++--------- 2 files changed, 281 insertions(+), 283 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0aec92430..e597a7a91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -123,9 +123,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1109c57718022ac84c194f775977a534e1b3969b405e55693a61c42187cc0612" +checksum = "f95d76a38cae906fd394a5afb0736aaceee5432efe76addfd71048e623e208af" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -200,9 +200,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cc0e59c803dd44d14fc0cfa9fea1f74cfa8fd9fb60ca303ced390c58c28d4e" +checksum = "03c66eec1acdd96b39b995b8f5ee5239bc0c871d62c527ae1ac9fd1d7fecd455" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -260,9 +260,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a289ffd7448036f2f436b377f981c79ce0b2090877bad938d43387dc09931877" +checksum = "8ecb848c43f6b06ae3de2e4a67496cbbabd78ae87db0f1248934f15d76192c6a" dependencies = [ "alloy-rlp", "arbitrary", @@ -606,9 +606,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0409e3ba5d1de409997a7db8b8e9d679d52088c1dee042a85033affd3cadeab4" +checksum = "661c516eb1fa3294cc7f2fb8955b3b609d639c282ac81a4eedb14d3046db503a" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -620,9 +620,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18372ef450d59f74c7a64a738f546ba82c92f816597fed1802ef559304c81f1" +checksum = "ecbabb8fc3d75a0c2cea5215be22e7a267e3efde835b0f2a8922f5e3f5d47683" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -639,9 +639,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7bad89dd0d5f109e8feeaf787a9ed7a05a91a9a0efc6687d147a70ebca8eff7" +checksum = "16517f2af03064485150d89746b8ffdcdbc9b6eeb3d536fb66efd7c2846fbc75" dependencies = [ "alloy-json-abi", "const-hex", @@ -656,9 +656,9 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbd3548d5262867c2c4be6223fe4f2583e21ade0ca1c307fd23bc7f28fca479e" +checksum = "c07ebb0c1674ff8cbb08378d7c2e0e27919d2a2dae07ad3bca26174deda8d389" dependencies = [ "serde", "winnow", @@ -666,9 +666,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aa666f1036341b46625e72bd36878bf45ad0185f1b88601223e1ec6ed4b72b1" +checksum = "8e448d879903624863f608c552d10efb0e0905ddbee98b0049412799911eb062" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -1129,9 +1129,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e614738943d3f68c628ae3dbce7c3daffb196665f82f8c8ea6b65de73c79429" +checksum = "998282f8f49ccd6116b0ed8a4de0fbd3151697920e7c7533416d6e25e76434a7" dependencies = [ "flate2", "futures-core", @@ -1321,9 +1321,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.46.0" +version = "1.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33590e8d45206fdc4273ded8a1f292bcceaadd513037aa790fc67b237bc30ee" +checksum = "564a597a3c71a957d60a2e4c62c93d78ee5a0d636531e15b760acad983a5c18e" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1343,9 +1343,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.45.0" +version = "1.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33ae899566f3d395cbf42858e433930682cc9c1889fa89318896082fef45efb" +checksum = "0dc2faec3205d496c7e57eff685dd944203df7ce16a4116d0281c44021788a7b" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1365,9 +1365,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.46.0" +version = "1.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f39c09e199ebd96b9f860b0fce4b6625f211e064ad7c8693b72ecf7ef03881e0" +checksum = "c93c241f52bc5e0476e259c953234dab7e2a35ee207ee202e86c0095ec4951dc" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1387,9 +1387,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.45.0" +version = "1.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d95f93a98130389eb6233b9d615249e543f6c24a68ca1f109af9ca5164a8765" +checksum = "b259429be94a3459fa1b00c5684faee118d74f9577cc50aebadc36e507c63b5f" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1483,9 +1483,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1ce695746394772e7000b39fe073095db6d45a862d0767dd5ad0ac0d7f8eb87" +checksum = "a065c0fe6fdbdf9f11817eb68582b2ab4aff9e9c39e986ae48f7ec576c6322db" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -1991,9 +1991,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.28" +version = "1.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1" +checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" dependencies = [ "shlex", ] @@ -2105,9 +2105,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.19" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", @@ -2115,9 +2115,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.19" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstream", "anstyle", @@ -2130,9 +2130,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.32" +version = "4.5.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74a01f4f9ee6c066d42a1c8dedf0dcddad16c72a8981a309d6398de3a75b0c39" +checksum = "9646e2e245bf62f45d39a0f3f36f1171ad1ea0d6967fd114bca72cb02a8fcdfb" dependencies = [ "clap", ] @@ -2663,18 +2663,18 @@ dependencies = [ [[package]] name = "derive_builder" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd33f37ee6a119146a1781d3356a7c26028f83d779b2e04ecd45fdc75c76877b" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" dependencies = [ "derive_builder_macro", ] [[package]] name = "derive_builder_core" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7431fa049613920234f22c47fdc33e6cf3ee83067091ea4277a3f8c4587aae38" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ "darling", "proc-macro2", @@ -2684,9 +2684,9 @@ dependencies = [ [[package]] name = "derive_builder_macro" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4abae7035bf79b9877b779505d8cf3749285b80c43941eda66604841889451dc" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", "syn 2.0.79", @@ -4673,10 +4673,6 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "allocator-api2", -] [[package]] name = "hashbrown" @@ -4684,6 +4680,8 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" dependencies = [ + "allocator-api2", + "equivalent", "foldhash", "serde", ] @@ -5280,9 +5278,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] @@ -5493,11 +5491,11 @@ dependencies = [ [[package]] name = "lru" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.0", ] [[package]] @@ -5753,9 +5751,9 @@ checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "newtype-uuid" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526cb7c660872e401beaf3297f95f548ce3b4b4bdd8121b7c0713771d7c4a6e" +checksum = "4f4933943834e236c864a48aefdc2da43885dbd5eb77bff3ab20f31e0c3146f5" dependencies = [ "uuid 1.10.0", ] @@ -6661,9 +6659,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" dependencies = [ "unicode-ident", ] @@ -7602,9 +7600,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836f1e0f4963ef5288b539b643b35e043e76a32d0f4e47e67febf69576527f50" +checksum = "553f8299af7450cda9a52d3a370199904e7a46b5ffd1bef187c4a6af3bb6db69" dependencies = [ "sdd", ] @@ -7678,9 +7676,9 @@ dependencies = [ [[package]] name = "sdd" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a7b59a5d9b0099720b417b6325d91a52cbf5b3dcb5041d864be53eefa58abc" +checksum = "49c1eeaf4b6a87c7479688c6d52b9f1153cedd3c489300564f932b065c6eab95" [[package]] name = "sec1" @@ -8343,9 +8341,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a850d65181df41b83c6be01a7d91f5e9377c43d48faa5af7d95816f437f5a3" +checksum = "20e7b52ad118b2153644eea95c6fc740b6c1555b2344fdab763fc9de4075f665" dependencies = [ "paste", "proc-macro2", @@ -9252,9 +9250,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", "once_cell", @@ -9263,9 +9261,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", @@ -9278,9 +9276,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" dependencies = [ "cfg-if", "js-sys", @@ -9290,9 +9288,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -9300,9 +9298,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", @@ -9313,9 +9311,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "wasm-streams" @@ -9395,9 +9393,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/crates/forge/tests/fixtures/SimpleContractTestVerbose.json b/crates/forge/tests/fixtures/SimpleContractTestVerbose.json index adc700d4d..81803d949 100644 --- a/crates/forge/tests/fixtures/SimpleContractTestVerbose.json +++ b/crates/forge/tests/fixtures/SimpleContractTestVerbose.json @@ -1,217 +1,217 @@ { - "src/Simple.t.sol:SimpleContractTest": { - "duration": "{...}", - "test_results": { - "test()": { - "status": "Success", - "reason": null, - "counterexample": null, - "logs": [ + "src/Simple.t.sol:SimpleContractTest": { + "duration": "{...}", + "test_results": { + "test()": { + "status": "Success", + "reason": null, + "counterexample": null, + "logs": [ + { + "address": "0x000000000000000000636f6e736f6c652e6c6f67", + "topics": [ + "0x41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000f56616c7565207365743a20203130300000000000000000000000000000000000" + } + ], + "decoded_logs": [ + "Value set: 100" + ], + "kind": { + "Unit": { + "gas": "{...}" + } + }, + "traces": [ + [ + "Deployment", + { + "arena": [ + { + "parent": null, + "children": [], + "idx": 0, + "trace": { + "depth": 0, + "success": true, + "caller": "0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38", + "address": "0x7fa9385be102ac3eac297483dd6233d62b3e1496", + "maybe_precompile": false, + "selfdestruct_address": null, + "selfdestruct_refund_target": null, + "selfdestruct_transferred_value": null, + "kind": "CREATE", + "value": "0x0", + "data": "{...}", + "output": "{...}", + "gas_used": "{...}", + "gas_limit": "{...}", + "status": "Return", + "steps": [], + "decoded": { + "label": null, + "return_data": null, + "call_data": null + } + }, + "logs": [], + "ordering": [] + } + ] + } + ], + [ + "Execution", + { + "arena": [ + { + "parent": null, + "children": [ + 1, + 2, + 3 + ], + "idx": 0, + "trace": { + "depth": 0, + "success": true, + "caller": "0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38", + "address": "0x7fa9385be102ac3eac297483dd6233d62b3e1496", + "maybe_precompile": null, + "selfdestruct_address": null, + "selfdestruct_refund_target": null, + "selfdestruct_transferred_value": null, + "kind": "CALL", + "value": "0x0", + "data": "0xf8a8fd6d", + "output": "0x", + "gas_used": "{...}", + "gas_limit": "{...}", + "status": "Stop", + "steps": [], + "decoded": { + "label": null, + "return_data": null, + "call_data": null + } + }, + "logs": [], + "ordering": [ + { + "Call": 0 + }, { - "address": "0x000000000000000000636F6e736F6c652e6c6f67", - "topics": [ - "0x41304facd9323d75b11bcdd609cb38effffdb05710f7caf0e9b16c6d9d709f50" - ], - "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000f56616c7565207365743a20203130300000000000000000000000000000000000" + "Call": 1 + }, + { + "Call": 2 } - ], - "decoded_logs": [ - "Value set: 100" - ], - "kind": { - "Unit": { - "gas": "{...}" + ] + }, + { + "parent": 0, + "children": [], + "idx": 1, + "trace": { + "depth": 1, + "success": true, + "caller": "0x7fa9385be102ac3eac297483dd6233d62b3e1496", + "address": "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", + "maybe_precompile": false, + "selfdestruct_address": null, + "selfdestruct_refund_target": null, + "selfdestruct_transferred_value": null, + "kind": "CREATE", + "value": "0x0", + "data": "{...}", + "output": "{...}", + "gas_used": "{...}", + "gas_limit": "{...}", + "status": "Return", + "steps": [], + "decoded": { + "label": null, + "return_data": null, + "call_data": null } + }, + "logs": [], + "ordering": [] }, - "traces": [ - [ - "Deployment", - { - "arena": [ - { - "parent": null, - "children": [], - "idx": 0, - "trace": { - "depth": 0, - "success": true, - "caller": "0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38", - "address": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", - "maybe_precompile": false, - "selfdestruct_address": null, - "selfdestruct_refund_target": null, - "selfdestruct_transferred_value": null, - "kind": "CREATE", - "value": "0x0", - "data": "{...}", - "output": "{...}", - "gas_used": "{...}", - "gas_limit": "{...}", - "status": "Return", - "steps": [], - "decoded": { - "label": null, - "return_data": null, - "call_data": null - } - }, - "logs": [], - "ordering": [] - } - ] - } - ], - [ - "Execution", - { - "arena": [ - { - "parent": null, - "children": [ - 1, - 2, - 3 - ], - "idx": 0, - "trace": { - "depth": 0, - "success": true, - "caller": "0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38", - "address": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", - "maybe_precompile": null, - "selfdestruct_address": null, - "selfdestruct_refund_target": null, - "selfdestruct_transferred_value": null, - "kind": "CALL", - "value": "0x0", - "data": "0xf8a8fd6d", - "output": "0x", - "gas_used": "{...}", - "gas_limit": "{...}", - "status": "Stop", - "steps": [], - "decoded": { - "label": null, - "return_data": null, - "call_data": null - } - }, - "logs": [], - "ordering": [ - { - "Call": 0 - }, - { - "Call": 1 - }, - { - "Call": 2 - } - ] - }, - { - "parent": 0, - "children": [], - "idx": 1, - "trace": { - "depth": 1, - "success": true, - "caller": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", - "address": "0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f", - "maybe_precompile": false, - "selfdestruct_address": null, - "selfdestruct_refund_target": null, - "selfdestruct_transferred_value": null, - "kind": "CREATE", - "value": "0x0", - "data": "{...}", - "output": "{...}", - "gas_used": "{...}", - "gas_limit": "{...}", - "status": "Return", - "steps": [], - "decoded": { - "label": null, - "return_data": null, - "call_data": null - } - }, - "logs": [], - "ordering": [] - }, - { - "parent": 0, - "children": [], - "idx": 2, - "trace": { - "depth": 1, - "success": true, - "caller": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", - "address": "0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f", - "maybe_precompile": null, - "selfdestruct_address": null, - "selfdestruct_refund_target": null, - "selfdestruct_transferred_value": null, - "kind": "CALL", - "value": "0x0", - "data": "0xe26d14740000000000000000000000000000000000000000000000000000000000000064", - "output": "0x", - "gas_used": "{...}", - "gas_limit": "{...}", - "status": "Stop", - "steps": [], - "decoded": { - "label": null, - "return_data": null, - "call_data": null - } - }, - "logs": [], - "ordering": [] - }, - { - "parent": 0, - "children": [], - "idx": 3, - "trace": { - "depth": 1, - "success": true, - "caller": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", - "address": "0x000000000000000000636F6e736F6c652e6c6f67", - "maybe_precompile": null, - "selfdestruct_address": null, - "selfdestruct_refund_target": null, - "selfdestruct_transferred_value": null, - "kind": "STATICCALL", - "value": "0x0", - "data": "{...}", - "output": "0x", - "gas_used": "{...}", - "gas_limit": "{...}", - "status": "Stop", - "steps": [], - "decoded": { - "label": null, - "return_data": null, - "call_data": null - } - }, - "logs": [], - "ordering": [] - } - ] - } - ] - ], - "labeled_addresses": {}, - "duration": { - "secs": "{...}", - "nanos": "{...}" + { + "parent": 0, + "children": [], + "idx": 2, + "trace": { + "depth": 1, + "success": true, + "caller": "0x7fa9385be102ac3eac297483dd6233d62b3e1496", + "address": "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", + "maybe_precompile": null, + "selfdestruct_address": null, + "selfdestruct_refund_target": null, + "selfdestruct_transferred_value": null, + "kind": "CALL", + "value": "0x0", + "data": "0xe26d14740000000000000000000000000000000000000000000000000000000000000064", + "output": "0x", + "gas_used": "{...}", + "gas_limit": "{...}", + "status": "Stop", + "steps": [], + "decoded": { + "label": null, + "return_data": null, + "call_data": null + } + }, + "logs": [], + "ordering": [] }, - "breakpoints": {}, - "gas_snapshots": {} + { + "parent": 0, + "children": [], + "idx": 3, + "trace": { + "depth": 1, + "success": true, + "caller": "0x7fa9385be102ac3eac297483dd6233d62b3e1496", + "address": "0x000000000000000000636f6e736f6c652e6c6f67", + "maybe_precompile": null, + "selfdestruct_address": null, + "selfdestruct_refund_target": null, + "selfdestruct_transferred_value": null, + "kind": "STATICCALL", + "value": "0x0", + "data": "{...}", + "output": "0x", + "gas_used": "{...}", + "gas_limit": "{...}", + "status": "Stop", + "steps": [], + "decoded": { + "label": null, + "return_data": null, + "call_data": null + } + }, + "logs": [], + "ordering": [] + } + ] } + ] + ], + "labeled_addresses": {}, + "duration": { + "secs": "{...}", + "nanos": "{...}" }, - "warnings": [] - } + "breakpoints": {}, + "gas_snapshots": {} + } + }, + "warnings": [] + } } \ No newline at end of file From 9415dde6e6b4ce14bb773eab7a8ebe0ed8e0c52c Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Mon, 14 Oct 2024 12:29:58 +0530 Subject: [PATCH 094/100] feat(`cheatcodes`): vm.rememberKeys (#9087) * feat(`cheatcodes`): vm.rememberKeys * docs + return addresses + test * remeberKeys with language * doc nits * cargo cheats * set script wallet in config if unset * nit * test --- crates/cheatcodes/assets/cheatcodes.json | 40 +++++++++ crates/cheatcodes/spec/src/vm.rs | 14 ++++ crates/cheatcodes/src/crypto.rs | 102 +++++++++++++++++++++-- crates/forge/tests/cli/script.rs | 38 +++++++++ crates/forge/tests/cli/test_cmd.rs | 55 ++++++++++++ testdata/cheats/Vm.sol | 2 + 6 files changed, 244 insertions(+), 7 deletions(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index b41f73667..554cae92a 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -7533,6 +7533,46 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "rememberKeys_0", + "description": "Derive a set number of wallets from a mnemonic at the derivation path `m/44'/60'/0'/0/{0..count}`.\nThe respective private keys are saved to the local forge wallet for later use and their addresses are returned.", + "declaration": "function rememberKeys(string calldata mnemonic, string calldata derivationPath, uint32 count) external returns (address[] memory keyAddrs);", + "visibility": "external", + "mutability": "", + "signature": "rememberKeys(string,string,uint32)", + "selector": "0x97cb9189", + "selectorBytes": [ + 151, + 203, + 145, + 137 + ] + }, + "group": "crypto", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "rememberKeys_1", + "description": "Derive a set number of wallets from a mnemonic in the specified language at the derivation path `m/44'/60'/0'/0/{0..count}`.\nThe respective private keys are saved to the local forge wallet for later use and their addresses are returned.", + "declaration": "function rememberKeys(string calldata mnemonic, string calldata derivationPath, string calldata language, uint32 count) external returns (address[] memory keyAddrs);", + "visibility": "external", + "mutability": "", + "signature": "rememberKeys(string,string,string,uint32)", + "selector": "0xf8d58eaf", + "selectorBytes": [ + 248, + 213, + 142, + 175 + ] + }, + "group": "crypto", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "removeDir", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 2a342a044..0fd8a62a7 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -2407,6 +2407,20 @@ interface Vm { #[cheatcode(group = Crypto)] function rememberKey(uint256 privateKey) external returns (address keyAddr); + /// Derive a set number of wallets from a mnemonic at the derivation path `m/44'/60'/0'/0/{0..count}`. + /// + /// The respective private keys are saved to the local forge wallet for later use and their addresses are returned. + #[cheatcode(group = Crypto)] + function rememberKeys(string calldata mnemonic, string calldata derivationPath, uint32 count) external returns (address[] memory keyAddrs); + + /// Derive a set number of wallets from a mnemonic in the specified language at the derivation path `m/44'/60'/0'/0/{0..count}`. + /// + /// The respective private keys are saved to the local forge wallet for later use and their addresses are returned. + #[cheatcode(group = Crypto)] + function rememberKeys(string calldata mnemonic, string calldata derivationPath, string calldata language, uint32 count) + external + returns (address[] memory keyAddrs); + // -------- Uncategorized Utilities -------- /// Labels an address in call traces. diff --git a/crates/cheatcodes/src/crypto.rs b/crates/cheatcodes/src/crypto.rs index f080938ac..e5e6f56e3 100644 --- a/crates/cheatcodes/src/crypto.rs +++ b/crates/cheatcodes/src/crypto.rs @@ -1,6 +1,6 @@ //! Implementations of [`Crypto`](spec::Group::Crypto) Cheatcodes. -use crate::{Cheatcode, Cheatcodes, Result, Vm::*}; +use crate::{Cheatcode, Cheatcodes, Result, ScriptWallets, Vm::*}; use alloy_primitives::{keccak256, Address, B256, U256}; use alloy_signer::{Signer, SignerSync}; use alloy_signer_local::{ @@ -8,14 +8,16 @@ use alloy_signer_local::{ ChineseSimplified, ChineseTraditional, Czech, English, French, Italian, Japanese, Korean, Portuguese, Spanish, Wordlist, }, - MnemonicBuilder, PrivateKeySigner, + LocalSigner, MnemonicBuilder, PrivateKeySigner, }; use alloy_sol_types::SolValue; +use foundry_wallets::multi_wallet::MultiWallet; use k256::{ ecdsa::SigningKey, elliptic_curve::{bigint::ArrayEncoding, sec1::ToEncodedPoint}, }; use p256::ecdsa::{signature::hazmat::PrehashSigner, Signature, SigningKey as P256SigningKey}; +use std::sync::Arc; /// The BIP32 default derivation path prefix. const DEFAULT_DERIVATION_PATH_PREFIX: &str = "m/44'/60'/0'/0/"; @@ -89,14 +91,56 @@ 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); - } + let address = inject_wallet(state, wallet); Ok(address.abi_encode()) } } +impl Cheatcode for rememberKeys_0Call { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { mnemonic, derivationPath, count } = self; + tracing::info!("Remembering {} keys", count); + let wallets = derive_wallets::(mnemonic, derivationPath, *count)?; + + tracing::info!("Adding {} keys to script wallets", count); + + let mut addresses = Vec::

::with_capacity(wallets.len()); + for wallet in wallets { + let addr = inject_wallet(state, wallet); + addresses.push(addr); + } + + Ok(addresses.abi_encode()) + } +} + +impl Cheatcode for rememberKeys_1Call { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { mnemonic, derivationPath, language, count } = self; + let wallets = derive_wallets_str(mnemonic, derivationPath, language, *count)?; + let mut addresses = Vec::
::with_capacity(wallets.len()); + for wallet in wallets { + let addr = inject_wallet(state, wallet); + addresses.push(addr); + } + + Ok(addresses.abi_encode()) + } +} + +fn inject_wallet(state: &mut Cheatcodes, wallet: LocalSigner) -> Address { + let address = wallet.address(); + if let Some(script_wallets) = state.script_wallets() { + script_wallets.add_local_signer(wallet); + } else { + // This is needed in case of testing scripts, wherein script wallets are not set on setup. + let script_wallets = ScriptWallets::new(MultiWallet::default(), None); + script_wallets.add_local_signer(wallet); + Arc::make_mut(&mut state.config).script_wallets = Some(script_wallets); + } + address +} + impl Cheatcode for sign_1Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { privateKey, digest } = self; @@ -228,7 +272,7 @@ fn sign_with_wallet( } else if signers.len() == 1 { *signers.keys().next().unwrap() } else { - bail!("could not determine signer"); + bail!("could not determine signer, there are multiple signers available use vm.sign(signer, digest) to specify one"); }; let wallet = signers @@ -309,6 +353,50 @@ fn derive_key(mnemonic: &str, path: &str, index: u32) -> Result { Ok(private_key.abi_encode()) } +fn derive_wallets_str( + mnemonic: &str, + path: &str, + language: &str, + count: u32, +) -> Result>> { + match language { + "chinese_simplified" => derive_wallets::(mnemonic, path, count), + "chinese_traditional" => derive_wallets::(mnemonic, path, count), + "czech" => derive_wallets::(mnemonic, path, count), + "english" => derive_wallets::(mnemonic, path, count), + "french" => derive_wallets::(mnemonic, path, count), + "italian" => derive_wallets::(mnemonic, path, count), + "japanese" => derive_wallets::(mnemonic, path, count), + "korean" => derive_wallets::(mnemonic, path, count), + "portuguese" => derive_wallets::(mnemonic, path, count), + "spanish" => derive_wallets::(mnemonic, path, count), + _ => Err(fmt_err!("unsupported mnemonic language: {language:?}")), + } +} + +fn derive_wallets( + mnemonic: &str, + path: &str, + count: u32, +) -> Result>> { + let mut out = path.to_string(); + + if !out.ends_with('/') { + out.push('/'); + } + + let mut wallets = Vec::with_capacity(count as usize); + for idx in 0..count { + let wallet = MnemonicBuilder::::default() + .phrase(mnemonic) + .derivation_path(format!("{out}{idx}"))? + .build()?; + wallets.push(wallet); + } + + Ok(wallets) +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index e8f1466b9..81b746120 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -2108,3 +2108,41 @@ Script ran successfully. "#]]); }); + +forgetest_init!(can_remeber_keys, |prj, cmd| { + let script = prj + .add_source( + "Foo", + r#" +import "forge-std/Script.sol"; + +interface Vm { + function rememberKeys(string calldata mnemonic, string calldata derivationPath, uint32 count) external returns (address[] memory keyAddrs); +} + +contract WalletScript is Script { + function run() public { + string memory mnemonic = "test test test test test test test test test test test junk"; + string memory derivationPath = "m/44'/60'/0'/0/"; + address[] memory wallets = Vm(address(vm)).rememberKeys(mnemonic, derivationPath, 3); + for (uint256 i = 0; i < wallets.length; i++) { + console.log(wallets[i]); + } + } +}"#, + ) + .unwrap(); + 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] + +== Logs == + 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 + 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC + +"#]]); +}); diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index ea3ce1911..2cd258f66 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -2219,3 +2219,58 @@ warning: specifying argument for --decode-internal is deprecated and will be rem "#]]); }); + +// Test a script that calls vm.rememberKeys +forgetest_init!(script_testing, |prj, cmd| { + prj + .add_source( + "Foo", + r#" +import "forge-std/Script.sol"; + +interface Vm { +function rememberKeys(string calldata mnemonic, string calldata derivationPath, uint32 count) external returns (address[] memory keyAddrs); +} + +contract WalletScript is Script { +function run() public { + string memory mnemonic = "test test test test test test test test test test test junk"; + string memory derivationPath = "m/44'/60'/0'/0/"; + address[] memory wallets = Vm(address(vm)).rememberKeys(mnemonic, derivationPath, 3); + for (uint256 i = 0; i < wallets.length; i++) { + console.log(wallets[i]); + } +} +} + +contract FooTest { + WalletScript public script; + + + function setUp() public { + script = new WalletScript(); + } + + function testWalletScript() public { + script.run(); + } +} + +"#, + ) + .unwrap(); + + cmd.args(["test", "--mt", "testWalletScript", "-vvv"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for src/Foo.sol:FooTest +[PASS] testWalletScript() ([GAS]) +Logs: + 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 + 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC +... +"#]]); +}); diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index e47a79cc6..bb9b07a65 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -371,6 +371,8 @@ interface Vm { function record() external; function recordLogs() external; function rememberKey(uint256 privateKey) external returns (address keyAddr); + function rememberKeys(string calldata mnemonic, string calldata derivationPath, uint32 count) external returns (address[] memory keyAddrs); + function rememberKeys(string calldata mnemonic, string calldata derivationPath, string calldata language, uint32 count) external returns (address[] memory keyAddrs); 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); From 9a813d50a500a7f70d3607a7bfca84d6614c9f0e Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Mon, 14 Oct 2024 17:37:17 +0530 Subject: [PATCH 095/100] refactor(`cheatcodes`): mv `ScriptWallets` into `Cheatcode` (#9106) * refactor(`cheatcodes`): mv `ScriptWallets` into `Cheatcode` from `CheatsConfig` * nit * rename `ScriptWallets` to `Wallets` * rename cheatcode * doc nits --- crates/cheatcodes/assets/cheatcodes.json | 16 ++++++++-------- crates/cheatcodes/spec/src/vm.rs | 2 +- crates/cheatcodes/src/config.rs | 8 +------- crates/cheatcodes/src/crypto.rs | 7 +++---- crates/cheatcodes/src/inspector.rs | 14 +++++++++++--- crates/cheatcodes/src/lib.rs | 2 +- crates/cheatcodes/src/script.rs | 18 +++++++++--------- crates/chisel/src/executor.rs | 1 - crates/evm/evm/src/inspectors/stack.rs | 20 ++++++++++++++++++-- crates/forge/src/multi_runner.rs | 1 - crates/forge/tests/cli/script.rs | 4 ++-- crates/script/src/broadcast.rs | 4 ++-- crates/script/src/build.rs | 6 +++--- crates/script/src/execute.rs | 8 ++++---- crates/script/src/lib.rs | 10 +++++----- crates/script/src/simulate.rs | 6 +++--- testdata/cheats/Vm.sol | 2 +- 17 files changed, 72 insertions(+), 57 deletions(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 554cae92a..4dbdd29b5 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -5453,18 +5453,18 @@ }, { "func": { - "id": "getScriptWallets", + "id": "getWallets", "description": "Returns addresses of available unlocked wallets in the script environment.", - "declaration": "function getScriptWallets() external returns (address[] memory wallets);", + "declaration": "function getWallets() external returns (address[] memory wallets);", "visibility": "external", "mutability": "", - "signature": "getScriptWallets()", - "selector": "0x7c49aa1f", + "signature": "getWallets()", + "selector": "0xdb7a4605", "selectorBytes": [ - 124, - 73, - 170, - 31 + 219, + 122, + 70, + 5 ] }, "group": "scripting", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 0fd8a62a7..21d41b373 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1912,7 +1912,7 @@ interface Vm { /// Returns addresses of available unlocked wallets in the script environment. #[cheatcode(group = Scripting)] - function getScriptWallets() external returns (address[] memory wallets); + function getWallets() external returns (address[] memory wallets); // ======== Utilities ======== diff --git a/crates/cheatcodes/src/config.rs b/crates/cheatcodes/src/config.rs index c6a15f45d..cfd6e9452 100644 --- a/crates/cheatcodes/src/config.rs +++ b/crates/cheatcodes/src/config.rs @@ -1,5 +1,5 @@ use super::Result; -use crate::{script::ScriptWallets, Vm::Rpc}; +use crate::Vm::Rpc; use alloy_primitives::{map::AddressHashMap, U256}; use foundry_common::{fs::normalize_path, ContractsByArtifact}; use foundry_compilers::{utils::canonicalize, ProjectPathsConfig}; @@ -43,8 +43,6 @@ pub struct CheatsConfig { pub evm_opts: EvmOpts, /// Address labels from config pub labels: AddressHashMap, - /// Script wallets - pub script_wallets: Option, /// Artifacts which are guaranteed to be fresh (either recompiled or cached). /// If Some, `vm.getDeployedCode` invocations are validated to be in scope of this list. /// If None, no validation is performed. @@ -65,7 +63,6 @@ impl CheatsConfig { config: &Config, evm_opts: EvmOpts, available_artifacts: Option, - script_wallets: Option, running_contract: Option, running_version: Option, ) -> Self { @@ -93,7 +90,6 @@ impl CheatsConfig { allowed_paths, evm_opts, labels: config.labels.clone(), - script_wallets, available_artifacts, running_contract, running_version, @@ -223,7 +219,6 @@ impl Default for CheatsConfig { allowed_paths: vec![], evm_opts: Default::default(), labels: Default::default(), - script_wallets: None, available_artifacts: Default::default(), running_contract: Default::default(), running_version: Default::default(), @@ -245,7 +240,6 @@ mod tests { None, None, None, - None, ) } diff --git a/crates/cheatcodes/src/crypto.rs b/crates/cheatcodes/src/crypto.rs index e5e6f56e3..f8f24e70b 100644 --- a/crates/cheatcodes/src/crypto.rs +++ b/crates/cheatcodes/src/crypto.rs @@ -1,6 +1,6 @@ //! Implementations of [`Crypto`](spec::Group::Crypto) Cheatcodes. -use crate::{Cheatcode, Cheatcodes, Result, ScriptWallets, Vm::*}; +use crate::{Cheatcode, Cheatcodes, Result, Vm::*, Wallets}; use alloy_primitives::{keccak256, Address, B256, U256}; use alloy_signer::{Signer, SignerSync}; use alloy_signer_local::{ @@ -17,7 +17,6 @@ use k256::{ elliptic_curve::{bigint::ArrayEncoding, sec1::ToEncodedPoint}, }; use p256::ecdsa::{signature::hazmat::PrehashSigner, Signature, SigningKey as P256SigningKey}; -use std::sync::Arc; /// The BIP32 default derivation path prefix. const DEFAULT_DERIVATION_PATH_PREFIX: &str = "m/44'/60'/0'/0/"; @@ -134,9 +133,9 @@ fn inject_wallet(state: &mut Cheatcodes, wallet: LocalSigner) -> Add script_wallets.add_local_signer(wallet); } else { // This is needed in case of testing scripts, wherein script wallets are not set on setup. - let script_wallets = ScriptWallets::new(MultiWallet::default(), None); + let script_wallets = Wallets::new(MultiWallet::default(), None); script_wallets.add_local_signer(wallet); - Arc::make_mut(&mut state.config).script_wallets = Some(script_wallets); + state.set_wallets(script_wallets); } address } diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index acb86681f..a5a2a474f 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -8,7 +8,7 @@ use crate::{ DealRecord, GasRecord, RecordAccess, }, inspector::utils::CommonCreateInput, - script::{Broadcast, ScriptWallets}, + script::{Broadcast, Wallets}, test::{ assume::AssumeNoRevert, expect::{ @@ -476,6 +476,8 @@ pub struct Cheatcodes { /// Deprecated cheatcodes mapped to the reason. Used to report warnings on test results. pub deprecated: HashMap<&'static str, Option<&'static str>>, + /// Unlocked wallets used in scripts and testing of scripts. + pub wallets: Option, } // This is not derived because calling this in `fn new` with `..Default::default()` creates a second @@ -523,12 +525,18 @@ impl Cheatcodes { ignored_traces: Default::default(), arbitrary_storage: Default::default(), deprecated: Default::default(), + wallets: Default::default(), } } /// Returns the configured script wallets. - pub fn script_wallets(&self) -> Option<&ScriptWallets> { - self.config.script_wallets.as_ref() + pub fn script_wallets(&self) -> Option<&Wallets> { + self.wallets.as_ref() + } + + /// Sets the unlocked wallets. + pub fn set_wallets(&mut self, wallets: Wallets) { + self.wallets = Some(wallets); } /// Decodes the input data and applies the cheatcode. diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index 106b4c961..ff93b8336 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -45,7 +45,7 @@ mod inspector; mod json; mod script; -pub use script::{ScriptWallets, ScriptWalletsInner}; +pub use script::{Wallets, WalletsInner}; mod string; diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index f9535844c..960d6a550 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -61,7 +61,7 @@ impl Cheatcode for stopBroadcastCall { } } -impl Cheatcode for getScriptWalletsCall { +impl Cheatcode for getWalletsCall { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let script_wallets = ccx.state.script_wallets().cloned().map(|sw| sw.signers().unwrap_or_default()); @@ -91,29 +91,29 @@ pub struct Broadcast { /// Contains context for wallet management. #[derive(Debug)] -pub struct ScriptWalletsInner { +pub struct WalletsInner { /// All signers in scope of the script. pub multi_wallet: MultiWallet, /// Optional signer provided as `--sender` flag. pub provided_sender: Option
, } -/// Clonable wrapper around [`ScriptWalletsInner`]. +/// Clonable wrapper around [`WalletsInner`]. #[derive(Debug, Clone)] -pub struct ScriptWallets { +pub struct Wallets { /// Inner data. - pub inner: Arc>, + pub inner: Arc>, } -impl ScriptWallets { +impl Wallets { #[allow(missing_docs)] pub fn new(multi_wallet: MultiWallet, provided_sender: Option
) -> Self { - Self { inner: Arc::new(Mutex::new(ScriptWalletsInner { multi_wallet, provided_sender })) } + Self { inner: Arc::new(Mutex::new(WalletsInner { multi_wallet, provided_sender })) } } - /// Consumes [ScriptWallets] and returns [MultiWallet]. + /// Consumes [Wallets] and returns [MultiWallet]. /// - /// Panics if [ScriptWallets] is still in use. + /// Panics if [Wallets] is still in use. pub fn into_multi_wallet(self) -> MultiWallet { Arc::into_inner(self.inner) .map(|m| m.into_inner().multi_wallet) diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index b63ebe3e4..1a2267719 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -335,7 +335,6 @@ impl SessionSource { self.config.evm_opts.clone(), None, None, - None, Some(self.solc.version.clone()), ) .into(), diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 1e3b36181..403e906f6 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -3,7 +3,7 @@ use super::{ TracingInspector, }; use alloy_primitives::{map::AddressHashMap, Address, Bytes, Log, TxKind, U256}; -use foundry_cheatcodes::CheatcodesExecutor; +use foundry_cheatcodes::{CheatcodesExecutor, Wallets}; use foundry_evm_core::{backend::DatabaseExt, InspectorExt}; use foundry_evm_coverage::HitMaps; use foundry_evm_traces::{SparsedTraceArena, TraceMode}; @@ -57,6 +57,8 @@ pub struct InspectorStackBuilder { pub enable_isolation: bool, /// Whether to enable Alphanet features. pub alphanet: bool, + /// The wallets to set in the cheatcodes context. + pub wallets: Option, } impl InspectorStackBuilder { @@ -87,6 +89,13 @@ impl InspectorStackBuilder { self } + /// Set the wallets. + #[inline] + pub fn wallets(mut self, wallets: Wallets) -> Self { + self.wallets = Some(wallets); + self + } + /// Set the fuzzer inspector. #[inline] pub fn fuzzer(mut self, fuzzer: Fuzzer) -> Self { @@ -161,13 +170,20 @@ impl InspectorStackBuilder { chisel_state, enable_isolation, alphanet, + wallets, } = self; let mut stack = InspectorStack::new(); // inspectors if let Some(config) = cheatcodes { - stack.set_cheatcodes(Cheatcodes::new(config)); + let mut cheatcodes = Cheatcodes::new(config); + // Set wallets if they are provided + if let Some(wallets) = wallets { + cheatcodes.set_wallets(wallets); + } + stack.set_cheatcodes(cheatcodes); } + if let Some(fuzzer) = fuzzer { stack.set_fuzzer(fuzzer); } diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 802ad3884..e7361db00 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -242,7 +242,6 @@ impl MultiContractRunner { &self.config, self.evm_opts.clone(), Some(self.known_contracts.clone()), - None, Some(artifact_id.name.clone()), Some(artifact_id.version.clone()), ); diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 81b746120..558580728 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -2077,12 +2077,12 @@ forgetest_init!(can_get_script_wallets, |prj, cmd| { import "forge-std/Script.sol"; interface Vm { - function getScriptWallets() external returns (address[] memory wallets); + function getWallets() external returns (address[] memory wallets); } contract WalletScript is Script { function run() public { - address[] memory wallets = Vm(address(vm)).getScriptWallets(); + address[] memory wallets = Vm(address(vm)).getWallets(); console.log(wallets[0]); } }"#, diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index 3faaa441e..b0551de57 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -17,7 +17,7 @@ use alloy_serde::WithOtherFields; use alloy_transport::Transport; use eyre::{bail, Context, Result}; use forge_verify::provider::VerificationProviderType; -use foundry_cheatcodes::ScriptWallets; +use foundry_cheatcodes::Wallets; use foundry_cli::utils::{has_batch_support, has_different_gas_calc}; use foundry_common::{ provider::{get_http_provider, try_get_http_provider, RetryProvider}, @@ -154,7 +154,7 @@ impl SendTransactionsKind { pub struct BundledState { pub args: ScriptArgs, pub script_config: ScriptConfig, - pub script_wallets: ScriptWallets, + pub script_wallets: Wallets, pub build_data: LinkedBuildData, pub sequence: ScriptSequenceKind, } diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index b0c5a2947..3f5ffae81 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -8,7 +8,7 @@ use crate::{ use alloy_primitives::{Bytes, B256}; use alloy_provider::Provider; use eyre::{OptionExt, Result}; -use foundry_cheatcodes::ScriptWallets; +use foundry_cheatcodes::Wallets; use foundry_common::{ compile::ProjectCompiler, provider::try_get_http_provider, ContractData, ContractsByArtifact, }; @@ -156,7 +156,7 @@ impl LinkedBuildData { pub struct PreprocessedState { pub args: ScriptArgs, pub script_config: ScriptConfig, - pub script_wallets: ScriptWallets, + pub script_wallets: Wallets, } impl PreprocessedState { @@ -242,7 +242,7 @@ impl PreprocessedState { pub struct CompiledState { pub args: ScriptArgs, pub script_config: ScriptConfig, - pub script_wallets: ScriptWallets, + pub script_wallets: Wallets, pub build_data: BuildData, } diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index a1b3cf613..4c2e89368 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -14,7 +14,7 @@ use alloy_provider::Provider; use alloy_rpc_types::TransactionInput; use async_recursion::async_recursion; use eyre::{OptionExt, Result}; -use foundry_cheatcodes::ScriptWallets; +use foundry_cheatcodes::Wallets; use foundry_cli::utils::{ensure_clean_constructor, needs_setup}; use foundry_common::{ fmt::{format_token, format_token_raw}, @@ -41,7 +41,7 @@ use yansi::Paint; pub struct LinkedState { pub args: ScriptArgs, pub script_config: ScriptConfig, - pub script_wallets: ScriptWallets, + pub script_wallets: Wallets, pub build_data: LinkedBuildData, } @@ -92,7 +92,7 @@ impl LinkedState { pub struct PreExecutionState { pub args: ScriptArgs, pub script_config: ScriptConfig, - pub script_wallets: ScriptWallets, + pub script_wallets: Wallets, pub build_data: LinkedBuildData, pub execution_data: ExecutionData, } @@ -274,7 +274,7 @@ pub struct ExecutionArtifacts { pub struct ExecutedState { pub args: ScriptArgs, pub script_config: ScriptConfig, - pub script_wallets: ScriptWallets, + pub script_wallets: Wallets, pub build_data: LinkedBuildData, pub execution_data: ExecutionData, pub execution_result: ScriptResult, diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 94c028bb9..de06fa0c1 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -43,7 +43,7 @@ use foundry_evm::{ constants::DEFAULT_CREATE2_DEPLOYER, executors::ExecutorBuilder, inspectors::{ - cheatcodes::{BroadcastableTransactions, ScriptWallets}, + cheatcodes::{BroadcastableTransactions, Wallets}, CheatsConfig, }, opts::EvmOpts, @@ -207,7 +207,7 @@ pub struct ScriptArgs { impl ScriptArgs { pub async fn preprocess(self) -> Result { let script_wallets = - ScriptWallets::new(self.wallets.get_multi_wallet().await?, self.evm_opts.sender); + Wallets::new(self.wallets.get_multi_wallet().await?, self.evm_opts.sender); let (config, mut evm_opts) = self.load_config_and_evm_opts_emit_warnings()?; @@ -560,7 +560,7 @@ impl ScriptConfig { async fn get_runner_with_cheatcodes( &mut self, known_contracts: ContractsByArtifact, - script_wallets: ScriptWallets, + script_wallets: Wallets, debug: bool, target: ArtifactId, ) -> Result { @@ -569,7 +569,7 @@ impl ScriptConfig { async fn _get_runner( &mut self, - cheats_data: Option<(ContractsByArtifact, ScriptWallets, ArtifactId)>, + cheats_data: Option<(ContractsByArtifact, Wallets, ArtifactId)>, debug: bool, ) -> Result { trace!("preparing script runner"); @@ -611,12 +611,12 @@ impl ScriptConfig { &self.config, self.evm_opts.clone(), Some(known_contracts), - Some(script_wallets), Some(target.name), Some(target.version), ) .into(), ) + .wallets(script_wallets) .enable_isolation(self.evm_opts.isolate) }); } diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 432a8ccd5..2fbfbcffa 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -16,7 +16,7 @@ use alloy_network::TransactionBuilder; use alloy_primitives::{map::HashMap, utils::format_units, Address, Bytes, TxKind, U256}; use dialoguer::Confirm; use eyre::{Context, Result}; -use foundry_cheatcodes::ScriptWallets; +use foundry_cheatcodes::Wallets; 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}; @@ -36,7 +36,7 @@ use yansi::Paint; pub struct PreSimulationState { pub args: ScriptArgs, pub script_config: ScriptConfig, - pub script_wallets: ScriptWallets, + pub script_wallets: Wallets, pub build_data: LinkedBuildData, pub execution_data: ExecutionData, pub execution_result: ScriptResult, @@ -234,7 +234,7 @@ impl PreSimulationState { pub struct FilledTransactionsState { pub args: ScriptArgs, pub script_config: ScriptConfig, - pub script_wallets: ScriptWallets, + pub script_wallets: Wallets, pub build_data: LinkedBuildData, pub execution_artifacts: ExecutionArtifacts, pub transactions: VecDeque, diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index bb9b07a65..2d4030d30 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -267,7 +267,7 @@ interface Vm { function getNonce(address account) external view returns (uint64 nonce); function getNonce(Wallet calldata wallet) external returns (uint64 nonce); function getRecordedLogs() external returns (Log[] memory logs); - function getScriptWallets() external returns (address[] memory wallets); + function getWallets() external returns (address[] memory wallets); function indexOf(string calldata input, string calldata key) external pure returns (uint256); function isContext(ForgeContext context) external view returns (bool result); function isDir(string calldata path) external returns (bool result); From 6f7c1f72f8c3361f1e738296a0ec634c099c8a7c Mon Sep 17 00:00:00 2001 From: pogobounce Date: Mon, 14 Oct 2024 15:32:53 +0300 Subject: [PATCH 096/100] fix: running script with `--broadcast` for a transaction sequence can error out due to nonce desync from rpc latency (#9096) * fix for issue #9095 * changed 'if' statement into 'match' * fmt fix * repeat ask for provider nonce on desync * loop break and tokio::time use instead of std::thread --- Cargo.lock | 1 + crates/script/Cargo.toml | 1 + crates/script/src/broadcast.rs | 24 +++++++++++++++++++----- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e597a7a91..257f63e31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3497,6 +3497,7 @@ dependencies = [ "serde", "serde_json", "tempfile", + "tokio", "tracing", "yansi", ] diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml index 9c0426617..37cc13741 100644 --- a/crates/script/Cargo.toml +++ b/crates/script/Cargo.toml @@ -33,6 +33,7 @@ tracing.workspace = true clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } semver.workspace = true futures.workspace = true +tokio.workspace = true async-recursion = "1.0.5" itertools.workspace = true diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index b0551de57..cb99b26a1 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -26,7 +26,7 @@ use foundry_common::{ use foundry_config::Config; use futures::{future::join_all, StreamExt}; use itertools::Itertools; -use std::sync::Arc; +use std::{cmp::Ordering, sync::Arc}; pub async fn estimate_gas( tx: &mut WithOtherFields, @@ -67,11 +67,25 @@ pub async fn send_transaction( if sequential_broadcast { let from = tx.from.expect("no sender"); - 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.") + for attempt in 0..5 { + let nonce = provider.get_transaction_count(from).await?; + match nonce.cmp(&tx_nonce) { + Ordering::Greater => { + bail!("EOA nonce changed unexpectedly while sending transactions. Expected {tx_nonce} got {nonce} from provider.") + } + Ordering::Less => { + if attempt == 4 { + bail!("After 5 attempts, provider nonce ({nonce}) is still behind expected nonce ({tx_nonce}).") + } + warn!("Expected nonce ({tx_nonce}) is ahead of provider nonce ({nonce}). Retrying in 1 second..."); + tokio::time::sleep(std::time::Duration::from_millis(1000)).await; + } + Ordering::Equal => { + // Nonces are equal, we can proceed + break; + } + } } } From 440837d3e71c4cd4c551352bbc8486110a1db44d Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 14 Oct 2024 21:59:48 +0300 Subject: [PATCH 097/100] fix(fmt): do not panic when no named arg (#9114) --- crates/fmt/src/formatter.rs | 7 +++++-- crates/fmt/testdata/Repros/fmt.sol | 15 +++++++++++++++ crates/fmt/testdata/Repros/original.sol | 15 +++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/crates/fmt/src/formatter.rs b/crates/fmt/src/formatter.rs index 02b7bd7fe..02d62f705 100644 --- a/crates/fmt/src/formatter.rs +++ b/crates/fmt/src/formatter.rs @@ -2340,8 +2340,11 @@ impl Visitor for Formatter<'_, W> { "" }; let closing_bracket = format!("{prefix}{}", "}"); - let closing_bracket_loc = args.last().unwrap().loc.end(); - write_chunk!(self, closing_bracket_loc, "{closing_bracket}")?; + if let Some(arg) = args.last() { + write_chunk!(self, arg.loc.end(), "{closing_bracket}")?; + } else { + write_chunk!(self, "{closing_bracket}")?; + } Ok(()) } diff --git a/crates/fmt/testdata/Repros/fmt.sol b/crates/fmt/testdata/Repros/fmt.sol index a61a626a0..0a480c0b0 100644 --- a/crates/fmt/testdata/Repros/fmt.sol +++ b/crates/fmt/testdata/Repros/fmt.sol @@ -144,3 +144,18 @@ contract IfElseTest { } } } + +contract DbgFmtTest is Test { + function test_argsList() public { + uint256 result1 = internalNoArgs({}); + result2 = add({a: 1, b: 2}); + } + + function add(uint256 a, uint256 b) internal pure returns (uint256) { + return a + b; + } + + function internalNoArgs() internal pure returns (uint256) { + return 0; + } +} diff --git a/crates/fmt/testdata/Repros/original.sol b/crates/fmt/testdata/Repros/original.sol index ef75931e2..6f18784d3 100644 --- a/crates/fmt/testdata/Repros/original.sol +++ b/crates/fmt/testdata/Repros/original.sol @@ -143,3 +143,18 @@ contract IfElseTest { } } } + +contract DbgFmtTest is Test { + function test_argsList() public { + uint256 result1 = internalNoArgs({}); + result2 = add({a: 1, b: 2}); + } + + function add(uint256 a, uint256 b) internal pure returns (uint256) { + return a + b; + } + + function internalNoArgs() internal pure returns (uint256) { + return 0; + } +} From fdd321bac95f0935529164a88faf99d4d5cfa321 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 14 Oct 2024 22:20:28 +0300 Subject: [PATCH 098/100] fix(traces): identify artifacts using both deployed and creation code (#9050) * Identify by creation code * Compute score for both creation and runtime code * Fallback to deployed bytecode only if min creation bytecode score is under threshold * reuse check closure, add basic test --- crates/evm/traces/src/decoder/mod.rs | 5 +- crates/evm/traces/src/identifier/etherscan.rs | 4 +- crates/evm/traces/src/identifier/local.rs | 46 ++++++--- crates/evm/traces/src/identifier/mod.rs | 4 +- crates/forge/tests/cli/test_cmd.rs | 99 +++++++++++++++++++ 5 files changed, 139 insertions(+), 19 deletions(-) diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index de3a0c02f..43a53286c 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -262,7 +262,7 @@ impl CallTraceDecoder { pub fn trace_addresses<'a>( &'a self, arena: &'a CallTraceArena, - ) -> impl Iterator)> + Clone + 'a { + ) -> impl Iterator, Option<&'a [u8]>)> + Clone + 'a { arena .nodes() .iter() @@ -270,9 +270,10 @@ impl CallTraceDecoder { ( &node.trace.address, node.trace.kind.is_any_create().then_some(&node.trace.output[..]), + node.trace.kind.is_any_create().then_some(&node.trace.data[..]), ) }) - .filter(|&(address, _)| { + .filter(|&(address, _, _)| { !self.labels.contains_key(address) || !self.contracts.contains_key(address) }) } diff --git a/crates/evm/traces/src/identifier/etherscan.rs b/crates/evm/traces/src/identifier/etherscan.rs index 4a34b31b3..2af6fbb59 100644 --- a/crates/evm/traces/src/identifier/etherscan.rs +++ b/crates/evm/traces/src/identifier/etherscan.rs @@ -97,7 +97,7 @@ impl EtherscanIdentifier { impl TraceIdentifier for EtherscanIdentifier { fn identify_addresses<'a, A>(&mut self, addresses: A) -> Vec> where - A: Iterator)>, + A: Iterator, Option<&'a [u8]>)>, { trace!(target: "evm::traces", "identify {:?} addresses", addresses.size_hint().1); @@ -114,7 +114,7 @@ impl TraceIdentifier for EtherscanIdentifier { Arc::clone(&self.invalid_api_key), ); - for (addr, _) in addresses { + for (addr, _, _) in addresses { if let Some(metadata) = self.contracts.get(addr) { let label = metadata.contract_name.clone(); let abi = metadata.abi().ok().map(Cow::Owned); diff --git a/crates/evm/traces/src/identifier/local.rs b/crates/evm/traces/src/identifier/local.rs index 04680b01a..90c28e016 100644 --- a/crates/evm/traces/src/identifier/local.rs +++ b/crates/evm/traces/src/identifier/local.rs @@ -32,23 +32,34 @@ impl<'a> LocalTraceIdentifier<'a> { self.known_contracts } - /// Tries to the bytecode most similar to the given one. - pub fn identify_code(&self, code: &[u8]) -> Option<(&'a ArtifactId, &'a JsonAbi)> { - let len = code.len(); + /// Identifies the artifact based on score computed for both creation and deployed bytecodes. + pub fn identify_code( + &self, + runtime_code: &[u8], + creation_code: &[u8], + ) -> Option<(&'a ArtifactId, &'a JsonAbi)> { + let len = runtime_code.len(); let mut min_score = f64::MAX; let mut min_score_id = None; - let mut check = |id| { + let mut check = |id, is_creation, min_score: &mut f64| { let contract = self.known_contracts.get(id)?; - if let Some(deployed_bytecode) = contract.deployed_bytecode() { - let score = bytecode_diff_score(deployed_bytecode, code); + // Select bytecodes to compare based on `is_creation` flag. + let (contract_bytecode, current_bytecode) = if is_creation { + (contract.bytecode(), creation_code) + } else { + (contract.deployed_bytecode(), runtime_code) + }; + + if let Some(bytecode) = contract_bytecode { + let score = bytecode_diff_score(bytecode, current_bytecode); if score == 0.0 { trace!(target: "evm::traces", "found exact match"); return Some((id, &contract.abi)); } - if score < min_score { - min_score = score; + if score < *min_score { + *min_score = score; min_score_id = Some((id, &contract.abi)); } } @@ -65,7 +76,7 @@ impl<'a> LocalTraceIdentifier<'a> { if len > max_len { break; } - if let found @ Some(_) = check(id) { + if let found @ Some(_) = check(id, true, &mut min_score) { return found; } } @@ -75,11 +86,20 @@ impl<'a> LocalTraceIdentifier<'a> { let idx = self.find_index(min_len); for i in idx..same_length_idx { let (id, _) = self.ordered_ids[i]; - if let found @ Some(_) = check(id) { + if let found @ Some(_) = check(id, true, &mut min_score) { return found; } } + // Fallback to comparing deployed code if min score greater than threshold. + if min_score >= 0.85 { + for (artifact, _) in &self.ordered_ids { + if let found @ Some(_) = check(artifact, false, &mut min_score) { + return found; + } + } + } + trace!(target: "evm::traces", %min_score, "no exact match found"); // Note: the diff score can be inaccurate for small contracts so we're using a relatively @@ -109,16 +129,16 @@ impl<'a> LocalTraceIdentifier<'a> { impl TraceIdentifier for LocalTraceIdentifier<'_> { fn identify_addresses<'a, A>(&mut self, addresses: A) -> Vec> where - A: Iterator)>, + A: Iterator, Option<&'a [u8]>)>, { trace!(target: "evm::traces", "identify {:?} addresses", addresses.size_hint().1); addresses - .filter_map(|(address, code)| { + .filter_map(|(address, runtime_code, creation_code)| { let _span = trace_span!(target: "evm::traces", "identify", %address).entered(); trace!(target: "evm::traces", "identifying"); - let (id, abi) = self.identify_code(code?)?; + let (id, abi) = self.identify_code(runtime_code?, creation_code?)?; trace!(target: "evm::traces", id=%id.identifier(), "identified"); Some(AddressIdentity { diff --git a/crates/evm/traces/src/identifier/mod.rs b/crates/evm/traces/src/identifier/mod.rs index a16b108d8..008e5f841 100644 --- a/crates/evm/traces/src/identifier/mod.rs +++ b/crates/evm/traces/src/identifier/mod.rs @@ -35,7 +35,7 @@ pub trait TraceIdentifier { /// Attempts to identify an address in one or more call traces. fn identify_addresses<'a, A>(&mut self, addresses: A) -> Vec> where - A: Iterator)> + Clone; + A: Iterator, Option<&'a [u8]>)> + Clone; } /// A collection of trace identifiers. @@ -55,7 +55,7 @@ impl Default for TraceIdentifiers<'_> { impl TraceIdentifier for TraceIdentifiers<'_> { fn identify_addresses<'a, A>(&mut self, addresses: A) -> Vec> where - A: Iterator)> + Clone, + A: Iterator, Option<&'a [u8]>)> + Clone, { let mut identities = Vec::new(); if let Some(local) = &mut self.local { diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 2cd258f66..4f812e79c 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -2274,3 +2274,102 @@ Logs: ... "#]]); }); + +// +forgetest_init!(metadata_bytecode_traces, |prj, cmd| { + prj.add_source( + "ParentProxy.sol", + r#" +import {Counter} from "./Counter.sol"; + +abstract contract ParentProxy { + Counter impl; + bytes data; + + constructor(Counter _implementation, bytes memory _data) { + impl = _implementation; + data = _data; + } +} + "#, + ) + .unwrap(); + prj.add_source( + "Proxy.sol", + r#" +import {ParentProxy} from "./ParentProxy.sol"; +import {Counter} from "./Counter.sol"; + +contract Proxy is ParentProxy { + constructor(Counter _implementation, bytes memory _data) + ParentProxy(_implementation, _data) + {} +} + "#, + ) + .unwrap(); + + prj.add_test( + "MetadataTraceTest.t.sol", + r#" +import {Counter} from "src/Counter.sol"; +import {Proxy} from "src/Proxy.sol"; + +import {Test} from "forge-std/Test.sol"; + +contract MetadataTraceTest is Test { + function test_proxy_trace() public { + Counter counter = new Counter(); + new Proxy(counter, ""); + } +} + "#, + ) + .unwrap(); + + cmd.args(["test", "--mt", "test_proxy_trace", "-vvvv"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/MetadataTraceTest.t.sol:MetadataTraceTest +[PASS] test_proxy_trace() ([GAS]) +Traces: + [152142] MetadataTraceTest::test_proxy_trace() + ├─ [49499] → new Counter@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f + │ └─ ← [Return] 247 bytes of code + ├─ [37978] → new Proxy@0x2e234DAe75C793f67A35089C9d99245E1C58470b + │ └─ ← [Return] 63 bytes of code + └─ ← [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) + +"#]]); + + // Check consistent traces for running with no metadata. + cmd.forge_fuse() + .args(["test", "--mt", "test_proxy_trace", "-vvvv", "--no-metadata"]) + .assert_success() + .stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/MetadataTraceTest.t.sol:MetadataTraceTest +[PASS] test_proxy_trace() ([GAS]) +Traces: + [130521] MetadataTraceTest::test_proxy_trace() + ├─ [38693] → new Counter@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f + │ └─ ← [Return] 193 bytes of code + ├─ [27175] → new Proxy@0x2e234DAe75C793f67A35089C9d99245E1C58470b + │ └─ ← [Return] 9 bytes of code + └─ ← [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) + +"#]]); +}); From 11bd7715d5c16252f58c47b6e418930cf1501576 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Tue, 15 Oct 2024 14:08:09 +0530 Subject: [PATCH 099/100] chore(`cheatcodes`): wallet nits (#9118) --- crates/cheatcodes/src/crypto.rs | 28 +++++----------- crates/cheatcodes/src/inspector.rs | 7 ++-- crates/cheatcodes/src/script.rs | 51 ++++++++++++++++-------------- 3 files changed, 40 insertions(+), 46 deletions(-) diff --git a/crates/cheatcodes/src/crypto.rs b/crates/cheatcodes/src/crypto.rs index f8f24e70b..cdb07720c 100644 --- a/crates/cheatcodes/src/crypto.rs +++ b/crates/cheatcodes/src/crypto.rs @@ -1,6 +1,6 @@ //! Implementations of [`Crypto`](spec::Group::Crypto) Cheatcodes. -use crate::{Cheatcode, Cheatcodes, Result, Vm::*, Wallets}; +use crate::{Cheatcode, Cheatcodes, Result, Vm::*}; use alloy_primitives::{keccak256, Address, B256, U256}; use alloy_signer::{Signer, SignerSync}; use alloy_signer_local::{ @@ -11,7 +11,6 @@ use alloy_signer_local::{ LocalSigner, MnemonicBuilder, PrivateKeySigner, }; use alloy_sol_types::SolValue; -use foundry_wallets::multi_wallet::MultiWallet; use k256::{ ecdsa::SigningKey, elliptic_curve::{bigint::ArrayEncoding, sec1::ToEncodedPoint}, @@ -98,11 +97,7 @@ impl Cheatcode for rememberKeyCall { impl Cheatcode for rememberKeys_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { mnemonic, derivationPath, count } = self; - tracing::info!("Remembering {} keys", count); let wallets = derive_wallets::(mnemonic, derivationPath, *count)?; - - tracing::info!("Adding {} keys to script wallets", count); - let mut addresses = Vec::
::with_capacity(wallets.len()); for wallet in wallets { let addr = inject_wallet(state, wallet); @@ -129,14 +124,7 @@ impl Cheatcode for rememberKeys_1Call { fn inject_wallet(state: &mut Cheatcodes, wallet: LocalSigner) -> Address { let address = wallet.address(); - if let Some(script_wallets) = state.script_wallets() { - script_wallets.add_local_signer(wallet); - } else { - // This is needed in case of testing scripts, wherein script wallets are not set on setup. - let script_wallets = Wallets::new(MultiWallet::default(), None); - script_wallets.add_local_signer(wallet); - state.set_wallets(script_wallets); - } + state.wallets().add_local_signer(wallet); address } @@ -256,13 +244,13 @@ fn sign_with_wallet( signer: Option
, digest: &B256, ) -> Result { - let Some(script_wallets) = state.script_wallets() else { - bail!("no wallets are available"); - }; + if state.wallets().is_empty() { + bail!("no wallets 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 mut wallets = state.wallets().inner.lock(); + let maybe_provided_sender = wallets.provided_sender; + let signers = wallets.multi_wallet.signers()?; let signer = if let Some(signer) = signer { signer diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index a5a2a474f..043f8af2f 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -37,6 +37,7 @@ use foundry_evm_core::{ InspectorExt, }; use foundry_evm_traces::{TracingInspector, TracingInspectorConfig}; +use foundry_wallets::multi_wallet::MultiWallet; use itertools::Itertools; use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner}; use rand::Rng; @@ -529,9 +530,9 @@ impl Cheatcodes { } } - /// Returns the configured script wallets. - pub fn script_wallets(&self) -> Option<&Wallets> { - self.wallets.as_ref() + /// Returns the configured wallets if available, else creates a new instance. + pub fn wallets(&mut self) -> &Wallets { + self.wallets.get_or_insert(Wallets::new(MultiWallet::default(), None)) } /// Sets the unlocked wallets. diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index 960d6a550..29a804efd 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -63,15 +63,8 @@ impl Cheatcode for stopBroadcastCall { impl Cheatcode for getWalletsCall { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { - let script_wallets = - ccx.state.script_wallets().cloned().map(|sw| sw.signers().unwrap_or_default()); - - if let Some(script_wallets) = script_wallets { - let script_wallets: Vec
= script_wallets.into_iter().collect(); - Ok(script_wallets.abi_encode()) - } else { - Ok(Default::default()) - } + let wallets = ccx.state.wallets().signers().unwrap_or_default(); + Ok(wallets.abi_encode()) } } @@ -135,6 +128,21 @@ impl Wallets { pub fn signers(&self) -> Result> { Ok(self.inner.lock().multi_wallet.signers()?.keys().cloned().collect()) } + + /// Number of signers in the [MultiWallet]. + pub fn len(&self) -> usize { + let mut inner = self.inner.lock(); + let signers = inner.multi_wallet.signers(); + if signers.is_err() { + return 0; + } + signers.unwrap().len() + } + + /// Whether the [MultiWallet] is empty. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } } /// Sets up broadcasting from a script using `new_origin` as the sender. @@ -148,16 +156,14 @@ fn broadcast(ccx: &mut CheatsCtxt, new_origin: Option<&Address>, single_call: bo let mut new_origin = new_origin.cloned(); if new_origin.is_none() { - if let Some(script_wallets) = ccx.state.script_wallets() { - let mut script_wallets = script_wallets.inner.lock(); - if let Some(provided_sender) = script_wallets.provided_sender { - new_origin = Some(provided_sender); - } else { - let signers = script_wallets.multi_wallet.signers()?; - if signers.len() == 1 { - let address = signers.keys().next().unwrap(); - new_origin = Some(*address); - } + let mut wallets = ccx.state.wallets().inner.lock(); + if let Some(provided_sender) = wallets.provided_sender { + new_origin = Some(provided_sender); + } else { + let signers = wallets.multi_wallet.signers()?; + if signers.len() == 1 { + let address = signers.keys().next().unwrap(); + new_origin = Some(*address); } } } @@ -175,7 +181,7 @@ fn broadcast(ccx: &mut CheatsCtxt, new_origin: Option<&Address>, single_call: bo } /// Sets up broadcasting from a script with the sender derived from `private_key`. -/// Adds this private key to `state`'s `script_wallets` vector to later be used for signing +/// Adds this private key to `state`'s `wallets` vector to later be used for signing /// if broadcast is successful. fn broadcast_key(ccx: &mut CheatsCtxt, private_key: &U256, single_call: bool) -> Result { let wallet = super::crypto::parse_wallet(private_key)?; @@ -183,9 +189,8 @@ fn broadcast_key(ccx: &mut CheatsCtxt, private_key: &U256, single_call: bool) -> let result = broadcast(ccx, Some(&new_origin), single_call); if result.is_ok() { - if let Some(script_wallets) = ccx.state.script_wallets() { - script_wallets.add_local_signer(wallet); - } + let wallets = ccx.state.wallets(); + wallets.add_local_signer(wallet); } result } From f5aa05ee9ac4f088db64b93ae5df4d8656faf1ea Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:36:37 +0530 Subject: [PATCH 100/100] refactor(`script`): mv `ScriptSequence` to new crate (#9098) * refac(`script`): extract script sequence and related types to new crate * replace MultiChainSequence in script crate * replace TransactionWithMetadata and AdditionalContract * replace ScriptSequence * replace all underlying ScriptSequence and related types * doc nits * add `ScriptTransactionBuilder` * remove `TxWithMetadata` * mv verify fns and use `ScriptSequence` directly * clippy --- Cargo.lock | 17 ++ Cargo.toml | 2 + crates/script-sequence/Cargo.toml | 29 ++ crates/script-sequence/src/lib.rs | 7 + crates/script-sequence/src/sequence.rs | 248 +++++++++++++++ crates/script-sequence/src/transaction.rs | 75 +++++ crates/script/Cargo.toml | 1 + crates/script/src/broadcast.rs | 4 +- crates/script/src/build.rs | 8 +- crates/script/src/lib.rs | 10 +- crates/script/src/multi_sequence.rs | 5 +- crates/script/src/progress.rs | 6 +- crates/script/src/sequence.rs | 350 +--------------------- crates/script/src/simulate.rs | 43 +-- crates/script/src/transaction.rs | 251 ++++++---------- crates/script/src/verify.rs | 118 +++++++- 16 files changed, 629 insertions(+), 545 deletions(-) create mode 100644 crates/script-sequence/Cargo.toml create mode 100644 crates/script-sequence/src/lib.rs create mode 100644 crates/script-sequence/src/sequence.rs create mode 100644 crates/script-sequence/src/transaction.rs diff --git a/Cargo.lock b/Cargo.lock index 257f63e31..5736ded80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3478,6 +3478,7 @@ dependencies = [ "dialoguer", "dunce", "eyre", + "forge-script-sequence", "forge-verify", "foundry-cheatcodes", "foundry-cli", @@ -3502,6 +3503,22 @@ dependencies = [ "yansi", ] +[[package]] +name = "forge-script-sequence" +version = "0.2.0" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types", + "eyre", + "foundry-common", + "foundry-compilers", + "foundry-config", + "revm-inspectors", + "serde", + "serde_json", + "tracing", +] + [[package]] name = "forge-sol-macro-gen" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 322152f87..2bb8f8945 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ members = [ "crates/evm/traces/", "crates/fmt/", "crates/forge/", + "crates/script-sequence/", "crates/macros/", "crates/test-utils/", ] @@ -147,6 +148,7 @@ forge-fmt = { path = "crates/fmt" } forge-verify = { path = "crates/verify" } forge-script = { path = "crates/script" } forge-sol-macro-gen = { path = "crates/sol-macro-gen" } +forge-script-sequence = { path = "crates/script-sequence" } foundry-cheatcodes = { path = "crates/cheatcodes" } foundry-cheatcodes-spec = { path = "crates/cheatcodes/spec" } foundry-cli = { path = "crates/cli" } diff --git a/crates/script-sequence/Cargo.toml b/crates/script-sequence/Cargo.toml new file mode 100644 index 000000000..e128fe37b --- /dev/null +++ b/crates/script-sequence/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "forge-script-sequence" +description = "Script sequence types" + +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[lints] +workspace = true + +[dependencies] +foundry-config.workspace = true +foundry-common.workspace = true +foundry-compilers = { workspace = true, features = ["full"] } + +serde.workspace = true +eyre.workspace = true +serde_json.workspace = true +tracing.workspace = true + +revm-inspectors.workspace = true + +alloy-rpc-types.workspace = true +alloy-primitives.workspace = true diff --git a/crates/script-sequence/src/lib.rs b/crates/script-sequence/src/lib.rs new file mode 100644 index 000000000..3aa5fc65a --- /dev/null +++ b/crates/script-sequence/src/lib.rs @@ -0,0 +1,7 @@ +//! Script Sequence and related types. + +pub mod sequence; +pub mod transaction; + +pub use sequence::*; +pub use transaction::*; diff --git a/crates/script-sequence/src/sequence.rs b/crates/script-sequence/src/sequence.rs new file mode 100644 index 000000000..080c725be --- /dev/null +++ b/crates/script-sequence/src/sequence.rs @@ -0,0 +1,248 @@ +use crate::transaction::TransactionWithMetadata; +use alloy_primitives::{hex, map::HashMap, TxHash}; +use alloy_rpc_types::AnyTransactionReceipt; +use eyre::{ContextCompat, Result, WrapErr}; +use foundry_common::{fs, shell, TransactionMaybeSigned, SELECTOR_LEN}; +use foundry_compilers::ArtifactId; +use foundry_config::Config; +use serde::{Deserialize, Serialize}; +use std::{ + collections::VecDeque, + io::{BufWriter, Write}, + path::PathBuf, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; + +pub const DRY_RUN_DIR: &str = "dry-run"; + +#[derive(Clone, Serialize, Deserialize)] +pub struct NestedValue { + pub internal_type: String, + pub value: String, +} + +/// Helper that saves the transactions sequence and its state on which transactions have been +/// broadcasted +#[derive(Clone, Default, Serialize, Deserialize)] +pub struct ScriptSequence { + pub transactions: VecDeque, + pub receipts: Vec, + pub libraries: Vec, + pub pending: Vec, + #[serde(skip)] + /// Contains paths to the sequence files + /// None if sequence should not be saved to disk (e.g. part of a multi-chain sequence) + pub paths: Option<(PathBuf, PathBuf)>, + pub returns: HashMap, + pub timestamp: u64, + pub chain: u64, + pub commit: Option, +} + +/// Sensitive values from the transactions in a script sequence +#[derive(Clone, Default, Serialize, Deserialize)] +pub struct SensitiveTransactionMetadata { + pub rpc: String, +} + +/// Sensitive info from the script sequence which is saved into the cache folder +#[derive(Clone, Default, Serialize, Deserialize)] +pub struct SensitiveScriptSequence { + pub transactions: VecDeque, +} + +impl From for SensitiveScriptSequence { + fn from(sequence: ScriptSequence) -> Self { + Self { + transactions: sequence + .transactions + .iter() + .map(|tx| SensitiveTransactionMetadata { rpc: tx.rpc.clone() }) + .collect(), + } + } +} + +impl ScriptSequence { + /// Loads The sequence for the corresponding json file + pub fn load( + config: &Config, + sig: &str, + target: &ArtifactId, + chain_id: u64, + dry_run: bool, + ) -> Result { + let (path, sensitive_path) = Self::get_paths(config, sig, target, chain_id, dry_run)?; + + let mut script_sequence: Self = fs::read_json_file(&path) + .wrap_err(format!("Deployment not found for chain `{chain_id}`."))?; + + let sensitive_script_sequence: SensitiveScriptSequence = fs::read_json_file( + &sensitive_path, + ) + .wrap_err(format!("Deployment's sensitive details not found for chain `{chain_id}`."))?; + + script_sequence.fill_sensitive(&sensitive_script_sequence); + + script_sequence.paths = Some((path, sensitive_path)); + + Ok(script_sequence) + } + + /// Saves the transactions as file if it's a standalone deployment. + /// `save_ts` should be set to true for checkpoint updates, which might happen many times and + /// could result in us saving many identical files. + pub fn save(&mut self, silent: bool, save_ts: bool) -> Result<()> { + self.sort_receipts(); + + if self.transactions.is_empty() { + return Ok(()) + } + + let Some((path, sensitive_path)) = self.paths.clone() else { return Ok(()) }; + + self.timestamp = now().as_secs(); + let ts_name = format!("run-{}.json", self.timestamp); + + let sensitive_script_sequence: SensitiveScriptSequence = self.clone().into(); + + // broadcast folder writes + //../run-latest.json + let mut writer = BufWriter::new(fs::create_file(&path)?); + serde_json::to_writer_pretty(&mut writer, &self)?; + writer.flush()?; + if save_ts { + //../run-[timestamp].json + fs::copy(&path, path.with_file_name(&ts_name))?; + } + + // cache folder writes + //../run-latest.json + let mut writer = BufWriter::new(fs::create_file(&sensitive_path)?); + serde_json::to_writer_pretty(&mut writer, &sensitive_script_sequence)?; + writer.flush()?; + if save_ts { + //../run-[timestamp].json + fs::copy(&sensitive_path, sensitive_path.with_file_name(&ts_name))?; + } + + if !silent { + shell::println(format!("\nTransactions saved to: {}\n", path.display()))?; + shell::println(format!("Sensitive values saved to: {}\n", sensitive_path.display()))?; + } + + Ok(()) + } + + pub fn add_receipt(&mut self, receipt: AnyTransactionReceipt) { + self.receipts.push(receipt); + } + + /// Sorts all receipts with ascending transaction index + pub fn sort_receipts(&mut self) { + self.receipts.sort_by_key(|r| (r.block_number, r.transaction_index)); + } + + pub fn add_pending(&mut self, index: usize, tx_hash: TxHash) { + if !self.pending.contains(&tx_hash) { + self.transactions[index].hash = Some(tx_hash); + self.pending.push(tx_hash); + } + } + + pub fn remove_pending(&mut self, tx_hash: TxHash) { + self.pending.retain(|element| element != &tx_hash); + } + + /// Gets paths in the formats + /// `./broadcast/[contract_filename]/[chain_id]/[sig]-[timestamp].json` and + /// `./cache/[contract_filename]/[chain_id]/[sig]-[timestamp].json`. + pub fn get_paths( + config: &Config, + sig: &str, + target: &ArtifactId, + chain_id: u64, + dry_run: bool, + ) -> Result<(PathBuf, PathBuf)> { + let mut broadcast = config.broadcast.to_path_buf(); + let mut cache = config.cache_path.to_path_buf(); + let mut common = PathBuf::new(); + + let target_fname = target.source.file_name().wrap_err("No filename.")?; + common.push(target_fname); + common.push(chain_id.to_string()); + if dry_run { + common.push(DRY_RUN_DIR); + } + + broadcast.push(common.clone()); + cache.push(common); + + fs::create_dir_all(&broadcast)?; + fs::create_dir_all(&cache)?; + + // TODO: ideally we want the name of the function here if sig is calldata + let filename = sig_to_file_name(sig); + + broadcast.push(format!("{filename}-latest.json")); + cache.push(format!("{filename}-latest.json")); + + Ok((broadcast, cache)) + } + + /// Returns the first RPC URL of this sequence. + pub fn rpc_url(&self) -> &str { + self.transactions.front().expect("empty sequence").rpc.as_str() + } + + /// Returns the list of the transactions without the metadata. + pub fn transactions(&self) -> impl Iterator { + self.transactions.iter().map(|tx| tx.tx()) + } + + pub fn fill_sensitive(&mut self, sensitive: &SensitiveScriptSequence) { + self.transactions + .iter_mut() + .enumerate() + .for_each(|(i, tx)| tx.rpc.clone_from(&sensitive.transactions[i].rpc)); + } +} + +/// Converts the `sig` argument into the corresponding file path. +/// +/// This accepts either the signature of the function or the raw calldata. +pub fn sig_to_file_name(sig: &str) -> String { + if let Some((name, _)) = sig.split_once('(') { + // strip until call argument parenthesis + return name.to_string() + } + // assume calldata if `sig` is hex + if let Ok(calldata) = hex::decode(sig) { + // in which case we return the function signature + return hex::encode(&calldata[..SELECTOR_LEN]) + } + + // return sig as is + sig.to_string() +} + +pub fn now() -> Duration { + SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_convert_sig() { + assert_eq!(sig_to_file_name("run()").as_str(), "run"); + assert_eq!( + sig_to_file_name( + "522bb704000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfFFb92266" + ) + .as_str(), + "522bb704" + ); + } +} diff --git a/crates/script-sequence/src/transaction.rs b/crates/script-sequence/src/transaction.rs new file mode 100644 index 000000000..7f72a4d30 --- /dev/null +++ b/crates/script-sequence/src/transaction.rs @@ -0,0 +1,75 @@ +use alloy_primitives::{Address, Bytes, B256}; +use foundry_common::TransactionMaybeSigned; +use revm_inspectors::tracing::types::CallKind; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AdditionalContract { + #[serde(rename = "transactionType")] + pub opcode: CallKind, + pub address: Address, + pub init_code: Bytes, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TransactionWithMetadata { + pub hash: Option, + #[serde(rename = "transactionType")] + pub opcode: CallKind, + #[serde(default = "default_string")] + pub contract_name: Option, + #[serde(default = "default_address")] + pub contract_address: Option
, + #[serde(default = "default_string")] + pub function: Option, + #[serde(default = "default_vec_of_strings")] + pub arguments: Option>, + #[serde(skip)] + pub rpc: String, + pub transaction: TransactionMaybeSigned, + pub additional_contracts: Vec, + pub is_fixed_gas_limit: bool, +} + +fn default_string() -> Option { + Some(String::new()) +} + +fn default_address() -> Option
{ + Some(Address::ZERO) +} + +fn default_vec_of_strings() -> Option> { + Some(vec![]) +} + +impl TransactionWithMetadata { + 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 tx(&self) -> &TransactionMaybeSigned { + &self.transaction + } + + pub fn tx_mut(&mut self) -> &mut TransactionMaybeSigned { + &mut self.transaction + } + + pub fn is_create2(&self) -> bool { + self.opcode == CallKind::Create2 + } +} diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml index 37cc13741..0d4810da6 100644 --- a/crates/script/Cargo.toml +++ b/crates/script/Cargo.toml @@ -23,6 +23,7 @@ foundry-debugger.workspace = true foundry-cheatcodes.workspace = true foundry-wallets.workspace = true foundry-linking.workspace = true +forge-script-sequence.workspace = true serde.workspace = true eyre.workspace = true diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index cb99b26a1..82f28562d 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -163,8 +163,8 @@ impl SendTransactionsKind { } /// State after we have bundled all -/// [`TransactionWithMetadata`](crate::transaction::TransactionWithMetadata) objects into a single -/// [`ScriptSequenceKind`] object containing one or more script sequences. +/// [`TransactionWithMetadata`](forge_script_sequence::TransactionWithMetadata) objects into a +/// single [`ScriptSequenceKind`] object containing one or more script sequences. pub struct BundledState { pub args: ScriptArgs, pub script_config: ScriptConfig, diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index 3f5ffae81..4b357ae23 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -1,13 +1,11 @@ use crate::{ - broadcast::BundledState, - execute::LinkedState, - multi_sequence::MultiChainSequence, - sequence::{ScriptSequence, ScriptSequenceKind}, - ScriptArgs, ScriptConfig, + broadcast::BundledState, execute::LinkedState, multi_sequence::MultiChainSequence, + sequence::ScriptSequenceKind, ScriptArgs, ScriptConfig, }; use alloy_primitives::{Bytes, B256}; use alloy_provider::Provider; use eyre::{OptionExt, Result}; +use forge_script_sequence::ScriptSequence; use foundry_cheatcodes::Wallets; use foundry_common::{ compile::ProjectCompiler, provider::try_get_http_provider, ContractData, ContractsByArtifact, diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index de06fa0c1..af44f2062 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -8,7 +8,6 @@ #[macro_use] extern crate tracing; -use self::transaction::AdditionalContract; use crate::runner::ScriptRunner; use alloy_json_abi::{Function, JsonAbi}; use alloy_primitives::{ @@ -22,6 +21,7 @@ use build::PreprocessedState; use clap::{Parser, ValueHint}; use dialoguer::Confirm; use eyre::{ContextCompat, Result}; +use forge_script_sequence::{AdditionalContract, NestedValue}; use forge_verify::RetryArgs; use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; use foundry_common::{ @@ -50,7 +50,7 @@ use foundry_evm::{ traces::{TraceMode, Traces}, }; use foundry_wallets::MultiWalletOpts; -use serde::{Deserialize, Serialize}; +use serde::Serialize; use yansi::Paint; mod broadcast; @@ -516,12 +516,6 @@ struct JsonResult<'a> { result: &'a ScriptResult, } -#[derive(Clone, Serialize, Deserialize)] -pub struct NestedValue { - pub internal_type: String, - pub value: String, -} - #[derive(Clone, Debug)] pub struct ScriptConfig { pub config: Config, diff --git a/crates/script/src/multi_sequence.rs b/crates/script/src/multi_sequence.rs index 24490bf01..0aabcf79a 100644 --- a/crates/script/src/multi_sequence.rs +++ b/crates/script/src/multi_sequence.rs @@ -1,6 +1,7 @@ -use super::sequence::{sig_to_file_name, ScriptSequence, SensitiveScriptSequence, DRY_RUN_DIR}; use eyre::{ContextCompat, Result, WrapErr}; -use foundry_cli::utils::now; +use forge_script_sequence::{ + now, sig_to_file_name, ScriptSequence, SensitiveScriptSequence, DRY_RUN_DIR, +}; use foundry_common::fs; use foundry_compilers::ArtifactId; use foundry_config::Config; diff --git a/crates/script/src/progress.rs b/crates/script/src/progress.rs index b8bedb6cd..d9fa53bb3 100644 --- a/crates/script/src/progress.rs +++ b/crates/script/src/progress.rs @@ -1,13 +1,11 @@ -use crate::{ - receipts::{check_tx_status, format_receipt, TxStatus}, - sequence::ScriptSequence, -}; +use crate::receipts::{check_tx_status, format_receipt, TxStatus}; use alloy_chains::Chain; use alloy_primitives::{ map::{B256HashMap, HashMap}, B256, }; use eyre::Result; +use forge_script_sequence::ScriptSequence; use foundry_cli::utils::init_progress; use foundry_common::provider::RetryProvider; use futures::StreamExt; diff --git a/crates/script/src/sequence.rs b/crates/script/src/sequence.rs index 3c31773ba..8bdb35bb3 100644 --- a/crates/script/src/sequence.rs +++ b/crates/script/src/sequence.rs @@ -1,23 +1,10 @@ -use super::{multi_sequence::MultiChainSequence, NestedValue}; -use crate::{ - transaction::{AdditionalContract, TransactionWithMetadata}, - verify::VerifyBundle, -}; -use alloy_primitives::{hex, map::HashMap, Address, TxHash}; -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, TransactionMaybeSigned, SELECTOR_LEN}; +use crate::multi_sequence::MultiChainSequence; +use eyre::Result; +use forge_script_sequence::ScriptSequence; +use foundry_cli::utils::Git; use foundry_compilers::ArtifactId; use foundry_config::Config; -use serde::{Deserialize, Serialize}; -use std::{ - collections::VecDeque, - io::{BufWriter, Write}, - path::{Path, PathBuf}, -}; -use yansi::Paint; +use std::path::Path; /// Returns the commit hash of the project if it exists pub fn get_commit_hash(root: &Path) -> Option { @@ -79,330 +66,3 @@ impl Drop for ScriptSequenceKind { } } } - -pub const DRY_RUN_DIR: &str = "dry-run"; - -/// Helper that saves the transactions sequence and its state on which transactions have been -/// broadcasted -#[derive(Clone, Default, Serialize, Deserialize)] -pub struct ScriptSequence { - pub transactions: VecDeque, - pub receipts: Vec, - pub libraries: Vec, - pub pending: Vec, - #[serde(skip)] - /// Contains paths to the sequence files - /// None if sequence should not be saved to disk (e.g. part of a multi-chain sequence) - pub paths: Option<(PathBuf, PathBuf)>, - pub returns: HashMap, - pub timestamp: u64, - pub chain: u64, - pub commit: Option, -} - -/// Sensitive values from the transactions in a script sequence -#[derive(Clone, Default, Serialize, Deserialize)] -pub struct SensitiveTransactionMetadata { - pub rpc: String, -} - -/// Sensitive info from the script sequence which is saved into the cache folder -#[derive(Clone, Default, Serialize, Deserialize)] -pub struct SensitiveScriptSequence { - pub transactions: VecDeque, -} - -impl From for SensitiveScriptSequence { - fn from(sequence: ScriptSequence) -> Self { - Self { - transactions: sequence - .transactions - .iter() - .map(|tx| SensitiveTransactionMetadata { rpc: tx.rpc.clone() }) - .collect(), - } - } -} - -impl ScriptSequence { - /// Loads The sequence for the corresponding json file - pub fn load( - config: &Config, - sig: &str, - target: &ArtifactId, - chain_id: u64, - dry_run: bool, - ) -> Result { - let (path, sensitive_path) = Self::get_paths(config, sig, target, chain_id, dry_run)?; - - let mut script_sequence: Self = foundry_compilers::utils::read_json_file(&path) - .wrap_err(format!("Deployment not found for chain `{chain_id}`."))?; - - let sensitive_script_sequence: SensitiveScriptSequence = - foundry_compilers::utils::read_json_file(&sensitive_path).wrap_err(format!( - "Deployment's sensitive details not found for chain `{chain_id}`." - ))?; - - script_sequence.fill_sensitive(&sensitive_script_sequence); - - script_sequence.paths = Some((path, sensitive_path)); - - Ok(script_sequence) - } - - /// Saves the transactions as file if it's a standalone deployment. - /// `save_ts` should be set to true for checkpoint updates, which might happen many times and - /// could result in us saving many identical files. - pub fn save(&mut self, silent: bool, save_ts: bool) -> Result<()> { - self.sort_receipts(); - - if self.transactions.is_empty() { - return Ok(()) - } - - let Some((path, sensitive_path)) = self.paths.clone() else { return Ok(()) }; - - self.timestamp = now().as_secs(); - let ts_name = format!("run-{}.json", self.timestamp); - - let sensitive_script_sequence: SensitiveScriptSequence = self.clone().into(); - - // broadcast folder writes - //../run-latest.json - let mut writer = BufWriter::new(fs::create_file(&path)?); - serde_json::to_writer_pretty(&mut writer, &self)?; - writer.flush()?; - if save_ts { - //../run-[timestamp].json - fs::copy(&path, path.with_file_name(&ts_name))?; - } - - // cache folder writes - //../run-latest.json - let mut writer = BufWriter::new(fs::create_file(&sensitive_path)?); - serde_json::to_writer_pretty(&mut writer, &sensitive_script_sequence)?; - writer.flush()?; - if save_ts { - //../run-[timestamp].json - fs::copy(&sensitive_path, sensitive_path.with_file_name(&ts_name))?; - } - - if !silent { - shell::println(format!("\nTransactions saved to: {}\n", path.display()))?; - shell::println(format!("Sensitive values saved to: {}\n", sensitive_path.display()))?; - } - - Ok(()) - } - - pub fn add_receipt(&mut self, receipt: AnyTransactionReceipt) { - self.receipts.push(receipt); - } - - /// Sorts all receipts with ascending transaction index - pub fn sort_receipts(&mut self) { - self.receipts.sort_by_key(|r| (r.block_number, r.transaction_index)); - } - - pub fn add_pending(&mut self, index: usize, tx_hash: TxHash) { - if !self.pending.contains(&tx_hash) { - self.transactions[index].hash = Some(tx_hash); - self.pending.push(tx_hash); - } - } - - pub fn remove_pending(&mut self, tx_hash: TxHash) { - self.pending.retain(|element| element != &tx_hash); - } - - /// Gets paths in the formats - /// `./broadcast/[contract_filename]/[chain_id]/[sig]-[timestamp].json` and - /// `./cache/[contract_filename]/[chain_id]/[sig]-[timestamp].json`. - pub fn get_paths( - config: &Config, - sig: &str, - target: &ArtifactId, - chain_id: u64, - dry_run: bool, - ) -> Result<(PathBuf, PathBuf)> { - let mut broadcast = config.broadcast.to_path_buf(); - let mut cache = config.cache_path.to_path_buf(); - let mut common = PathBuf::new(); - - let target_fname = target.source.file_name().wrap_err("No filename.")?; - common.push(target_fname); - common.push(chain_id.to_string()); - if dry_run { - common.push(DRY_RUN_DIR); - } - - broadcast.push(common.clone()); - cache.push(common); - - fs::create_dir_all(&broadcast)?; - fs::create_dir_all(&cache)?; - - // TODO: ideally we want the name of the function here if sig is calldata - let filename = sig_to_file_name(sig); - - broadcast.push(format!("{filename}-latest.json")); - cache.push(format!("{filename}-latest.json")); - - Ok((broadcast, cache)) - } - - /// Given the broadcast log, it matches transactions with receipts, and tries to verify any - /// created contract on etherscan. - pub async fn verify_contracts( - &mut self, - config: &Config, - mut verify: VerifyBundle, - ) -> Result<()> { - trace!(target: "script", "verifying {} contracts [{}]", verify.known_contracts.len(), self.chain); - - verify.set_chain(config, self.chain.into()); - - if verify.etherscan.has_key() || - verify.verifier.verifier != VerificationProviderType::Etherscan - { - trace!(target: "script", "prepare future verifications"); - - let mut future_verifications = Vec::with_capacity(self.receipts.len()); - let mut unverifiable_contracts = vec![]; - - // Make sure the receipts have the right order first. - self.sort_receipts(); - - for (receipt, tx) in self.receipts.iter_mut().zip(self.transactions.iter()) { - // create2 hash offset - let mut offset = 0; - - if tx.is_create2() { - receipt.contract_address = tx.contract_address; - offset = 32; - } - - // Verify contract created directly from the transaction - 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), - }; - } - - // Verify potential contracts created during the transaction execution - for AdditionalContract { address, init_code, .. } in &tx.additional_contracts { - match verify.get_verify_args(*address, 0, init_code.as_ref(), &self.libraries) { - Some(verify) => future_verifications.push(verify.run()), - None => unverifiable_contracts.push(*address), - }; - } - } - - trace!(target: "script", "collected {} verification jobs and {} unverifiable contracts", future_verifications.len(), unverifiable_contracts.len()); - - self.check_unverified(unverifiable_contracts, verify); - - let num_verifications = future_verifications.len(); - let mut num_of_successful_verifications = 0; - println!("##\nStart verification for ({num_verifications}) contracts"); - for verification in future_verifications { - match verification.await { - Ok(_) => { - num_of_successful_verifications += 1; - } - Err(err) => eprintln!("Error during verification: {err:#}"), - } - } - - if num_of_successful_verifications < num_verifications { - return Err(eyre!("Not all ({num_of_successful_verifications} / {num_verifications}) contracts were verified!")) - } - - println!("All ({num_verifications}) contracts were verified!"); - } - - Ok(()) - } - - /// Let the user know if there are any contracts which can not be verified. Also, present some - /// hints on potential causes. - fn check_unverified(&self, unverifiable_contracts: Vec
, verify: VerifyBundle) { - if !unverifiable_contracts.is_empty() { - println!( - "\n{}", - format!( - "We haven't found any matching bytecode for the following contracts: {:?}.\n\n{}", - unverifiable_contracts, - "This may occur when resuming a verification, but the underlying source code or compiler version has changed." - ) - .yellow() - .bold(), - ); - - if let Some(commit) = &self.commit { - let current_commit = verify - .project_paths - .root - .map(|root| get_commit_hash(&root).unwrap_or_default()) - .unwrap_or_default(); - - if ¤t_commit != commit { - println!("\tScript was broadcasted on commit `{commit}`, but we are at `{current_commit}`."); - } - } - } - } - - /// Returns the first RPC URL of this sequence. - pub fn rpc_url(&self) -> &str { - self.transactions.front().expect("empty sequence").rpc.as_str() - } - - /// Returns the list of the transactions without the metadata. - pub fn transactions(&self) -> impl Iterator { - self.transactions.iter().map(|tx| tx.tx()) - } - - pub fn fill_sensitive(&mut self, sensitive: &SensitiveScriptSequence) { - self.transactions - .iter_mut() - .enumerate() - .for_each(|(i, tx)| tx.rpc.clone_from(&sensitive.transactions[i].rpc)); - } -} - -/// Converts the `sig` argument into the corresponding file path. -/// -/// This accepts either the signature of the function or the raw calldata. -pub fn sig_to_file_name(sig: &str) -> String { - if let Some((name, _)) = sig.split_once('(') { - // strip until call argument parenthesis - return name.to_string() - } - // assume calldata if `sig` is hex - if let Ok(calldata) = hex::decode(sig) { - // in which case we return the function signature - return hex::encode(&calldata[..SELECTOR_LEN]) - } - - // return sig as is - sig.to_string() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn can_convert_sig() { - assert_eq!(sig_to_file_name("run()").as_str(), "run"); - assert_eq!( - sig_to_file_name( - "522bb704000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfFFb92266" - ) - .as_str(), - "522bb704" - ); - } -} diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 2fbfbcffa..44788672a 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -1,9 +1,6 @@ use super::{ - multi_sequence::MultiChainSequence, - providers::ProvidersManager, - runner::ScriptRunner, - sequence::{ScriptSequence, ScriptSequenceKind}, - transaction::TransactionWithMetadata, + multi_sequence::MultiChainSequence, providers::ProvidersManager, runner::ScriptRunner, + sequence::ScriptSequenceKind, transaction::ScriptTransactionBuilder, }; use crate::{ broadcast::{estimate_gas, BundledState}, @@ -16,6 +13,7 @@ use alloy_network::TransactionBuilder; use alloy_primitives::{map::HashMap, utils::format_units, Address, Bytes, TxKind, U256}; use dialoguer::Confirm; use eyre::{Context, Result}; +use forge_script_sequence::{ScriptSequence, TransactionWithMetadata}; use foundry_cheatcodes::Wallets; use foundry_cli::utils::{has_different_gas_calc, now}; use foundry_common::{get_contract_name, shell, ContractData}; @@ -60,12 +58,19 @@ impl PreSimulationState { .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, - ) + let sender = tx.transaction.from().expect("all transactions should have a sender"); + let nonce = tx.transaction.nonce().expect("all transactions should have a sender"); + let to = tx.transaction.to(); + + let mut builder = ScriptTransactionBuilder::new(tx.transaction, rpc); + + if let Some(TxKind::Call(_)) = to { + builder.set_call(&address_to_abi, &self.execution_artifacts.decoder)?; + } else { + builder.set_create(false, sender.create(nonce), &address_to_abi)?; + } + + Ok(builder.build()) }) .collect::>>()?; @@ -111,7 +116,7 @@ impl PreSimulationState { .map(|mut transaction| async { let mut runner = runners.get(&transaction.rpc).expect("invalid rpc url").write(); - let tx = &mut transaction.transaction; + let tx = transaction.tx_mut(); let to = if let Some(TxKind::Call(to)) = tx.to() { Some(to) } else { None }; let result = runner .simulate( @@ -138,8 +143,9 @@ impl PreSimulationState { false }; - let transaction = - transaction.with_execution_result(&result, self.args.gas_estimate_multiplier); + let transaction = ScriptTransactionBuilder::from(transaction) + .with_execution_result(&result, self.args.gas_estimate_multiplier) + .build(); eyre::Ok((Some(transaction), is_noop_tx, result.traces)) }) @@ -265,10 +271,10 @@ impl FilledTransactionsState { let mut txes_iter = self.transactions.clone().into_iter().peekable(); while let Some(mut tx) = txes_iter.next() { - let tx_rpc = tx.rpc.clone(); + let tx_rpc = tx.rpc.to_owned(); let provider_info = manager.get_or_init_provider(&tx.rpc, self.args.legacy).await?; - if let Some(tx) = tx.transaction.as_unsigned_mut() { + if let Some(tx) = tx.tx_mut().as_unsigned_mut() { // Handles chain specific requirements for unsigned transactions. tx.set_chain_id(provider_info.chain); } @@ -418,7 +424,7 @@ impl FilledTransactionsState { }) .collect(); - Ok(ScriptSequence { + let sequence = ScriptSequence { transactions, returns: self.execution_artifacts.returns.clone(), receipts: vec![], @@ -428,6 +434,7 @@ impl FilledTransactionsState { libraries, chain, commit, - }) + }; + Ok(sequence) } } diff --git a/crates/script/src/transaction.rs b/crates/script/src/transaction.rs index 639c5f022..39bf0490e 100644 --- a/crates/script/src/transaction.rs +++ b/crates/script/src/transaction.rs @@ -1,88 +1,41 @@ use super::ScriptResult; use alloy_dyn_abi::JsonAbiExt; -use alloy_primitives::{hex, Address, Bytes, TxKind, B256}; -use eyre::{Result, WrapErr}; +use alloy_primitives::{hex, Address, TxKind, B256}; +use eyre::Result; +use forge_script_sequence::TransactionWithMetadata; 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; -use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct AdditionalContract { - #[serde(rename = "transactionType")] - pub opcode: CallKind, - pub address: Address, - pub init_code: Bytes, +#[derive(Debug)] +pub struct ScriptTransactionBuilder { + transaction: TransactionWithMetadata, } -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct TransactionWithMetadata { - pub hash: Option, - #[serde(rename = "transactionType")] - pub opcode: CallKind, - #[serde(default = "default_string")] - pub contract_name: Option, - #[serde(default = "default_address")] - pub contract_address: Option
, - #[serde(default = "default_string")] - pub function: Option, - #[serde(default = "default_vec_of_strings")] - pub arguments: Option>, - #[serde(skip)] - pub rpc: String, - pub transaction: TransactionMaybeSigned, - pub additional_contracts: Vec, - pub is_fixed_gas_limit: bool, -} - -fn default_string() -> Option { - Some(String::new()) -} - -fn default_address() -> Option
{ - Some(Address::ZERO) -} - -fn default_vec_of_strings() -> Option> { - Some(vec![]) -} +impl ScriptTransactionBuilder { + pub fn new(transaction: TransactionMaybeSigned, rpc: String) -> Self { + let mut transaction = TransactionWithMetadata::from_tx_request(transaction); + transaction.rpc = rpc; + // If tx.gas is already set that means it was specified in script + transaction.is_fixed_gas_limit = transaction.tx().gas().is_some(); -impl TransactionWithMetadata { - 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(), - } + Self { transaction } } - pub fn new( - transaction: TransactionMaybeSigned, - rpc: String, + /// Populate the transaction as CALL tx + pub fn set_call( + &mut self, local_contracts: &BTreeMap, decoder: &CallTraceDecoder, - ) -> Result { - let mut metadata = Self::from_tx_request(transaction); - metadata.rpc = rpc; - // If tx.gas is already set that means it was specified in script - metadata.is_fixed_gas_limit = metadata.tx().gas().is_some(); - - if let Some(TxKind::Call(to)) = metadata.transaction.to() { + ) -> Result<()> { + if let Some(TxKind::Call(to)) = self.transaction.transaction.to() { if to == DEFAULT_CREATE2_DEPLOYER { - if let Some(input) = metadata.transaction.input() { + if let Some(input) = self.transaction.transaction.input() { let (salt, init_code) = input.split_at(32); - metadata.set_create( + + self.set_create( true, DEFAULT_CREATE2_DEPLOYER .create2_from_code(B256::from_slice(salt), init_code), @@ -90,65 +43,68 @@ impl TransactionWithMetadata { )?; } } else { - metadata - .set_call(to, local_contracts, decoder) - .wrap_err("Could not decode transaction type.")?; - } - } else { - 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)?; - } + self.transaction.opcode = CallKind::Call; + self.transaction.contract_address = Some(to); - 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(); + let Some(data) = self.transaction.transaction.input() else { return Ok(()) }; - // Add the additional contracts created in this transaction, so we can verify them later. - 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 data.len() < SELECTOR_LEN { + return Ok(()); + } - 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); + let (selector, data) = data.split_at(SELECTOR_LEN); + + let function = if let Some(info) = local_contracts.get(&to) { + // This CALL is made to a local contract. + self.transaction.contract_name = Some(info.name.clone()); + info.abi.functions().find(|function| function.selector() == selector) + } else { + // This CALL is made to an external contract; try to decode it from the given + // decoder. + decoder.functions.get(selector).and_then(|v| v.first()) + }; + + if let Some(function) = function { + self.transaction.function = Some(function.signature()); + + let values = function.abi_decode_input(data, false).inspect_err(|_| { + error!( + contract=?self.transaction.contract_name, + signature=?function, + data=hex::encode(data), + "Failed to decode function arguments", + ); + })?; + self.transaction.arguments = + Some(values.iter().map(format_token_raw).collect()); + } } } - self + Ok(()) } /// Populate the transaction as CREATE tx /// /// If this is a CREATE2 transaction this attempt to decode the arguments from the CREATE2 /// deployer's function - fn set_create( + pub fn set_create( &mut self, is_create2: bool, address: Address, contracts: &BTreeMap, ) -> Result<()> { if is_create2 { - self.opcode = CallKind::Create2; + self.transaction.opcode = CallKind::Create2; } else { - self.opcode = CallKind::Create; + self.transaction.opcode = CallKind::Create; } let info = contracts.get(&address); - self.contract_name = info.map(|info| info.name.clone()); - self.contract_address = Some(address); + self.transaction.contract_name = info.map(|info| info.name.clone()); + self.transaction.contract_address = Some(address); - let Some(data) = self.transaction.input() else { return Ok(()) }; + let Some(data) = self.transaction.transaction.input() else { return Ok(()) }; let Some(info) = info else { return Ok(()) }; let Some(bytecode) = info.bytecode() else { return Ok(()) }; @@ -171,70 +127,51 @@ impl TransactionWithMetadata { let Some(constructor) = info.abi.constructor() else { return Ok(()) }; let values = constructor.abi_decode_input(constructor_args, false).inspect_err(|_| { - error!( - contract=?self.contract_name, - signature=%format!("constructor({})", constructor.inputs.iter().map(|p| &p.ty).format(",")), - is_create2, - constructor_args=%hex::encode(constructor_args), - "Failed to decode constructor arguments", - ); - debug!(full_data=%hex::encode(data), bytecode=%hex::encode(creation_code)); - })?; - self.arguments = Some(values.iter().map(format_token_raw).collect()); + error!( + contract=?self.transaction.contract_name, + signature=%format!("constructor({})", constructor.inputs.iter().map(|p| &p.ty).format(",")), + is_create2, + constructor_args=%hex::encode(constructor_args), + "Failed to decode constructor arguments", + ); + debug!(full_data=%hex::encode(data), bytecode=%hex::encode(creation_code)); + })?; + self.transaction.arguments = Some(values.iter().map(format_token_raw).collect()); Ok(()) } - /// Populate the transaction as CALL tx - fn set_call( - &mut self, - target: Address, - local_contracts: &BTreeMap, - decoder: &CallTraceDecoder, - ) -> Result<()> { - self.opcode = CallKind::Call; - self.contract_address = Some(target); - - let Some(data) = self.transaction.input() else { return Ok(()) }; - if data.len() < SELECTOR_LEN { - return Ok(()); - } - let (selector, data) = data.split_at(SELECTOR_LEN); + /// 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(); - let function = if let Some(info) = local_contracts.get(&target) { - // This CALL is made to a local contract. - self.contract_name = Some(info.name.clone()); - info.abi.functions().find(|function| function.selector() == selector) - } else { - // This CALL is made to an external contract; try to decode it from the given decoder. - decoder.functions.get(selector).and_then(|v| v.first()) - }; - if let Some(function) = function { - self.function = Some(function.signature()); + // Add the additional contracts created in this transaction, so we can verify them later. + created_contracts.retain(|contract| { + // Filter out the contract that was created by the transaction itself. + self.transaction.contract_address.map_or(true, |addr| addr != contract.address) + }); - let values = function.abi_decode_input(data, false).inspect_err(|_| { - error!( - contract=?self.contract_name, - signature=?function, - data=hex::encode(data), - "Failed to decode function arguments", - ); - })?; - self.arguments = Some(values.iter().map(format_token_raw).collect()); + if !self.transaction.is_fixed_gas_limit { + if let Some(unsigned) = self.transaction.transaction.as_unsigned_mut() { + // We inflate the gas used by the user specified percentage + unsigned.gas = Some(result.gas_used * gas_estimate_multiplier / 100); + } } - Ok(()) - } - - pub fn tx(&self) -> &TransactionMaybeSigned { - &self.transaction + self } - pub fn tx_mut(&mut self) -> &mut TransactionMaybeSigned { - &mut self.transaction + pub fn build(self) -> TransactionWithMetadata { + self.transaction } +} - pub fn is_create2(&self) -> bool { - self.opcode == CallKind::Create2 +impl From for ScriptTransactionBuilder { + fn from(transaction: TransactionWithMetadata) -> Self { + Self { transaction } } } diff --git a/crates/script/src/verify.rs b/crates/script/src/verify.rs index 18ee6ccf2..74cc4a3f9 100644 --- a/crates/script/src/verify.rs +++ b/crates/script/src/verify.rs @@ -1,12 +1,18 @@ -use crate::{build::LinkedBuildData, sequence::ScriptSequenceKind, ScriptArgs, ScriptConfig}; +use crate::{ + build::LinkedBuildData, + sequence::{get_commit_hash, ScriptSequenceKind}, + ScriptArgs, ScriptConfig, +}; use alloy_primitives::{hex, Address}; -use eyre::Result; -use forge_verify::{RetryArgs, VerifierArgs, VerifyArgs}; +use eyre::{eyre, Result}; +use forge_script_sequence::{AdditionalContract, ScriptSequence}; +use forge_verify::{provider::VerificationProviderType, RetryArgs, VerifierArgs, VerifyArgs}; use foundry_cli::opts::{EtherscanOpts, ProjectPathsArgs}; use foundry_common::ContractsByArtifact; use foundry_compilers::{info::ContractInfo, Project}; use foundry_config::{Chain, Config}; use semver::Version; +use yansi::Paint; /// State after we have broadcasted the script. /// It is assumed that at this point [BroadcastedState::sequence] contains receipts for all @@ -31,7 +37,7 @@ impl BroadcastedState { ); for sequence in sequence.sequences_mut() { - sequence.verify_contracts(&script_config.config, verify.clone()).await?; + verify_contracts(sequence, &script_config.config, verify.clone()).await?; } Ok(()) @@ -158,3 +164,107 @@ impl VerifyBundle { None } } + +/// Given the broadcast log, it matches transactions with receipts, and tries to verify any +/// created contract on etherscan. +async fn verify_contracts( + sequence: &mut ScriptSequence, + config: &Config, + mut verify: VerifyBundle, +) -> Result<()> { + trace!(target: "script", "verifying {} contracts [{}]", verify.known_contracts.len(), sequence.chain); + + verify.set_chain(config, sequence.chain.into()); + + if verify.etherscan.has_key() || verify.verifier.verifier != VerificationProviderType::Etherscan + { + trace!(target: "script", "prepare future verifications"); + + let mut future_verifications = Vec::with_capacity(sequence.receipts.len()); + let mut unverifiable_contracts = vec![]; + + // Make sure the receipts have the right order first. + sequence.sort_receipts(); + + for (receipt, tx) in sequence.receipts.iter_mut().zip(sequence.transactions.iter()) { + // create2 hash offset + let mut offset = 0; + + if tx.is_create2() { + receipt.contract_address = tx.contract_address; + offset = 32; + } + + // Verify contract created directly from the transaction + if let (Some(address), Some(data)) = (receipt.contract_address, tx.tx().input()) { + match verify.get_verify_args(address, offset, data, &sequence.libraries) { + Some(verify) => future_verifications.push(verify.run()), + None => unverifiable_contracts.push(address), + }; + } + + // Verify potential contracts created during the transaction execution + for AdditionalContract { address, init_code, .. } in &tx.additional_contracts { + match verify.get_verify_args(*address, 0, init_code.as_ref(), &sequence.libraries) { + Some(verify) => future_verifications.push(verify.run()), + None => unverifiable_contracts.push(*address), + }; + } + } + + trace!(target: "script", "collected {} verification jobs and {} unverifiable contracts", future_verifications.len(), unverifiable_contracts.len()); + + check_unverified(sequence, unverifiable_contracts, verify); + + let num_verifications = future_verifications.len(); + let mut num_of_successful_verifications = 0; + println!("##\nStart verification for ({num_verifications}) contracts"); + for verification in future_verifications { + match verification.await { + Ok(_) => { + num_of_successful_verifications += 1; + } + Err(err) => eprintln!("Error during verification: {err:#}"), + } + } + + if num_of_successful_verifications < num_verifications { + return Err(eyre!("Not all ({num_of_successful_verifications} / {num_verifications}) contracts were verified!")) + } + + println!("All ({num_verifications}) contracts were verified!"); + } + + Ok(()) +} + +fn check_unverified( + sequence: &ScriptSequence, + unverifiable_contracts: Vec
, + verify: VerifyBundle, +) { + if !unverifiable_contracts.is_empty() { + println!( + "\n{}", + format!( + "We haven't found any matching bytecode for the following contracts: {:?}.\n\n{}", + unverifiable_contracts, + "This may occur when resuming a verification, but the underlying source code or compiler version has changed." + ) + .yellow() + .bold(), + ); + + if let Some(commit) = &sequence.commit { + let current_commit = verify + .project_paths + .root + .map(|root| get_commit_hash(&root).unwrap_or_default()) + .unwrap_or_default(); + + if ¤t_commit != commit { + println!("\tScript was broadcasted on commit `{commit}`, but we are at `{current_commit}`."); + } + } + } +}