cast/cmd/
call.rs

1use super::run::fetch_contracts_bytecode_from_trace;
2use crate::{
3    Cast,
4    traces::TraceKind,
5    tx::{CastTxBuilder, SenderKind},
6};
7use alloy_ens::NameOrAddress;
8use alloy_primitives::{Address, B256, Bytes, TxKind, U256, map::HashMap};
9use alloy_provider::Provider;
10use alloy_rpc_types::{
11    BlockId, BlockNumberOrTag, BlockOverrides,
12    state::{StateOverride, StateOverridesBuilder},
13};
14use clap::Parser;
15use eyre::Result;
16use foundry_cli::{
17    opts::{EthereumOpts, TransactionOpts},
18    utils::{self, TraceResult, handle_traces, parse_ether_value},
19};
20use foundry_common::shell;
21use foundry_compilers::artifacts::EvmVersion;
22use foundry_config::{
23    Config,
24    figment::{
25        self, Metadata, Profile,
26        value::{Dict, Map},
27    },
28};
29use foundry_evm::{
30    executors::TracingExecutor,
31    opts::EvmOpts,
32    traces::{InternalTraceMode, TraceMode},
33};
34use itertools::Either;
35use regex::Regex;
36use revm::context::TransactionType;
37use std::{str::FromStr, sync::LazyLock};
38
39// matches override pattern <address>:<slot>:<value>
40// e.g. 0x123:0x1:0x1234
41static OVERRIDE_PATTERN: LazyLock<Regex> =
42    LazyLock::new(|| Regex::new(r"^([^:]+):([^:]+):([^:]+)$").unwrap());
43
44/// CLI arguments for `cast call`.
45///
46/// ## State Override Flags
47///
48/// The following flags can be used to override the state for the call:
49///
50/// * `--override-balance <address>:<balance>` - Override the balance of an account
51/// * `--override-nonce <address>:<nonce>` - Override the nonce of an account
52/// * `--override-code <address>:<code>` - Override the code of an account
53/// * `--override-state <address>:<slot>:<value>` - Override a storage slot of an account
54///
55/// Multiple overrides can be specified for the same account. For example:
56///
57/// ```bash
58/// cast call 0x... "transfer(address,uint256)" 0x... 100 \
59///   --override-balance 0x123:0x1234 \
60///   --override-nonce 0x123:1 \
61///   --override-code 0x123:0x1234 \
62///   --override-state 0x123:0x1:0x1234
63///   --override-state-diff 0x123:0x1:0x1234
64/// ```
65#[derive(Debug, Parser)]
66pub struct CallArgs {
67    /// The destination of the transaction.
68    #[arg(value_parser = NameOrAddress::from_str)]
69    to: Option<NameOrAddress>,
70
71    /// The signature of the function to call.
72    sig: Option<String>,
73
74    /// The arguments of the function to call.
75    #[arg(allow_negative_numbers = true)]
76    args: Vec<String>,
77
78    /// Raw hex-encoded data for the transaction. Used instead of \[SIG\] and \[ARGS\].
79    #[arg(
80        long,
81        conflicts_with_all = &["sig", "args"]
82    )]
83    data: Option<String>,
84
85    /// Forks the remote rpc, executes the transaction locally and prints a trace
86    #[arg(long, default_value_t = false)]
87    trace: bool,
88
89    /// Disables the labels in the traces.
90    /// Can only be set with `--trace`.
91    #[arg(long, default_value_t = false, requires = "trace")]
92    disable_labels: bool,
93
94    /// Opens an interactive debugger.
95    /// Can only be used with `--trace`.
96    #[arg(long, requires = "trace")]
97    debug: bool,
98
99    #[arg(long, requires = "trace")]
100    decode_internal: bool,
101
102    /// Labels to apply to the traces; format: `address:label`.
103    /// Can only be used with `--trace`.
104    #[arg(long, requires = "trace")]
105    labels: Vec<String>,
106
107    /// The EVM Version to use.
108    /// Can only be used with `--trace`.
109    #[arg(long, requires = "trace")]
110    evm_version: Option<EvmVersion>,
111
112    /// The block height to query at.
113    ///
114    /// Can also be the tags earliest, finalized, safe, latest, or pending.
115    #[arg(long, short)]
116    block: Option<BlockId>,
117
118    #[command(subcommand)]
119    command: Option<CallSubcommands>,
120
121    #[command(flatten)]
122    tx: TransactionOpts,
123
124    #[command(flatten)]
125    eth: EthereumOpts,
126
127    /// Use current project artifacts for trace decoding.
128    #[arg(long, visible_alias = "la")]
129    pub with_local_artifacts: bool,
130
131    /// Override the accounts balance.
132    /// Format: "address:balance,address:balance"
133    #[arg(long = "override-balance", value_name = "ADDRESS:BALANCE", value_delimiter = ',')]
134    pub balance_overrides: Option<Vec<String>>,
135
136    /// Override the accounts nonce.
137    /// Format: "address:nonce,address:nonce"
138    #[arg(long = "override-nonce", value_name = "ADDRESS:NONCE", value_delimiter = ',')]
139    pub nonce_overrides: Option<Vec<String>>,
140
141    /// Override the accounts code.
142    /// Format: "address:code,address:code"
143    #[arg(long = "override-code", value_name = "ADDRESS:CODE", value_delimiter = ',')]
144    pub code_overrides: Option<Vec<String>>,
145
146    /// Override the accounts state and replace the current state entirely with the new one.
147    /// Format: "address:slot:value,address:slot:value"
148    #[arg(long = "override-state", value_name = "ADDRESS:SLOT:VALUE", value_delimiter = ',')]
149    pub state_overrides: Option<Vec<String>>,
150
151    /// Override the accounts state specific slots and preserve the rest of the state.
152    /// Format: "address:slot:value,address:slot:value"
153    #[arg(long = "override-state-diff", value_name = "ADDRESS:SLOT:VALUE", value_delimiter = ',')]
154    pub state_diff_overrides: Option<Vec<String>>,
155
156    /// Override the block timestamp.
157    #[arg(long = "block.time", value_name = "TIME")]
158    pub block_time: Option<u64>,
159
160    /// Override the block number.
161    #[arg(long = "block.number", value_name = "NUMBER")]
162    pub block_number: Option<u64>,
163}
164
165#[derive(Debug, Parser)]
166pub enum CallSubcommands {
167    /// ignores the address field and simulates creating a contract
168    #[command(name = "--create")]
169    Create {
170        /// Bytecode of contract.
171        code: String,
172
173        /// The signature of the constructor.
174        sig: Option<String>,
175
176        /// The arguments of the constructor.
177        #[arg(allow_negative_numbers = true)]
178        args: Vec<String>,
179
180        /// Ether to send in the transaction.
181        ///
182        /// Either specified in wei, or as a string with a unit type.
183        ///
184        /// Examples: 1ether, 10gwei, 0.01ether
185        #[arg(long, value_parser = parse_ether_value)]
186        value: Option<U256>,
187    },
188}
189
190impl CallArgs {
191    pub async fn run(self) -> Result<()> {
192        let figment = self.eth.rpc.clone().into_figment(self.with_local_artifacts).merge(&self);
193        let evm_opts = figment.extract::<EvmOpts>()?;
194        let mut config = Config::from_provider(figment)?.sanitized();
195        let state_overrides = self.get_state_overrides()?;
196        let block_overrides = self.get_block_overrides()?;
197
198        let Self {
199            to,
200            mut sig,
201            mut args,
202            mut tx,
203            eth,
204            command,
205            block,
206            trace,
207            evm_version,
208            debug,
209            decode_internal,
210            labels,
211            data,
212            with_local_artifacts,
213            disable_labels,
214            ..
215        } = self;
216
217        if let Some(data) = data {
218            sig = Some(data);
219        }
220
221        let provider = utils::get_provider(&config)?;
222        let sender = SenderKind::from_wallet_opts(eth.wallet).await?;
223        let from = sender.address();
224
225        let code = if let Some(CallSubcommands::Create {
226            code,
227            sig: create_sig,
228            args: create_args,
229            value,
230        }) = command
231        {
232            sig = create_sig;
233            args = create_args;
234            if let Some(value) = value {
235                tx.value = Some(value);
236            }
237            Some(code)
238        } else {
239            None
240        };
241
242        let (tx, func) = CastTxBuilder::new(&provider, tx, &config)
243            .await?
244            .with_to(to)
245            .await?
246            .with_code_sig_and_args(code, sig, args)
247            .await?
248            .build_raw(sender)
249            .await?;
250
251        if trace {
252            if let Some(BlockId::Number(BlockNumberOrTag::Number(block_number))) = self.block {
253                // Override Config `fork_block_number` (if set) with CLI value.
254                config.fork_block_number = Some(block_number);
255            }
256
257            let create2_deployer = evm_opts.create2_deployer;
258            let (mut env, fork, chain, networks) =
259                TracingExecutor::get_fork_material(&config, evm_opts).await?;
260
261            // modify settings that usually set in eth_call
262            env.evm_env.cfg_env.disable_block_gas_limit = true;
263            env.evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX);
264            env.evm_env.block_env.gas_limit = u64::MAX;
265
266            // Apply the block overrides.
267            if let Some(block_overrides) = block_overrides {
268                if let Some(number) = block_overrides.number {
269                    env.evm_env.block_env.number = number.to();
270                }
271                if let Some(time) = block_overrides.time {
272                    env.evm_env.block_env.timestamp = U256::from(time);
273                }
274            }
275
276            let trace_mode = TraceMode::Call
277                .with_debug(debug)
278                .with_decode_internal(if decode_internal {
279                    InternalTraceMode::Full
280                } else {
281                    InternalTraceMode::None
282                })
283                .with_state_changes(shell::verbosity() > 4);
284            let mut executor = TracingExecutor::new(
285                env,
286                fork,
287                evm_version,
288                trace_mode,
289                networks,
290                create2_deployer,
291                state_overrides,
292            )?;
293
294            let value = tx.value.unwrap_or_default();
295            let input = tx.inner.input.into_input().unwrap_or_default();
296            let tx_kind = tx.inner.to.expect("set by builder");
297            let env_tx = &mut executor.env_mut().tx;
298
299            if let Some(tx_type) = tx.inner.transaction_type {
300                env_tx.tx_type = tx_type;
301            }
302
303            if let Some(access_list) = tx.inner.access_list {
304                env_tx.access_list = access_list;
305
306                if env_tx.tx_type == TransactionType::Legacy as u8 {
307                    env_tx.tx_type = TransactionType::Eip2930 as u8;
308                }
309            }
310
311            if let Some(auth) = tx.inner.authorization_list {
312                env_tx.authorization_list = auth.into_iter().map(Either::Left).collect();
313
314                env_tx.tx_type = TransactionType::Eip7702 as u8;
315            }
316
317            let trace = match tx_kind {
318                TxKind::Create => {
319                    let deploy_result = executor.deploy(from, input, value, None);
320                    TraceResult::try_from(deploy_result)?
321                }
322                TxKind::Call(to) => TraceResult::from_raw(
323                    executor.transact_raw(from, to, input, value)?,
324                    TraceKind::Execution,
325                ),
326            };
327
328            let contracts_bytecode = fetch_contracts_bytecode_from_trace(&provider, &trace).await?;
329            handle_traces(
330                trace,
331                &config,
332                chain,
333                &contracts_bytecode,
334                labels,
335                with_local_artifacts,
336                debug,
337                decode_internal,
338                disable_labels,
339            )
340            .await?;
341
342            return Ok(());
343        }
344
345        let response = Cast::new(&provider)
346            .call(&tx, func.as_ref(), block, state_overrides, block_overrides)
347            .await?;
348
349        if response == "0x"
350            && let Some(contract_address) = tx.to.and_then(|tx_kind| tx_kind.into_to())
351        {
352            let code = provider.get_code_at(contract_address).await?;
353            if code.is_empty() {
354                sh_warn!("Contract code is empty")?;
355            }
356        }
357        sh_println!("{}", response)?;
358
359        Ok(())
360    }
361
362    /// Parse state overrides from command line arguments.
363    pub fn get_state_overrides(&self) -> eyre::Result<Option<StateOverride>> {
364        // Early return if no override set - <https://github.com/foundry-rs/foundry/issues/10705>
365        if [
366            self.balance_overrides.as_ref(),
367            self.nonce_overrides.as_ref(),
368            self.code_overrides.as_ref(),
369            self.state_overrides.as_ref(),
370            self.state_diff_overrides.as_ref(),
371        ]
372        .iter()
373        .all(Option::is_none)
374        {
375            return Ok(None);
376        }
377
378        let mut state_overrides_builder = StateOverridesBuilder::default();
379
380        // Parse balance overrides
381        for override_str in self.balance_overrides.iter().flatten() {
382            let (addr, balance) = address_value_override(override_str)?;
383            state_overrides_builder =
384                state_overrides_builder.with_balance(addr.parse()?, balance.parse()?);
385        }
386
387        // Parse nonce overrides
388        for override_str in self.nonce_overrides.iter().flatten() {
389            let (addr, nonce) = address_value_override(override_str)?;
390            state_overrides_builder =
391                state_overrides_builder.with_nonce(addr.parse()?, nonce.parse()?);
392        }
393
394        // Parse code overrides
395        for override_str in self.code_overrides.iter().flatten() {
396            let (addr, code_str) = address_value_override(override_str)?;
397            state_overrides_builder =
398                state_overrides_builder.with_code(addr.parse()?, Bytes::from_str(code_str)?);
399        }
400
401        type StateOverrides = HashMap<Address, HashMap<B256, B256>>;
402        let parse_state_overrides =
403            |overrides: &Option<Vec<String>>| -> Result<StateOverrides, eyre::Report> {
404                let mut state_overrides: StateOverrides = StateOverrides::default();
405
406                overrides.iter().flatten().try_for_each(|s| -> Result<(), eyre::Report> {
407                    let (addr, slot, value) = address_slot_value_override(s)?;
408                    state_overrides.entry(addr).or_default().insert(slot.into(), value.into());
409                    Ok(())
410                })?;
411
412                Ok(state_overrides)
413            };
414
415        // Parse and apply state overrides
416        for (addr, entries) in parse_state_overrides(&self.state_overrides)? {
417            state_overrides_builder = state_overrides_builder.with_state(addr, entries.into_iter());
418        }
419
420        // Parse and apply state diff overrides
421        for (addr, entries) in parse_state_overrides(&self.state_diff_overrides)? {
422            state_overrides_builder =
423                state_overrides_builder.with_state_diff(addr, entries.into_iter())
424        }
425
426        Ok(Some(state_overrides_builder.build()))
427    }
428
429    /// Parse block overrides from command line arguments.
430    pub fn get_block_overrides(&self) -> eyre::Result<Option<BlockOverrides>> {
431        let mut overrides = BlockOverrides::default();
432        if let Some(number) = self.block_number {
433            overrides = overrides.with_number(U256::from(number));
434        }
435        if let Some(time) = self.block_time {
436            overrides = overrides.with_time(time);
437        }
438        if overrides.is_empty() { Ok(None) } else { Ok(Some(overrides)) }
439    }
440}
441
442impl figment::Provider for CallArgs {
443    fn metadata(&self) -> Metadata {
444        Metadata::named("CallArgs")
445    }
446
447    fn data(&self) -> Result<Map<Profile, Dict>, figment::Error> {
448        let mut map = Map::new();
449
450        if let Some(evm_version) = self.evm_version {
451            map.insert("evm_version".into(), figment::value::Value::serialize(evm_version)?);
452        }
453
454        Ok(Map::from([(Config::selected_profile(), map)]))
455    }
456}
457
458/// Parse an override string in the format address:value.
459fn address_value_override(address_override: &str) -> Result<(&str, &str)> {
460    address_override.split_once(':').ok_or_else(|| {
461        eyre::eyre!("Invalid override {address_override}. Expected <address>:<value>")
462    })
463}
464
465/// Parse an override string in the format address:slot:value.
466fn address_slot_value_override(address_override: &str) -> Result<(Address, U256, U256)> {
467    let captures = OVERRIDE_PATTERN.captures(address_override).ok_or_else(|| {
468        eyre::eyre!("Invalid override {address_override}. Expected <address>:<slot>:<value>")
469    })?;
470
471    Ok((
472        captures[1].parse()?, // Address
473        captures[2].parse()?, // Slot (U256)
474        captures[3].parse()?, // Value (U256)
475    ))
476}
477
478#[cfg(test)]
479mod tests {
480    use super::*;
481    use alloy_primitives::{address, b256, fixed_bytes, hex};
482
483    #[test]
484    fn test_get_state_overrides() {
485        let call_args = CallArgs::parse_from([
486            "foundry-cli",
487            "--override-balance",
488            "0x0000000000000000000000000000000000000001:2",
489            "--override-nonce",
490            "0x0000000000000000000000000000000000000001:3",
491            "--override-code",
492            "0x0000000000000000000000000000000000000001:0x04",
493            "--override-state",
494            "0x0000000000000000000000000000000000000001:5:6",
495            "--override-state-diff",
496            "0x0000000000000000000000000000000000000001:7:8",
497        ]);
498        let overrides = call_args.get_state_overrides().unwrap().unwrap();
499        let address = address!("0x0000000000000000000000000000000000000001");
500        if let Some(account_override) = overrides.get(&address) {
501            if let Some(balance) = account_override.balance {
502                assert_eq!(balance, U256::from(2));
503            }
504            if let Some(nonce) = account_override.nonce {
505                assert_eq!(nonce, 3);
506            }
507            if let Some(code) = &account_override.code {
508                assert_eq!(*code, Bytes::from([0x04]));
509            }
510            if let Some(state) = &account_override.state
511                && let Some(value) = state.get(&b256!(
512                    "0x0000000000000000000000000000000000000000000000000000000000000005"
513                ))
514            {
515                assert_eq!(
516                    *value,
517                    b256!("0x0000000000000000000000000000000000000000000000000000000000000006")
518                );
519            }
520            if let Some(state_diff) = &account_override.state_diff
521                && let Some(value) = state_diff.get(&b256!(
522                    "0x0000000000000000000000000000000000000000000000000000000000000007"
523                ))
524            {
525                assert_eq!(
526                    *value,
527                    b256!("0x0000000000000000000000000000000000000000000000000000000000000008")
528                );
529            }
530        }
531    }
532
533    #[test]
534    fn test_get_state_overrides_empty() {
535        let call_args = CallArgs::parse_from([""]);
536        let overrides = call_args.get_state_overrides().unwrap();
537        assert_eq!(overrides, None);
538    }
539
540    #[test]
541    fn test_get_block_overrides() {
542        let mut call_args = CallArgs::parse_from([""]);
543        call_args.block_number = Some(1);
544        call_args.block_time = Some(2);
545        let overrides = call_args.get_block_overrides().unwrap().unwrap();
546        assert_eq!(overrides.number, Some(U256::from(1)));
547        assert_eq!(overrides.time, Some(2));
548    }
549
550    #[test]
551    fn test_get_block_overrides_empty() {
552        let call_args = CallArgs::parse_from([""]);
553        let overrides = call_args.get_block_overrides().unwrap();
554        assert_eq!(overrides, None);
555    }
556
557    #[test]
558    fn test_address_value_override_success() {
559        let text = "0x0000000000000000000000000000000000000001:2";
560        let (address, value) = address_value_override(text).unwrap();
561        assert_eq!(address, "0x0000000000000000000000000000000000000001");
562        assert_eq!(value, "2");
563    }
564
565    #[test]
566    fn test_address_value_override_error() {
567        let text = "invalid_value";
568        let error = address_value_override(text).unwrap_err();
569        assert_eq!(error.to_string(), "Invalid override invalid_value. Expected <address>:<value>");
570    }
571
572    #[test]
573    fn test_address_slot_value_override_success() {
574        let text = "0x0000000000000000000000000000000000000001:2:3";
575        let (address, slot, value) = address_slot_value_override(text).unwrap();
576        assert_eq!(*address, fixed_bytes!("0x0000000000000000000000000000000000000001"));
577        assert_eq!(slot, U256::from(2));
578        assert_eq!(value, U256::from(3));
579    }
580
581    #[test]
582    fn test_address_slot_value_override_error() {
583        let text = "invalid_value";
584        let error = address_slot_value_override(text).unwrap_err();
585        assert_eq!(
586            error.to_string(),
587            "Invalid override invalid_value. Expected <address>:<slot>:<value>"
588        );
589    }
590
591    #[test]
592    fn can_parse_call_data() {
593        let data = hex::encode("hello");
594        let args = CallArgs::parse_from(["foundry-cli", "--data", data.as_str()]);
595        assert_eq!(args.data, Some(data));
596
597        let data = hex::encode_prefixed("hello");
598        let args = CallArgs::parse_from(["foundry-cli", "--data", data.as_str()]);
599        assert_eq!(args.data, Some(data));
600    }
601
602    #[test]
603    fn can_parse_state_overrides() {
604        let args = CallArgs::parse_from([
605            "foundry-cli",
606            "--override-balance",
607            "0x123:0x1234",
608            "--override-nonce",
609            "0x123:1",
610            "--override-code",
611            "0x123:0x1234",
612            "--override-state",
613            "0x123:0x1:0x1234",
614        ]);
615
616        assert_eq!(args.balance_overrides, Some(vec!["0x123:0x1234".to_string()]));
617        assert_eq!(args.nonce_overrides, Some(vec!["0x123:1".to_string()]));
618        assert_eq!(args.code_overrides, Some(vec!["0x123:0x1234".to_string()]));
619        assert_eq!(args.state_overrides, Some(vec!["0x123:0x1:0x1234".to_string()]));
620    }
621
622    #[test]
623    fn can_parse_multiple_state_overrides() {
624        let args = CallArgs::parse_from([
625            "foundry-cli",
626            "--override-balance",
627            "0x123:0x1234",
628            "--override-balance",
629            "0x456:0x5678",
630            "--override-nonce",
631            "0x123:1",
632            "--override-nonce",
633            "0x456:2",
634            "--override-code",
635            "0x123:0x1234",
636            "--override-code",
637            "0x456:0x5678",
638            "--override-state",
639            "0x123:0x1:0x1234",
640            "--override-state",
641            "0x456:0x2:0x5678",
642        ]);
643
644        assert_eq!(
645            args.balance_overrides,
646            Some(vec!["0x123:0x1234".to_string(), "0x456:0x5678".to_string()])
647        );
648        assert_eq!(args.nonce_overrides, Some(vec!["0x123:1".to_string(), "0x456:2".to_string()]));
649        assert_eq!(
650            args.code_overrides,
651            Some(vec!["0x123:0x1234".to_string(), "0x456:0x5678".to_string()])
652        );
653        assert_eq!(
654            args.state_overrides,
655            Some(vec!["0x123:0x1:0x1234".to_string(), "0x456:0x2:0x5678".to_string()])
656        );
657    }
658
659    #[test]
660    fn test_negative_args_with_flags() {
661        // Test that negative args work with flags
662        let args = CallArgs::parse_from([
663            "foundry-cli",
664            "--trace",
665            "0xDeaDBeeFcAfEbAbEfAcEfEeDcBaDbEeFcAfEbAbE",
666            "process(int256)",
667            "-999999",
668            "--debug",
669        ]);
670
671        assert!(args.trace);
672        assert!(args.debug);
673        assert_eq!(args.args, vec!["-999999"]);
674    }
675}