From 62f1681764e8a2ad3c19cd360afdf65c3bd589c3 Mon Sep 17 00:00:00 2001 From: George Bott Date: Thu, 5 Jun 2025 21:29:13 +0000 Subject: [PATCH 1/3] fix: Respect `--override-*` flags on `cast call` with `--trace` flag --- Cargo.lock | 1 + crates/cast/src/cmd/call.rs | 1 + crates/cast/src/cmd/run.rs | 1 + crates/evm/evm/Cargo.toml | 1 + crates/evm/evm/src/executors/mod.rs | 29 ++++++++++++++ crates/evm/evm/src/executors/trace.rs | 56 +++++++++++++++++++++------ crates/verify/src/utils.rs | 1 + 7 files changed, 78 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b92cb3acb23a..24f3798ac469b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4420,6 +4420,7 @@ dependencies = [ "alloy-evm", "alloy-json-abi", "alloy-primitives", + "alloy-rpc-types", "alloy-sol-types", "eyre", "foundry-cheatcodes", diff --git a/crates/cast/src/cmd/call.rs b/crates/cast/src/cmd/call.rs index c329dbe3cc717..3abf0ee5a2bd0 100644 --- a/crates/cast/src/cmd/call.rs +++ b/crates/cast/src/cmd/call.rs @@ -271,6 +271,7 @@ impl CallArgs { trace_mode, odyssey, create2_deployer, + state_overrides, )?; let value = tx.value.unwrap_or_default(); diff --git a/crates/cast/src/cmd/run.rs b/crates/cast/src/cmd/run.rs index 04f52957ef668..ae6ceb5ab9dbd 100644 --- a/crates/cast/src/cmd/run.rs +++ b/crates/cast/src/cmd/run.rs @@ -186,6 +186,7 @@ impl RunArgs { trace_mode, odyssey, create2_deployer, + None, )?; let mut env = Env::new_with_spec_id( env.evm_env.cfg_env.clone(), diff --git a/crates/evm/evm/Cargo.toml b/crates/evm/evm/Cargo.toml index 06074e305b630..e14c9f3f0156b 100644 --- a/crates/evm/evm/Cargo.toml +++ b/crates/evm/evm/Cargo.toml @@ -32,6 +32,7 @@ alloy-primitives = { workspace = true, features = [ "arbitrary", "rlp", ] } +alloy-rpc-types.workspace = true alloy-sol-types.workspace = true revm = { workspace = true, default-features = false, features = [ "std", diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index bfd57a4a0ccee..bd1ecaddc4281 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -258,6 +258,35 @@ impl Executor { Ok(self.backend().basic_ref(address)?.map(|acc| acc.nonce).unwrap_or_default()) } + /// Set the code of an account. + pub fn set_code(&mut self, address: Address, code: Bytecode) -> BackendResult<()> { + let mut account = self.backend().basic_ref(address)?.unwrap_or_default(); + account.code = Some(code); + self.backend_mut().insert_account_info(address, account); + Ok(()) + } + + /// Set the storage of an account. + pub fn set_storage( + &mut self, + address: Address, + storage: HashMap, + ) -> BackendResult<()> { + self.backend_mut().replace_account_storage(address, storage)?; + Ok(()) + } + + /// Set a storage slot of an account. + pub fn set_storage_slot( + &mut self, + address: Address, + slot: U256, + value: U256, + ) -> BackendResult<()> { + self.backend_mut().insert_account_storage(address, slot, value)?; + Ok(()) + } + /// Returns `true` if the account has no code. pub fn is_empty_code(&self, address: Address) -> BackendResult { Ok(self.backend().basic_ref(address)?.map(|acc| acc.is_empty_code_hash()).unwrap_or(true)) diff --git a/crates/evm/evm/src/executors/trace.rs b/crates/evm/evm/src/executors/trace.rs index c58ee8ddd6e59..118d56d117859 100644 --- a/crates/evm/evm/src/executors/trace.rs +++ b/crates/evm/evm/src/executors/trace.rs @@ -2,12 +2,14 @@ use crate::{ executors::{Executor, ExecutorBuilder}, Env, }; -use alloy_primitives::Address; +use alloy_primitives::{map::HashMap, Address, U256}; +use alloy_rpc_types::state::StateOverride; +use eyre::Context; use foundry_compilers::artifacts::EvmVersion; use foundry_config::{utils::evm_spec_id, Chain, Config}; use foundry_evm_core::{backend::Backend, fork::CreateFork, opts::EvmOpts}; use foundry_evm_traces::TraceMode; -use revm::primitives::hardfork::SpecId; +use revm::{primitives::hardfork::SpecId, state::Bytecode}; use std::ops::{Deref, DerefMut}; /// A default executor with tracing enabled @@ -23,18 +25,48 @@ impl TracingExecutor { trace_mode: TraceMode, odyssey: bool, create2_deployer: Address, + state_overrides: Option, ) -> eyre::Result { let db = Backend::spawn(fork)?; - Ok(Self { - // configures a bare version of the evm executor: no cheatcode inspector is enabled, - // tracing will be enabled only for the targeted transaction - executor: ExecutorBuilder::new() - .inspectors(|stack| { - stack.trace_mode(trace_mode).odyssey(odyssey).create2_deployer(create2_deployer) - }) - .spec_id(evm_spec_id(version.unwrap_or_default(), odyssey)) - .build(env, db), - }) + // configures a bare version of the evm executor: no cheatcode inspector is enabled, + // tracing will be enabled only for the targeted transaction + let mut executor = ExecutorBuilder::new() + .inspectors(|stack| { + stack.trace_mode(trace_mode).odyssey(odyssey).create2_deployer(create2_deployer) + }) + .spec_id(evm_spec_id(version.unwrap_or_default(), odyssey)) + .build(env, db); + + // Apply the state overrides. + if let Some(state_overrides) = state_overrides { + for (address, overrides) in state_overrides { + if let Some(balance) = overrides.balance { + executor.set_balance(address, balance)?; + } + if let Some(nonce) = overrides.nonce { + executor.set_nonce(address, nonce)?; + } + if let Some(code) = overrides.code { + let bytecode = Bytecode::new_raw_checked(code) + .wrap_err("invalid bytecode in state override")?; + executor.set_code(address, bytecode)?; + } + if let Some(state) = overrides.state { + let state: HashMap = state + .into_iter() + .map(|(slot, value)| (slot.into(), value.into())) + .collect(); + executor.set_storage(address, state)?; + } + if let Some(state_diff) = overrides.state_diff { + for (slot, value) in state_diff { + executor.set_storage_slot(address, slot.into(), value.into())?; + } + } + } + } + + Ok(Self { executor }) } /// Returns the spec id of the executor diff --git a/crates/verify/src/utils.rs b/crates/verify/src/utils.rs index 03eef1ff789ae..2c73e569da81f 100644 --- a/crates/verify/src/utils.rs +++ b/crates/verify/src/utils.rs @@ -321,6 +321,7 @@ pub async fn get_tracing_executor( TraceMode::Call, is_odyssey, create2_deployer, + None, )?; Ok((env, executor)) From a63319a6f05999754a10cd13aecd37a0485db36b Mon Sep 17 00:00:00 2001 From: George Bott Date: Fri, 6 Jun 2025 13:59:19 +0000 Subject: [PATCH 2/3] feat: Add tests for `cast call` with `--override-*` and `--trace` flags --- crates/cast/tests/cli/main.rs | 123 ++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index bf825cdd9f95f..00084ca04484c 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -2430,6 +2430,30 @@ contract Counter { .stdout_eq(str![[r#" 4660 +"#]]); + + // Override state, `number()` should return overridden value. + cmd.cast_fuse() + .args([ + "call", + "0x5FbDB2315678afecb367f032d93F642f64180aa3", + "--rpc-url", + &handle.http_endpoint(), + "--override-state", + "0x5FbDB2315678afecb367f032d93F642f64180aa3:0x0:0x1234", + "number()(uint256)", + "--trace", + ]) + .assert_success() + .stdout_eq(str![[r#" +Traces: + [2402] 0x5FbDB2315678afecb367f032d93F642f64180aa3::number() + └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000001234 + + +Transaction successfully executed. +[GAS] + "#]]); // Override balance, `getBalance()` should return overridden value. @@ -2448,6 +2472,31 @@ contract Counter { .stdout_eq(str![[r#" 4369 +"#]]); + + // Override balance, `getBalance()` should return overridden value. + cmd.cast_fuse() + .args([ + "call", + "0x5FbDB2315678afecb367f032d93F642f64180aa3", + "--rpc-url", + &handle.http_endpoint(), + "--override-balance", + "0x5FbDB2315678afecb367f032d93F642f64180aa3:0x1111", + "getBalance(address)(uint256)", + "0x5FbDB2315678afecb367f032d93F642f64180aa3", + "--trace", + ]) + .assert_success() + .stdout_eq(str![[r#" +Traces: + [747] 0x5FbDB2315678afecb367f032d93F642f64180aa3::getBalance(0x5FbDB2315678afecb367f032d93F642f64180aa3) + └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000001111 + + +Transaction successfully executed. +[GAS] + "#]]); // Override code with @@ -2469,6 +2518,28 @@ contract Counter { .stderr_eq(str![[r#" Error: server returned an error response: error code 3: execution reverted, data: "0x" +"#]]); + + // Override code with + // contract Counter { + // uint256 public number1; + // } + // Calling `number()` should revert. + cmd.cast_fuse() + .args([ + "call", + "0x5FbDB2315678afecb367f032d93F642f64180aa3", + "--rpc-url", + &handle.http_endpoint(), + "--override-code", + "0x5FbDB2315678afecb367f032d93F642f64180aa3:0x6080604052348015600e575f5ffd5b50600436106026575f3560e01c8063c223a39e14602a575b5f5ffd5b60306044565b604051603b9190605f565b60405180910390f35b5f5481565b5f819050919050565b6059816049565b82525050565b5f60208201905060705f8301846052565b9291505056fea26469706673582212202a0acfb9083efed3e0e9f27177b090731d4392cf196d58e27e05088f59008d0964736f6c634300081d0033", + "number()(uint256)", + "--trace" + ]) + .assert_success() + .stderr_eq(str![[r#" +Error: Transaction failed. + "#]]); // Calling `number1()` with overridden state should return new value. @@ -2488,6 +2559,32 @@ Error: server returned an error response: error code 3: execution reverted, data .stdout_eq(str![[r#" 8738 +"#]]); + + // Calling `number1()` with overridden state should return new value. + cmd.cast_fuse() + .args([ + "call", + "0x5FbDB2315678afecb367f032d93F642f64180aa3", + "--rpc-url", + &handle.http_endpoint(), + "--override-code", + "0x5FbDB2315678afecb367f032d93F642f64180aa3:0x6080604052348015600e575f5ffd5b50600436106026575f3560e01c8063c223a39e14602a575b5f5ffd5b60306044565b604051603b9190605f565b60405180910390f35b5f5481565b5f819050919050565b6059816049565b82525050565b5f60208201905060705f8301846052565b9291505056fea26469706673582212202a0acfb9083efed3e0e9f27177b090731d4392cf196d58e27e05088f59008d0964736f6c634300081d0033", + "--override-state", + "0x5FbDB2315678afecb367f032d93F642f64180aa3:0x0:0x2222", + "number1()(uint256)", + "--trace" + ]) + .assert_success() + .stdout_eq(str![[r#" +Traces: + [2402] 0x5FbDB2315678afecb367f032d93F642f64180aa3::number1() + └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000002222 + + +Transaction successfully executed. +[GAS] + "#]]); // Calling `number1()` with overridden state should return new value. @@ -2507,6 +2604,32 @@ Error: server returned an error response: error code 3: execution reverted, data .stdout_eq(str![[r#" 8738 +"#]]); + + // Calling `number1()` with overridden state should return new value. + cmd.cast_fuse() + .args([ + "call", + "0x5FbDB2315678afecb367f032d93F642f64180aa3", + "--rpc-url", + &handle.http_endpoint(), + "--override-code", + "0x5FbDB2315678afecb367f032d93F642f64180aa3:0x6080604052348015600e575f5ffd5b50600436106026575f3560e01c8063c223a39e14602a575b5f5ffd5b60306044565b604051603b9190605f565b60405180910390f35b5f5481565b5f819050919050565b6059816049565b82525050565b5f60208201905060705f8301846052565b9291505056fea26469706673582212202a0acfb9083efed3e0e9f27177b090731d4392cf196d58e27e05088f59008d0964736f6c634300081d0033", + "--override-state-diff", + "0x5FbDB2315678afecb367f032d93F642f64180aa3:0x0:0x2222", + "number1()(uint256)", + "--trace", + ]) + .assert_success() + .stdout_eq(str![[r#" +Traces: + [2402] 0x5FbDB2315678afecb367f032d93F642f64180aa3::number1() + └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000002222 + + +Transaction successfully executed. +[GAS] + "#]]); }); From 5cde566232a95a22edcd3700d2af0941e155dc4f Mon Sep 17 00:00:00 2001 From: George Bott Date: Thu, 19 Jun 2025 14:55:58 +0000 Subject: [PATCH 3/3] feat: Support block number and block time overrides with `cast call --trace` --- crates/cast/src/cmd/call.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/crates/cast/src/cmd/call.rs b/crates/cast/src/cmd/call.rs index 3abf0ee5a2bd0..ebd62120d38c6 100644 --- a/crates/cast/src/cmd/call.rs +++ b/crates/cast/src/cmd/call.rs @@ -10,7 +10,7 @@ use alloy_rpc_types::{ BlockId, BlockNumberOrTag, BlockOverrides, }; use clap::Parser; -use eyre::Result; +use eyre::{Context, Result}; use foundry_cli::{ opts::{EthereumOpts, TransactionOpts}, utils::{self, handle_traces, parse_ether_value, TraceResult}, @@ -256,6 +256,17 @@ impl CallArgs { env.evm_env.cfg_env.disable_block_gas_limit = true; env.evm_env.block_env.gas_limit = u64::MAX; + // Apply the block overrides. + if let Some(block_overrides) = block_overrides { + if let Some(number) = block_overrides.number { + env.evm_env.block_env.number = + u64::try_from(number).wrap_err("block number out of range")?; + } + if let Some(time) = block_overrides.time { + env.evm_env.block_env.timestamp = time; + } + } + let trace_mode = TraceMode::Call .with_debug(debug) .with_decode_internal(if decode_internal {