Skip to content

Commit 3396cc7

Browse files
authored
fix(cast): allow comma-separated values for overrides (#11553)
* fix(cast): allow comma sepparated values for overrides * Fix multiple slots * Tests with traces
1 parent a131c31 commit 3396cc7

File tree

2 files changed

+88
-27
lines changed

2 files changed

+88
-27
lines changed

crates/cast/src/cmd/call.rs

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
use super::run::fetch_contracts_bytecode_from_trace;
12
use crate::{
23
Cast,
34
traces::TraceKind,
45
tx::{CastTxBuilder, SenderKind},
56
};
67
use alloy_ens::NameOrAddress;
7-
use alloy_primitives::{Address, Bytes, TxKind, U256};
8+
use alloy_primitives::{Address, B256, Bytes, TxKind, U256, map::HashMap};
89
use alloy_provider::Provider;
910
use alloy_rpc_types::{
1011
BlockId, BlockNumberOrTag, BlockOverrides,
@@ -35,8 +36,6 @@ use regex::Regex;
3536
use revm::context::TransactionType;
3637
use std::{str::FromStr, sync::LazyLock};
3738

38-
use super::run::fetch_contracts_bytecode_from_trace;
39-
4039
// matches override pattern <address>:<slot>:<value>
4140
// e.g. 0x123:0x1:0x1234
4241
static OVERRIDE_PATTERN: LazyLock<Regex> =
@@ -133,29 +132,29 @@ pub struct CallArgs {
133132
#[arg(long, visible_alias = "la")]
134133
pub with_local_artifacts: bool,
135134

136-
/// Override the balance of an account.
137-
/// Format: address:balance
138-
#[arg(long = "override-balance", value_name = "ADDRESS:BALANCE")]
135+
/// Override the accounts balance.
136+
/// Format: "address:balance,address:balance"
137+
#[arg(long = "override-balance", value_name = "ADDRESS:BALANCE", value_delimiter = ',')]
139138
pub balance_overrides: Option<Vec<String>>,
140139

141-
/// Override the nonce of an account.
142-
/// Format: address:nonce
143-
#[arg(long = "override-nonce", value_name = "ADDRESS:NONCE")]
140+
/// Override the accounts nonce.
141+
/// Format: "address:nonce,address:nonce"
142+
#[arg(long = "override-nonce", value_name = "ADDRESS:NONCE", value_delimiter = ',')]
144143
pub nonce_overrides: Option<Vec<String>>,
145144

146-
/// Override the code of an account.
147-
/// Format: address:code
148-
#[arg(long = "override-code", value_name = "ADDRESS:CODE")]
145+
/// Override the accounts code.
146+
/// Format: "address:code,address:code"
147+
#[arg(long = "override-code", value_name = "ADDRESS:CODE", value_delimiter = ',')]
149148
pub code_overrides: Option<Vec<String>>,
150149

151-
/// Override the state of an account.
152-
/// Format: address:slot:value
153-
#[arg(long = "override-state", value_name = "ADDRESS:SLOT:VALUE")]
150+
/// Override the accounts state and replace the current state entirely with the new one.
151+
/// Format: "address:slot:value,address:slot:value"
152+
#[arg(long = "override-state", value_name = "ADDRESS:SLOT:VALUE", value_delimiter = ',')]
154153
pub state_overrides: Option<Vec<String>>,
155154

156-
/// Override the state diff of an account.
157-
/// Format: address:slot:value
158-
#[arg(long = "override-state-diff", value_name = "ADDRESS:SLOT:VALUE")]
155+
/// Override the accounts state specific slots and preserve the rest of the state.
156+
/// Format: "address:slot:value,address:slot:value"
157+
#[arg(long = "override-state-diff", value_name = "ADDRESS:SLOT:VALUE", value_delimiter = ',')]
159158
pub state_diff_overrides: Option<Vec<String>>,
160159

161160
/// Override the block timestamp.
@@ -402,18 +401,29 @@ impl CallArgs {
402401
state_overrides_builder.with_code(addr.parse()?, Bytes::from_str(code_str)?);
403402
}
404403

405-
// Parse state overrides
406-
for override_str in self.state_overrides.iter().flatten() {
407-
let (addr, slot, value) = address_slot_value_override(override_str)?;
408-
state_overrides_builder =
409-
state_overrides_builder.with_state(addr, [(slot.into(), value.into())]);
404+
type StateOverrides = HashMap<Address, HashMap<B256, B256>>;
405+
let parse_state_overrides =
406+
|overrides: &Option<Vec<String>>| -> Result<StateOverrides, eyre::Report> {
407+
let mut state_overrides: StateOverrides = StateOverrides::default();
408+
409+
overrides.iter().flatten().try_for_each(|s| -> Result<(), eyre::Report> {
410+
let (addr, slot, value) = address_slot_value_override(s)?;
411+
state_overrides.entry(addr).or_default().insert(slot.into(), value.into());
412+
Ok(())
413+
})?;
414+
415+
Ok(state_overrides)
416+
};
417+
418+
// Parse and apply state overrides
419+
for (addr, entries) in parse_state_overrides(&self.state_overrides)? {
420+
state_overrides_builder = state_overrides_builder.with_state(addr, entries.into_iter());
410421
}
411422

412-
// Parse state diff overrides
413-
for override_str in self.state_diff_overrides.iter().flatten() {
414-
let (addr, slot, value) = address_slot_value_override(override_str)?;
423+
// Parse and apply state diff overrides
424+
for (addr, entries) in parse_state_overrides(&self.state_diff_overrides)? {
415425
state_overrides_builder =
416-
state_overrides_builder.with_state_diff(addr, [(slot.into(), value.into())]);
426+
state_overrides_builder.with_state_diff(addr, entries.into_iter())
417427
}
418428

419429
Ok(Some(state_overrides_builder.build()))

crates/cast/tests/cli/main.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3929,3 +3929,54 @@ casttest!(cast_access_list_negative_numbers, |_prj, cmd| {
39293929
])
39303930
.assert_success();
39313931
});
3932+
3933+
// tests that cast call properly applies multiple state diff overrides
3934+
// <https://github.com/foundry-rs/foundry/issues/11551>
3935+
casttest!(cast_call_can_override_several_state_diff, |_prj, cmd| {
3936+
let rpc = next_http_archive_rpc_url();
3937+
cmd.args([
3938+
"call",
3939+
"--trace",
3940+
"--from",
3941+
"0xf6F444fD3B0088c1375671c05A7513661beFa4e6",
3942+
"0x5EA1d9A6dDC3A0329378a327746D71A2019eC332",
3943+
"--rpc-url",
3944+
rpc.as_str(),
3945+
"--block",
3946+
"23290753",
3947+
"--data",
3948+
"0xe75235b8",
3949+
"--override-state-diff",
3950+
"0x5EA1d9A6dDC3A0329378a327746D71A2019eC332:0xf0af0268363540b847b4c07f2f9a0401c607c1b11ebca511724a71755dfd4137:1,0x5EA1d9A6dDC3A0329378a327746D71A2019eC332:4:1,0x5EA1d9A6dDC3A0329378a327746D71A2019eC332:0x4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c8:0,0x5EA1d9A6dDC3A0329378a327746D71A2019eC332:0xb104e0b93118902c651344349b610029d694cfdec91c589c91ebafbcd0289947:0",
3951+
])
3952+
.assert_success()
3953+
.stdout_eq(str![[r#"
3954+
...
3955+
[..] 0x5EA1d9A6dDC3A0329378a327746D71A2019eC332::getThreshold()
3956+
...
3957+
3958+
"#]]);
3959+
3960+
cmd.cast_fuse().args([
3961+
"call",
3962+
"--trace",
3963+
"--from",
3964+
"0x2066901073a33ba2500274704aB04763875cF210",
3965+
"0x5EA1d9A6dDC3A0329378a327746D71A2019eC332",
3966+
"--rpc-url",
3967+
rpc.as_str(),
3968+
"--block",
3969+
"23290753",
3970+
"--data",
3971+
"0x2f54bf6e0000000000000000000000002066901073a33ba2500274704ab04763875cf210",
3972+
"--override-state-diff",
3973+
"0x5EA1d9A6dDC3A0329378a327746D71A2019eC332:0xf0af0268363540b847b4c07f2f9a0401c607c1b11ebca511724a71755dfd4137:1,0x5EA1d9A6dDC3A0329378a327746D71A2019eC332:4:1,0x5EA1d9A6dDC3A0329378a327746D71A2019eC332:0x4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c8:0,0x5EA1d9A6dDC3A0329378a327746D71A2019eC332:0xb104e0b93118902c651344349b610029d694cfdec91c589c91ebafbcd0289947:0",
3974+
])
3975+
.assert_success()
3976+
.stdout_eq(str![[r#"
3977+
...
3978+
[..] 0x5EA1d9A6dDC3A0329378a327746D71A2019eC332::isOwner(0x2066901073a33ba2500274704aB04763875cF210)
3979+
...
3980+
3981+
"#]]);
3982+
});

0 commit comments

Comments
 (0)