@@ -3,8 +3,11 @@ use crate::{
3
3
tx:: { CastTxBuilder , SenderKind } ,
4
4
Cast ,
5
5
} ;
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
+ } ;
8
11
use clap:: Parser ;
9
12
use eyre:: Result ;
10
13
use foundry_cli:: {
@@ -26,9 +29,35 @@ use foundry_evm::{
26
29
opts:: EvmOpts ,
27
30
traces:: { InternalTraceMode , TraceMode } ,
28
31
} ;
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 ( ) ) ;
30
39
31
40
/// 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
+ /// ```
32
61
#[ derive( Debug , Parser ) ]
33
62
pub struct CallArgs {
34
63
/// The destination of the transaction.
@@ -92,6 +121,31 @@ pub struct CallArgs {
92
121
/// Use current project artifacts for trace decoding.
93
122
#[ arg( long, visible_alias = "la" ) ]
94
123
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 > > ,
95
149
}
96
150
97
151
#[ derive( Debug , Parser ) ]
@@ -123,6 +177,7 @@ impl CallArgs {
123
177
let figment = Into :: < Figment > :: into ( & self . eth ) . merge ( & self ) ;
124
178
let evm_opts = figment. extract :: < EvmOpts > ( ) ?;
125
179
let mut config = Config :: from_provider ( figment) ?. sanitized ( ) ;
180
+ let state_overrides = self . get_state_overrides ( ) ?;
126
181
127
182
let Self {
128
183
to,
@@ -236,10 +291,54 @@ impl CallArgs {
236
291
return Ok ( ( ) ) ;
237
292
}
238
293
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
+ ) ?;
240
298
241
299
Ok ( ( ) )
242
300
}
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
+ }
243
342
}
244
343
245
344
impl figment:: Provider for CallArgs {
@@ -262,10 +361,30 @@ impl figment::Provider for CallArgs {
262
361
}
263
362
}
264
363
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
+
265
384
#[ cfg( test) ]
266
385
mod tests {
267
386
use super :: * ;
268
- use alloy_primitives:: { hex, Address } ;
387
+ use alloy_primitives:: hex;
269
388
270
389
#[ test]
271
390
fn can_parse_call_data ( ) {
@@ -279,17 +398,59 @@ mod tests {
279
398
}
280
399
281
400
#[ 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 ( [
286
423
"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" ,
291
440
] ) ;
292
441
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
+ ) ;
294
455
}
295
456
}
0 commit comments