Skip to content

Commit 55802ba

Browse files
tushar994grandizzy
andauthored
Add state overrides flags to cast call (#10255)
* add initial implementation * modify flag names * minor change: fix cargo clipy * fix docs * fix docs * minor fix * fix cargo fmt * Revert "fix cargo fmt" This reverts commit e771269. * add test * fix tests * use alloys Accountoverride * move get_state_overrides to self * minor fixes * remove parse_address_value_for_nonce and use a general function. use get_or_insert_default. remove clones(). * use regex for address slot value parsing * make all override flags options * Fmt and clippy * Use StateOverridesBuilder, nit * Fix docs * Add better tests for state, code and balance overrides --------- Co-authored-by: grandizzy <[email protected]> Co-authored-by: grandizzy <[email protected]>
1 parent 70ded2b commit 55802ba

File tree

3 files changed

+324
-20
lines changed

3 files changed

+324
-20
lines changed

crates/cast/src/cmd/call.rs

+175-14
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ use crate::{
33
tx::{CastTxBuilder, SenderKind},
44
Cast,
55
};
6-
use alloy_primitives::{TxKind, U256};
7-
use alloy_rpc_types::{BlockId, BlockNumberOrTag};
6+
use alloy_primitives::{Address, Bytes, TxKind, U256};
7+
use alloy_rpc_types::{
8+
state::{StateOverride, StateOverridesBuilder},
9+
BlockId, BlockNumberOrTag,
10+
};
811
use clap::Parser;
912
use eyre::Result;
1013
use foundry_cli::{
@@ -26,9 +29,35 @@ use foundry_evm::{
2629
opts::EvmOpts,
2730
traces::{InternalTraceMode, TraceMode},
2831
};
29-
use std::str::FromStr;
32+
use regex::Regex;
33+
use std::{str::FromStr, sync::LazyLock};
34+
35+
// matches override pattern <address>:<slot>:<value>
36+
// e.g. 0x123:0x1:0x1234
37+
static OVERRIDE_PATTERN: LazyLock<Regex> =
38+
LazyLock::new(|| Regex::new(r"^([^:]+):([^:]+):([^:]+)$").unwrap());
3039

3140
/// CLI arguments for `cast call`.
41+
///
42+
/// ## State Override Flags
43+
///
44+
/// The following flags can be used to override the state for the call:
45+
///
46+
/// * `--override-balance <address>:<balance>` - Override the balance of an account
47+
/// * `--override-nonce <address>:<nonce>` - Override the nonce of an account
48+
/// * `--override-code <address>:<code>` - Override the code of an account
49+
/// * `--override-state <address>:<slot>:<value>` - Override a storage slot of an account
50+
///
51+
/// Multiple overrides can be specified for the same account. For example:
52+
///
53+
/// ```bash
54+
/// cast call 0x... "transfer(address,uint256)" 0x... 100 \
55+
/// --override-balance 0x123:0x1234 \
56+
/// --override-nonce 0x123:1 \
57+
/// --override-code 0x123:0x1234 \
58+
/// --override-state 0x123:0x1:0x1234
59+
/// --override-state-diff 0x123:0x1:0x1234
60+
/// ```
3261
#[derive(Debug, Parser)]
3362
pub struct CallArgs {
3463
/// The destination of the transaction.
@@ -92,6 +121,31 @@ pub struct CallArgs {
92121
/// Use current project artifacts for trace decoding.
93122
#[arg(long, visible_alias = "la")]
94123
pub with_local_artifacts: bool,
124+
125+
/// Override the balance of an account.
126+
/// Format: address:balance
127+
#[arg(long = "override-balance", value_name = "ADDRESS:BALANCE")]
128+
pub balance_overrides: Option<Vec<String>>,
129+
130+
/// Override the nonce of an account.
131+
/// Format: address:nonce
132+
#[arg(long = "override-nonce", value_name = "ADDRESS:NONCE")]
133+
pub nonce_overrides: Option<Vec<String>>,
134+
135+
/// Override the code of an account.
136+
/// Format: address:code
137+
#[arg(long = "override-code", value_name = "ADDRESS:CODE")]
138+
pub code_overrides: Option<Vec<String>>,
139+
140+
/// Override the state of an account.
141+
/// Format: address:slot:value
142+
#[arg(long = "override-state", value_name = "ADDRESS:SLOT:VALUE")]
143+
pub state_overrides: Option<Vec<String>>,
144+
145+
/// Override the state diff of an account.
146+
/// Format: address:slot:value
147+
#[arg(long = "override-state-diff", value_name = "ADDRESS:SLOT:VALUE")]
148+
pub state_diff_overrides: Option<Vec<String>>,
95149
}
96150

97151
#[derive(Debug, Parser)]
@@ -123,6 +177,7 @@ impl CallArgs {
123177
let figment = Into::<Figment>::into(&self.eth).merge(&self);
124178
let evm_opts = figment.extract::<EvmOpts>()?;
125179
let mut config = Config::from_provider(figment)?.sanitized();
180+
let state_overrides = self.get_state_overrides()?;
126181

127182
let Self {
128183
to,
@@ -236,10 +291,54 @@ impl CallArgs {
236291
return Ok(());
237292
}
238293

239-
sh_println!("{}", Cast::new(provider).call(&tx, func.as_ref(), block).await?)?;
294+
sh_println!(
295+
"{}",
296+
Cast::new(provider).call(&tx, func.as_ref(), block, state_overrides).await?
297+
)?;
240298

241299
Ok(())
242300
}
301+
/// Parse state overrides from command line arguments
302+
pub fn get_state_overrides(&self) -> eyre::Result<StateOverride> {
303+
let mut state_overrides_builder = StateOverridesBuilder::default();
304+
305+
// Parse balance overrides
306+
for override_str in self.balance_overrides.iter().flatten() {
307+
let (addr, balance) = address_value_override(override_str)?;
308+
state_overrides_builder =
309+
state_overrides_builder.with_balance(addr.parse()?, balance.parse()?);
310+
}
311+
312+
// Parse nonce overrides
313+
for override_str in self.nonce_overrides.iter().flatten() {
314+
let (addr, nonce) = address_value_override(override_str)?;
315+
state_overrides_builder =
316+
state_overrides_builder.with_nonce(addr.parse()?, nonce.parse()?);
317+
}
318+
319+
// Parse code overrides
320+
for override_str in self.code_overrides.iter().flatten() {
321+
let (addr, code_str) = address_value_override(override_str)?;
322+
state_overrides_builder =
323+
state_overrides_builder.with_code(addr.parse()?, Bytes::from_str(code_str)?);
324+
}
325+
326+
// Parse state overrides
327+
for override_str in self.state_overrides.iter().flatten() {
328+
let (addr, slot, value) = address_slot_value_override(override_str)?;
329+
state_overrides_builder =
330+
state_overrides_builder.with_state(addr, [(slot.into(), value.into())]);
331+
}
332+
333+
// Parse state diff overrides
334+
for override_str in self.state_diff_overrides.iter().flatten() {
335+
let (addr, slot, value) = address_slot_value_override(override_str)?;
336+
state_overrides_builder =
337+
state_overrides_builder.with_state_diff(addr, [(slot.into(), value.into())]);
338+
}
339+
340+
Ok(state_overrides_builder.build())
341+
}
243342
}
244343

245344
impl figment::Provider for CallArgs {
@@ -262,10 +361,30 @@ impl figment::Provider for CallArgs {
262361
}
263362
}
264363

364+
/// Parse an override string in the format address:value.
365+
fn address_value_override(address_override: &str) -> Result<(&str, &str)> {
366+
address_override.split_once(':').ok_or_else(|| {
367+
eyre::eyre!("Invalid override {address_override}. Expected <address>:<value>")
368+
})
369+
}
370+
371+
/// Parse an override string in the format address:slot:value.
372+
fn address_slot_value_override(address_override: &str) -> Result<(Address, U256, U256)> {
373+
let captures = OVERRIDE_PATTERN.captures(address_override).ok_or_else(|| {
374+
eyre::eyre!("Invalid override {address_override}. Expected <address>:<slot>:<value>")
375+
})?;
376+
377+
Ok((
378+
captures[1].parse()?, // Address
379+
captures[2].parse()?, // Slot (U256)
380+
captures[3].parse()?, // Value (U256)
381+
))
382+
}
383+
265384
#[cfg(test)]
266385
mod tests {
267386
use super::*;
268-
use alloy_primitives::{hex, Address};
387+
use alloy_primitives::hex;
269388

270389
#[test]
271390
fn can_parse_call_data() {
@@ -279,17 +398,59 @@ mod tests {
279398
}
280399

281400
#[test]
282-
fn call_sig_and_data_exclusive() {
283-
let data = hex::encode("hello");
284-
let to = Address::ZERO;
285-
let args = CallArgs::try_parse_from([
401+
fn can_parse_state_overrides() {
402+
let args = CallArgs::parse_from([
403+
"foundry-cli",
404+
"--override-balance",
405+
"0x123:0x1234",
406+
"--override-nonce",
407+
"0x123:1",
408+
"--override-code",
409+
"0x123:0x1234",
410+
"--override-state",
411+
"0x123:0x1:0x1234",
412+
]);
413+
414+
assert_eq!(args.balance_overrides, Some(vec!["0x123:0x1234".to_string()]));
415+
assert_eq!(args.nonce_overrides, Some(vec!["0x123:1".to_string()]));
416+
assert_eq!(args.code_overrides, Some(vec!["0x123:0x1234".to_string()]));
417+
assert_eq!(args.state_overrides, Some(vec!["0x123:0x1:0x1234".to_string()]));
418+
}
419+
420+
#[test]
421+
fn can_parse_multiple_state_overrides() {
422+
let args = CallArgs::parse_from([
286423
"foundry-cli",
287-
to.to_string().as_str(),
288-
"signature",
289-
"--data",
290-
format!("0x{data}").as_str(),
424+
"--override-balance",
425+
"0x123:0x1234",
426+
"--override-balance",
427+
"0x456:0x5678",
428+
"--override-nonce",
429+
"0x123:1",
430+
"--override-nonce",
431+
"0x456:2",
432+
"--override-code",
433+
"0x123:0x1234",
434+
"--override-code",
435+
"0x456:0x5678",
436+
"--override-state",
437+
"0x123:0x1:0x1234",
438+
"--override-state",
439+
"0x456:0x2:0x5678",
291440
]);
292441

293-
assert!(args.is_err());
442+
assert_eq!(
443+
args.balance_overrides,
444+
Some(vec!["0x123:0x1234".to_string(), "0x456:0x5678".to_string()])
445+
);
446+
assert_eq!(args.nonce_overrides, Some(vec!["0x123:1".to_string(), "0x456:2".to_string()]));
447+
assert_eq!(
448+
args.code_overrides,
449+
Some(vec!["0x123:0x1234".to_string(), "0x456:0x5678".to_string()])
450+
);
451+
assert_eq!(
452+
args.state_overrides,
453+
Some(vec!["0x123:0x1:0x1234".to_string(), "0x456:0x2:0x5678".to_string()])
454+
);
294455
}
295456
}

crates/cast/src/lib.rs

+23-6
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ use alloy_provider::{
1616
PendingTransactionBuilder, Provider,
1717
};
1818
use alloy_rlp::Decodable;
19-
use alloy_rpc_types::{BlockId, BlockNumberOrTag, Filter, TransactionRequest};
19+
use alloy_rpc_types::{
20+
state::StateOverride, BlockId, BlockNumberOrTag, Filter, TransactionRequest,
21+
};
2022
use alloy_serde::WithOtherFields;
2123
use alloy_sol_types::sol;
2224
use base::{Base, NumberWithBase, ToBase};
@@ -107,26 +109,35 @@ impl<P: Provider<AnyNetwork>> Cast<P> {
107109
///
108110
/// ```
109111
/// use alloy_primitives::{Address, U256, Bytes};
110-
/// use alloy_rpc_types::{TransactionRequest};
112+
/// use alloy_rpc_types::{TransactionRequest, state::{StateOverride, AccountOverride}};
111113
/// use alloy_serde::WithOtherFields;
112114
/// use cast::Cast;
113115
/// use alloy_provider::{RootProvider, ProviderBuilder, network::AnyNetwork};
114-
/// use std::str::FromStr;
116+
/// use std::{str::FromStr, collections::HashMap};
117+
/// use alloy_rpc_types::state::StateOverridesBuilder;
115118
/// use alloy_sol_types::{sol, SolCall};
116119
///
117120
/// sol!(
118121
/// function greeting(uint256 i) public returns (string);
119122
/// );
120123
///
121124
/// # async fn foo() -> eyre::Result<()> {
122-
/// let alloy_provider = ProviderBuilder::<_,_, AnyNetwork>::default().on_builtin("http://localhost:8545").await?;;
125+
/// let alloy_provider = ProviderBuilder::<_,_, AnyNetwork>::default().connect("http://localhost:8545").await?;;
123126
/// let to = Address::from_str("0xB3C95ff08316fb2F2e3E52Ee82F8e7b605Aa1304")?;
124127
/// let greeting = greetingCall { i: U256::from(5) }.abi_encode();
125128
/// let bytes = Bytes::from_iter(greeting.iter());
126129
/// let tx = TransactionRequest::default().to(to).input(bytes.into());
127130
/// let tx = WithOtherFields::new(tx);
131+
///
132+
/// // Create state overrides
133+
/// let mut state_override = StateOverride::default();
134+
/// let mut account_override = AccountOverride::default();
135+
/// account_override.balance = Some(U256::from(1000));
136+
/// state_override.insert(to, account_override);
137+
/// let state_override_object = StateOverridesBuilder::default().build();
138+
///
128139
/// let cast = Cast::new(alloy_provider);
129-
/// let data = cast.call(&tx, None, None).await?;
140+
/// let data = cast.call(&tx, None, None, state_override_object).await?;
130141
/// println!("{}", data);
131142
/// # Ok(())
132143
/// # }
@@ -136,8 +147,14 @@ impl<P: Provider<AnyNetwork>> Cast<P> {
136147
req: &WithOtherFields<TransactionRequest>,
137148
func: Option<&Function>,
138149
block: Option<BlockId>,
150+
state_override: StateOverride,
139151
) -> Result<String> {
140-
let res = self.provider.call(req.clone()).block(block.unwrap_or_default()).await?;
152+
let res = self
153+
.provider
154+
.call(req.clone())
155+
.block(block.unwrap_or_default())
156+
.overrides(state_override)
157+
.await?;
141158

142159
let mut decoded = vec![];
143160

0 commit comments

Comments
 (0)