Skip to main content

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 regex::Regex;
35use revm::context::TransactionType;
36use std::{str::FromStr, sync::LazyLock};
37
38// matches override pattern <address>:<slot>:<value>
39// e.g. 0x123:0x1:0x1234
40static OVERRIDE_PATTERN: LazyLock<Regex> =
41    LazyLock::new(|| Regex::new(r"^([^:]+):([^:]+):([^:]+)$").unwrap());
42
43/// CLI arguments for `cast call`.
44///
45/// ## State Override Flags
46///
47/// The following flags can be used to override the state for the call:
48///
49/// * `--override-balance <address>:<balance>` - Override the balance of an account
50/// * `--override-nonce <address>:<nonce>` - Override the nonce of an account
51/// * `--override-code <address>:<code>` - Override the code of an account
52/// * `--override-state <address>:<slot>:<value>` - Override a storage slot of an account
53///
54/// Multiple overrides can be specified for the same account. For example:
55///
56/// ```bash
57/// cast call 0x... "transfer(address,uint256)" 0x... 100 \
58///   --override-balance 0x123:0x1234 \
59///   --override-nonce 0x123:1 \
60///   --override-code 0x123:0x1234 \
61///   --override-state 0x123:0x1:0x1234
62///   --override-state-diff 0x123:0x1:0x1234
63/// ```
64#[derive(Debug, Parser)]
65pub struct CallArgs {
66    /// The destination of the transaction.
67    #[arg(value_parser = NameOrAddress::from_str)]
68    to: Option<NameOrAddress>,
69
70    /// The signature of the function to call.
71    sig: Option<String>,
72
73    /// The arguments of the function to call.
74    args: Vec<String>,
75
76    /// Raw hex-encoded data for the transaction. Used instead of \[SIG\] and \[ARGS\].
77    #[arg(
78        long,
79        conflicts_with_all = &["sig", "args"]
80    )]
81    data: Option<String>,
82
83    /// Forks the remote rpc, executes the transaction locally and prints a trace
84    #[arg(long, default_value_t = false)]
85    trace: bool,
86
87    /// Disables the labels in the traces.
88    /// Can only be set with `--trace`.
89    #[arg(long, default_value_t = false, requires = "trace")]
90    disable_labels: bool,
91
92    /// Opens an interactive debugger.
93    /// Can only be used with `--trace`.
94    #[arg(long, requires = "trace")]
95    debug: bool,
96
97    #[arg(long, requires = "trace")]
98    decode_internal: bool,
99
100    /// Labels to apply to the traces; format: `address:label`.
101    /// Can only be used with `--trace`.
102    #[arg(long, requires = "trace")]
103    labels: Vec<String>,
104
105    /// The EVM Version to use.
106    /// Can only be used with `--trace`.
107    #[arg(long, requires = "trace")]
108    evm_version: Option<EvmVersion>,
109
110    /// The block height to query at.
111    ///
112    /// Can also be the tags earliest, finalized, safe, latest, or pending.
113    #[arg(long, short)]
114    block: Option<BlockId>,
115
116    /// Enable Odyssey features.
117    #[arg(long, alias = "alphanet")]
118    pub odyssey: bool,
119
120    #[command(subcommand)]
121    command: Option<CallSubcommands>,
122
123    #[command(flatten)]
124    tx: TransactionOpts,
125
126    #[command(flatten)]
127    eth: EthereumOpts,
128
129    /// Use current project artifacts for trace decoding.
130    #[arg(long, visible_alias = "la")]
131    pub with_local_artifacts: bool,
132
133    /// Override the accounts balance.
134    /// Format: "address:balance,address:balance"
135    #[arg(long = "override-balance", value_name = "ADDRESS:BALANCE", value_delimiter = ',')]
136    pub balance_overrides: Option<Vec<String>>,
137
138    /// Override the accounts nonce.
139    /// Format: "address:nonce,address:nonce"
140    #[arg(long = "override-nonce", value_name = "ADDRESS:NONCE", value_delimiter = ',')]
141    pub nonce_overrides: Option<Vec<String>>,
142
143    /// Override the accounts code.
144    /// Format: "address:code,address:code"
145    #[arg(long = "override-code", value_name = "ADDRESS:CODE", value_delimiter = ',')]
146    pub code_overrides: Option<Vec<String>>,
147
148    /// Override the accounts state and replace the current state entirely with the new one.
149    /// Format: "address:slot:value,address:slot:value"
150    #[arg(long = "override-state", value_name = "ADDRESS:SLOT:VALUE", value_delimiter = ',')]
151    pub state_overrides: Option<Vec<String>>,
152
153    /// Override the accounts state specific slots and preserve the rest of the state.
154    /// Format: "address:slot:value,address:slot:value"
155    #[arg(long = "override-state-diff", value_name = "ADDRESS:SLOT:VALUE", value_delimiter = ',')]
156    pub state_diff_overrides: Option<Vec<String>>,
157
158    /// Override the block timestamp.
159    #[arg(long = "block.time", value_name = "TIME")]
160    pub block_time: Option<u64>,
161
162    /// Override the block number.
163    #[arg(long = "block.number", value_name = "NUMBER")]
164    pub block_number: Option<u64>,
165}
166
167#[derive(Debug, Parser)]
168pub enum CallSubcommands {
169    /// ignores the address field and simulates creating a contract
170    #[command(name = "--create")]
171    Create {
172        /// Bytecode of contract.
173        code: String,
174
175        /// The signature of the constructor.
176        sig: Option<String>,
177
178        /// The arguments of the constructor.
179        args: Vec<String>,
180
181        /// Ether to send in the transaction.
182        ///
183        /// Either specified in wei, or as a string with a unit type.
184        ///
185        /// Examples: 1ether, 10gwei, 0.01ether
186        #[arg(long, value_parser = parse_ether_value)]
187        value: Option<U256>,
188    },
189}
190
191impl CallArgs {
192    pub async fn run(self) -> Result<()> {
193        let figment = self.eth.rpc.clone().into_figment(self.with_local_artifacts).merge(&self);
194        let evm_opts = figment.extract::<EvmOpts>()?;
195        let mut config = Config::from_provider(figment)?.sanitized();
196        let state_overrides = self.get_state_overrides()?;
197        let strategy = utils::get_executor_strategy(&config);
198        let block_overrides = self.get_block_overrides()?;
199
200        let Self {
201            to,
202            mut sig,
203            mut args,
204            mut tx,
205            eth,
206            command,
207            block,
208            trace,
209            evm_version,
210            debug,
211            decode_internal,
212            labels,
213            data,
214            with_local_artifacts,
215            disable_labels,
216            ..
217        } = self;
218
219        if let Some(data) = data {
220            sig = Some(data);
221        }
222
223        let provider = utils::get_provider(&config)?;
224        let sender = SenderKind::from_wallet_opts(eth.wallet).await?;
225        let from = sender.address();
226
227        let code = if let Some(CallSubcommands::Create {
228            code,
229            sig: create_sig,
230            args: create_args,
231            value,
232        }) = command
233        {
234            sig = create_sig;
235            args = create_args;
236            if let Some(value) = value {
237                tx.value = Some(value);
238            }
239            Some(code)
240        } else {
241            None
242        };
243
244        let (tx, func) = CastTxBuilder::new(&provider, tx, &config)
245            .await?
246            .with_to(to)
247            .await?
248            .with_code_sig_and_args(code, sig, args)
249            .await?
250            .build_raw(sender)
251            .await?;
252
253        if trace {
254            if let Some(BlockId::Number(BlockNumberOrTag::Number(block_number))) = self.block {
255                // Override Config `fork_block_number` (if set) with CLI value.
256                config.fork_block_number = Some(block_number);
257            }
258
259            let create2_deployer = evm_opts.create2_deployer;
260            let (mut env, fork, chain, odyssey) =
261                TracingExecutor::get_fork_material(&config, evm_opts).await?;
262
263            // modify settings that usually set in eth_call
264            env.evm_env.cfg_env.disable_block_gas_limit = true;
265            env.evm_env.block_env.gas_limit = u64::MAX;
266
267            // Apply the block overrides.
268            if let Some(block_overrides) = block_overrides {
269                if let Some(number) = block_overrides.number {
270                    env.evm_env.block_env.number = number.to();
271                }
272                if let Some(time) = block_overrides.time {
273                    env.evm_env.block_env.timestamp = U256::from(time);
274                }
275            }
276
277            let trace_mode = TraceMode::Call
278                .with_debug(debug)
279                .with_decode_internal(if decode_internal {
280                    InternalTraceMode::Full
281                } else {
282                    InternalTraceMode::None
283                })
284                .with_state_changes(shell::verbosity() > 4);
285            let mut executor = TracingExecutor::new(
286                env,
287                fork,
288                evm_version,
289                trace_mode,
290                odyssey,
291                create2_deployer,
292                state_overrides,
293                strategy,
294            )?;
295
296            let value = tx.value.unwrap_or_default();
297            let input = tx.inner.input.into_input().unwrap_or_default();
298            let tx_kind = tx.inner.to.expect("set by builder");
299            let env_tx = &mut executor.env_mut().tx;
300
301            if let Some(tx_type) = tx.inner.transaction_type {
302                env_tx.tx_type = tx_type;
303            }
304
305            if let Some(access_list) = tx.inner.access_list {
306                env_tx.access_list = access_list;
307
308                if env_tx.tx_type == TransactionType::Legacy as u8 {
309                    env_tx.tx_type = TransactionType::Eip2930 as u8;
310                }
311            }
312
313            let trace = match tx_kind {
314                TxKind::Create => {
315                    let deploy_result = executor.deploy(from, input, value, None);
316                    TraceResult::try_from(deploy_result)?
317                }
318                TxKind::Call(to) => TraceResult::from_raw(
319                    executor.transact_raw(from, to, input, value)?,
320                    TraceKind::Execution,
321                ),
322            };
323
324            let contracts_bytecode = fetch_contracts_bytecode_from_trace(&provider, &trace).await?;
325            handle_traces(
326                trace,
327                &config,
328                chain,
329                &contracts_bytecode,
330                labels,
331                with_local_artifacts,
332                debug,
333                decode_internal,
334                disable_labels,
335            )
336            .await?;
337
338            return Ok(());
339        }
340
341        let response = Cast::new(&provider)
342            .call(&tx, func.as_ref(), block, state_overrides, block_overrides)
343            .await?;
344
345        if response == "0x"
346            && let Some(contract_address) = tx.to.and_then(|tx_kind| tx_kind.into_to())
347        {
348            let code = provider.get_code_at(contract_address).await?;
349            if code.is_empty() {
350                sh_warn!("Contract code is empty")?;
351            }
352        }
353        sh_println!("{}", response)?;
354
355        Ok(())
356    }
357
358    /// Parse state overrides from command line arguments.
359    pub fn get_state_overrides(&self) -> eyre::Result<Option<StateOverride>> {
360        // Early return if no override set - <https://github.com/foundry-rs/foundry/issues/10705>
361        if [
362            self.balance_overrides.as_ref(),
363            self.nonce_overrides.as_ref(),
364            self.code_overrides.as_ref(),
365            self.state_overrides.as_ref(),
366            self.state_diff_overrides.as_ref(),
367        ]
368        .iter()
369        .all(Option::is_none)
370        {
371            return Ok(None);
372        }
373
374        let mut state_overrides_builder = StateOverridesBuilder::default();
375
376        // Parse balance overrides
377        for override_str in self.balance_overrides.iter().flatten() {
378            let (addr, balance) = address_value_override(override_str)?;
379            state_overrides_builder =
380                state_overrides_builder.with_balance(addr.parse()?, balance.parse()?);
381        }
382
383        // Parse nonce overrides
384        for override_str in self.nonce_overrides.iter().flatten() {
385            let (addr, nonce) = address_value_override(override_str)?;
386            state_overrides_builder =
387                state_overrides_builder.with_nonce(addr.parse()?, nonce.parse()?);
388        }
389
390        // Parse code overrides
391        for override_str in self.code_overrides.iter().flatten() {
392            let (addr, code_str) = address_value_override(override_str)?;
393            state_overrides_builder =
394                state_overrides_builder.with_code(addr.parse()?, Bytes::from_str(code_str)?);
395        }
396
397        type StateOverrides = HashMap<Address, HashMap<B256, B256>>;
398        let parse_state_overrides =
399            |overrides: &Option<Vec<String>>| -> Result<StateOverrides, eyre::Report> {
400                let mut state_overrides: StateOverrides = StateOverrides::default();
401
402                overrides.iter().flatten().try_for_each(|s| -> Result<(), eyre::Report> {
403                    let (addr, slot, value) = address_slot_value_override(s)?;
404                    state_overrides.entry(addr).or_default().insert(slot.into(), value.into());
405                    Ok(())
406                })?;
407
408                Ok(state_overrides)
409            };
410
411        // Parse and apply state overrides
412        for (addr, entries) in parse_state_overrides(&self.state_overrides)? {
413            state_overrides_builder = state_overrides_builder.with_state(addr, entries.into_iter());
414        }
415
416        // Parse and apply state diff overrides
417        for (addr, entries) in parse_state_overrides(&self.state_diff_overrides)? {
418            state_overrides_builder =
419                state_overrides_builder.with_state_diff(addr, entries.into_iter())
420        }
421
422        Ok(Some(state_overrides_builder.build()))
423    }
424
425    /// Parse block overrides from command line arguments.
426    pub fn get_block_overrides(&self) -> eyre::Result<Option<BlockOverrides>> {
427        let mut overrides = BlockOverrides::default();
428        if let Some(number) = self.block_number {
429            overrides = overrides.with_number(U256::from(number));
430        }
431        if let Some(time) = self.block_time {
432            overrides = overrides.with_time(time);
433        }
434        if overrides.is_empty() { Ok(None) } else { Ok(Some(overrides)) }
435    }
436}
437
438impl figment::Provider for CallArgs {
439    fn metadata(&self) -> Metadata {
440        Metadata::named("CallArgs")
441    }
442
443    fn data(&self) -> Result<Map<Profile, Dict>, figment::Error> {
444        let mut map = Map::new();
445
446        if self.odyssey {
447            map.insert("odyssey".into(), self.odyssey.into());
448        }
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}