Skip to content

fix: Respect --override-* flags on cast call with --trace flag #10721

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/cast/src/cmd/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ impl CallArgs {
trace_mode,
odyssey,
create2_deployer,
state_overrides,
)?;

let value = tx.value.unwrap_or_default();
Expand Down
1 change: 1 addition & 0 deletions crates/cast/src/cmd/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
123 changes: 123 additions & 0 deletions crates/cast/tests/cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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]

"#]]);
});

Expand Down
1 change: 1 addition & 0 deletions crates/evm/evm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
29 changes: 29 additions & 0 deletions crates/evm/evm/src/executors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<U256, U256>,
) -> 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<bool> {
Ok(self.backend().basic_ref(address)?.map(|acc| acc.is_empty_code_hash()).unwrap_or(true))
Expand Down
56 changes: 44 additions & 12 deletions crates/evm/evm/src/executors/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -23,18 +25,48 @@ impl TracingExecutor {
trace_mode: TraceMode,
odyssey: bool,
create2_deployer: Address,
state_overrides: Option<StateOverride>,
) -> eyre::Result<Self> {
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<U256, U256> = 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
Expand Down
1 change: 1 addition & 0 deletions crates/verify/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ pub async fn get_tracing_executor(
TraceMode::Call,
is_odyssey,
create2_deployer,
None,
)?;

Ok((env, executor))
Expand Down
Loading