cast/cmd/
call.rs

1use crate::{
2    Cast,
3    traces::TraceKind,
4    tx::{CastTxBuilder, SenderKind},
5};
6use alloy_ens::NameOrAddress;
7use alloy_primitives::{Address, Bytes, TxKind, U256};
8use alloy_provider::Provider;
9use alloy_rpc_types::{
10    BlockId, BlockNumberOrTag, BlockOverrides,
11    state::{StateOverride, StateOverridesBuilder},
12};
13use clap::Parser;
14use eyre::Result;
15use foundry_cli::{
16    opts::{EthereumOpts, TransactionOpts},
17    utils::{self, TraceResult, handle_traces, parse_ether_value},
18};
19use foundry_common::shell;
20use foundry_compilers::artifacts::EvmVersion;
21use foundry_config::{
22    Config,
23    figment::{
24        self, Figment, Metadata, Profile,
25        value::{Dict, Map},
26    },
27};
28use foundry_evm::{
29    executors::TracingExecutor,
30    opts::EvmOpts,
31    traces::{InternalTraceMode, TraceMode},
32};
33use regex::Regex;
34use revm::context::TransactionType;
35use std::{str::FromStr, sync::LazyLock};
36
37use super::run::fetch_contracts_bytecode_from_trace;
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    args: Vec<String>,
76
77    /// Raw hex-encoded data for the transaction. Used instead of \[SIG\] and \[ARGS\].
78    #[arg(
79        long,
80        conflicts_with_all = &["sig", "args"]
81    )]
82    data: Option<String>,
83
84    /// Forks the remote rpc, executes the transaction locally and prints a trace
85    #[arg(long, default_value_t = false)]
86    trace: bool,
87
88    /// Disables the labels in the traces.
89    /// Can only be set with `--trace`.
90    #[arg(long, default_value_t = false, requires = "trace")]
91    disable_labels: bool,
92
93    /// Opens an interactive debugger.
94    /// Can only be used with `--trace`.
95    #[arg(long, requires = "trace")]
96    debug: bool,
97
98    #[arg(long, requires = "trace")]
99    decode_internal: bool,
100
101    /// Labels to apply to the traces; format: `address:label`.
102    /// Can only be used with `--trace`.
103    #[arg(long, requires = "trace")]
104    labels: Vec<String>,
105
106    /// The EVM Version to use.
107    /// Can only be used with `--trace`.
108    #[arg(long, requires = "trace")]
109    evm_version: Option<EvmVersion>,
110
111    /// The block height to query at.
112    ///
113    /// Can also be the tags earliest, finalized, safe, latest, or pending.
114    #[arg(long, short)]
115    block: Option<BlockId>,
116
117    /// Enable Odyssey features.
118    #[arg(long, alias = "alphanet")]
119    pub odyssey: bool,
120
121    #[command(subcommand)]
122    command: Option<CallSubcommands>,
123
124    #[command(flatten)]
125    tx: TransactionOpts,
126
127    #[command(flatten)]
128    eth: EthereumOpts,
129
130    /// Use current project artifacts for trace decoding.
131    #[arg(long, visible_alias = "la")]
132    pub with_local_artifacts: bool,
133
134    /// Override the balance of an account.
135    /// Format: address:balance
136    #[arg(long = "override-balance", value_name = "ADDRESS:BALANCE")]
137    pub balance_overrides: Option<Vec<String>>,
138
139    /// Override the nonce of an account.
140    /// Format: address:nonce
141    #[arg(long = "override-nonce", value_name = "ADDRESS:NONCE")]
142    pub nonce_overrides: Option<Vec<String>>,
143
144    /// Override the code of an account.
145    /// Format: address:code
146    #[arg(long = "override-code", value_name = "ADDRESS:CODE")]
147    pub code_overrides: Option<Vec<String>>,
148
149    /// Override the state of an account.
150    /// Format: address:slot:value
151    #[arg(long = "override-state", value_name = "ADDRESS:SLOT:VALUE")]
152    pub state_overrides: Option<Vec<String>>,
153
154    /// Override the state diff of an account.
155    /// Format: address:slot:value
156    #[arg(long = "override-state-diff", value_name = "ADDRESS:SLOT:VALUE")]
157    pub state_diff_overrides: Option<Vec<String>>,
158
159    /// Override the block timestamp.
160    #[arg(long = "block.time", value_name = "TIME")]
161    pub block_time: Option<u64>,
162
163    /// Override the block number.
164    #[arg(long = "block.number", value_name = "NUMBER")]
165    pub block_number: Option<u64>,
166}
167
168#[derive(Debug, Parser)]
169pub enum CallSubcommands {
170    /// ignores the address field and simulates creating a contract
171    #[command(name = "--create")]
172    Create {
173        /// Bytecode of contract.
174        code: String,
175
176        /// The signature of the constructor.
177        sig: Option<String>,
178
179        /// The arguments of the constructor.
180        args: Vec<String>,
181
182        /// Ether to send in the transaction.
183        ///
184        /// Either specified in wei, or as a string with a unit type.
185        ///
186        /// Examples: 1ether, 10gwei, 0.01ether
187        #[arg(long, value_parser = parse_ether_value)]
188        value: Option<U256>,
189    },
190}
191
192impl CallArgs {
193    pub async fn run(self) -> Result<()> {
194        let figment = Into::<Figment>::into(&self.eth).merge(&self);
195        let evm_opts = figment.extract::<EvmOpts>()?;
196        let mut config = Config::from_provider(figment)?.sanitized();
197        let state_overrides = self.get_state_overrides()?;
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            )?;
294
295            let value = tx.value.unwrap_or_default();
296            let input = tx.inner.input.into_input().unwrap_or_default();
297            let tx_kind = tx.inner.to.expect("set by builder");
298            let env_tx = &mut executor.env_mut().tx;
299
300            if let Some(tx_type) = tx.inner.transaction_type {
301                env_tx.tx_type = tx_type;
302            }
303
304            if let Some(access_list) = tx.inner.access_list {
305                env_tx.access_list = access_list;
306
307                if env_tx.tx_type == TransactionType::Legacy as u8 {
308                    env_tx.tx_type = TransactionType::Eip2930 as u8;
309                }
310            }
311
312            let trace = match tx_kind {
313                TxKind::Create => {
314                    let deploy_result = executor.deploy(from, input, value, None);
315                    TraceResult::try_from(deploy_result)?
316                }
317                TxKind::Call(to) => TraceResult::from_raw(
318                    executor.transact_raw(from, to, input, value)?,
319                    TraceKind::Execution,
320                ),
321            };
322
323            let contracts_bytecode = fetch_contracts_bytecode_from_trace(&provider, &trace).await?;
324            handle_traces(
325                trace,
326                &config,
327                chain,
328                &contracts_bytecode,
329                labels,
330                with_local_artifacts,
331                debug,
332                decode_internal,
333                disable_labels,
334            )
335            .await?;
336
337            return Ok(());
338        }
339
340        let response = Cast::new(&provider)
341            .call(&tx, func.as_ref(), block, state_overrides, block_overrides)
342            .await?;
343
344        if response == "0x"
345            && let Some(contract_address) = tx.to.and_then(|tx_kind| tx_kind.into_to())
346        {
347            let code = provider.get_code_at(contract_address).await?;
348            if code.is_empty() {
349                sh_warn!("Contract code is empty")?;
350            }
351        }
352        sh_println!("{}", response)?;
353
354        Ok(())
355    }
356
357    /// Parse state overrides from command line arguments.
358    pub fn get_state_overrides(&self) -> eyre::Result<Option<StateOverride>> {
359        // Early return if no override set - <https://github.com/foundry-rs/foundry/issues/10705>
360        if [
361            self.balance_overrides.as_ref(),
362            self.nonce_overrides.as_ref(),
363            self.code_overrides.as_ref(),
364            self.state_overrides.as_ref(),
365            self.state_diff_overrides.as_ref(),
366        ]
367        .iter()
368        .all(Option::is_none)
369        {
370            return Ok(None);
371        }
372
373        let mut state_overrides_builder = StateOverridesBuilder::default();
374
375        // Parse balance overrides
376        for override_str in self.balance_overrides.iter().flatten() {
377            let (addr, balance) = address_value_override(override_str)?;
378            state_overrides_builder =
379                state_overrides_builder.with_balance(addr.parse()?, balance.parse()?);
380        }
381
382        // Parse nonce overrides
383        for override_str in self.nonce_overrides.iter().flatten() {
384            let (addr, nonce) = address_value_override(override_str)?;
385            state_overrides_builder =
386                state_overrides_builder.with_nonce(addr.parse()?, nonce.parse()?);
387        }
388
389        // Parse code overrides
390        for override_str in self.code_overrides.iter().flatten() {
391            let (addr, code_str) = address_value_override(override_str)?;
392            state_overrides_builder =
393                state_overrides_builder.with_code(addr.parse()?, Bytes::from_str(code_str)?);
394        }
395
396        // Parse state overrides
397        for override_str in self.state_overrides.iter().flatten() {
398            let (addr, slot, value) = address_slot_value_override(override_str)?;
399            state_overrides_builder =
400                state_overrides_builder.with_state(addr, [(slot.into(), value.into())]);
401        }
402
403        // Parse state diff overrides
404        for override_str in self.state_diff_overrides.iter().flatten() {
405            let (addr, slot, value) = address_slot_value_override(override_str)?;
406            state_overrides_builder =
407                state_overrides_builder.with_state_diff(addr, [(slot.into(), value.into())]);
408        }
409
410        Ok(Some(state_overrides_builder.build()))
411    }
412
413    /// Parse block overrides from command line arguments.
414    pub fn get_block_overrides(&self) -> eyre::Result<Option<BlockOverrides>> {
415        let mut overrides = BlockOverrides::default();
416        if let Some(number) = self.block_number {
417            overrides = overrides.with_number(U256::from(number));
418        }
419        if let Some(time) = self.block_time {
420            overrides = overrides.with_time(time);
421        }
422        if overrides.is_empty() { Ok(None) } else { Ok(Some(overrides)) }
423    }
424}
425
426impl figment::Provider for CallArgs {
427    fn metadata(&self) -> Metadata {
428        Metadata::named("CallArgs")
429    }
430
431    fn data(&self) -> Result<Map<Profile, Dict>, figment::Error> {
432        let mut map = Map::new();
433
434        if self.odyssey {
435            map.insert("odyssey".into(), self.odyssey.into());
436        }
437
438        if let Some(evm_version) = self.evm_version {
439            map.insert("evm_version".into(), figment::value::Value::serialize(evm_version)?);
440        }
441
442        Ok(Map::from([(Config::selected_profile(), map)]))
443    }
444}
445
446/// Parse an override string in the format address:value.
447fn address_value_override(address_override: &str) -> Result<(&str, &str)> {
448    address_override.split_once(':').ok_or_else(|| {
449        eyre::eyre!("Invalid override {address_override}. Expected <address>:<value>")
450    })
451}
452
453/// Parse an override string in the format address:slot:value.
454fn address_slot_value_override(address_override: &str) -> Result<(Address, U256, U256)> {
455    let captures = OVERRIDE_PATTERN.captures(address_override).ok_or_else(|| {
456        eyre::eyre!("Invalid override {address_override}. Expected <address>:<slot>:<value>")
457    })?;
458
459    Ok((
460        captures[1].parse()?, // Address
461        captures[2].parse()?, // Slot (U256)
462        captures[3].parse()?, // Value (U256)
463    ))
464}
465
466#[cfg(test)]
467mod tests {
468    use super::*;
469    use alloy_primitives::{address, b256, fixed_bytes, hex};
470
471    #[test]
472    fn test_get_state_overrides() {
473        let call_args = CallArgs::parse_from([
474            "foundry-cli",
475            "--override-balance",
476            "0x0000000000000000000000000000000000000001:2",
477            "--override-nonce",
478            "0x0000000000000000000000000000000000000001:3",
479            "--override-code",
480            "0x0000000000000000000000000000000000000001:0x04",
481            "--override-state",
482            "0x0000000000000000000000000000000000000001:5:6",
483            "--override-state-diff",
484            "0x0000000000000000000000000000000000000001:7:8",
485        ]);
486        let overrides = call_args.get_state_overrides().unwrap().unwrap();
487        let address = address!("0x0000000000000000000000000000000000000001");
488        if let Some(account_override) = overrides.get(&address) {
489            if let Some(balance) = account_override.balance {
490                assert_eq!(balance, U256::from(2));
491            }
492            if let Some(nonce) = account_override.nonce {
493                assert_eq!(nonce, 3);
494            }
495            if let Some(code) = &account_override.code {
496                assert_eq!(*code, Bytes::from([0x04]));
497            }
498            if let Some(state) = &account_override.state
499                && let Some(value) = state.get(&b256!(
500                    "0x0000000000000000000000000000000000000000000000000000000000000005"
501                ))
502            {
503                assert_eq!(
504                    *value,
505                    b256!("0x0000000000000000000000000000000000000000000000000000000000000006")
506                );
507            }
508            if let Some(state_diff) = &account_override.state_diff
509                && let Some(value) = state_diff.get(&b256!(
510                    "0x0000000000000000000000000000000000000000000000000000000000000007"
511                ))
512            {
513                assert_eq!(
514                    *value,
515                    b256!("0x0000000000000000000000000000000000000000000000000000000000000008")
516                );
517            }
518        }
519    }
520
521    #[test]
522    fn test_get_state_overrides_empty() {
523        let call_args = CallArgs::parse_from([""]);
524        let overrides = call_args.get_state_overrides().unwrap();
525        assert_eq!(overrides, None);
526    }
527
528    #[test]
529    fn test_get_block_overrides() {
530        let mut call_args = CallArgs::parse_from([""]);
531        call_args.block_number = Some(1);
532        call_args.block_time = Some(2);
533        let overrides = call_args.get_block_overrides().unwrap().unwrap();
534        assert_eq!(overrides.number, Some(U256::from(1)));
535        assert_eq!(overrides.time, Some(2));
536    }
537
538    #[test]
539    fn test_get_block_overrides_empty() {
540        let call_args = CallArgs::parse_from([""]);
541        let overrides = call_args.get_block_overrides().unwrap();
542        assert_eq!(overrides, None);
543    }
544
545    #[test]
546    fn test_address_value_override_success() {
547        let text = "0x0000000000000000000000000000000000000001:2";
548        let (address, value) = address_value_override(text).unwrap();
549        assert_eq!(address, "0x0000000000000000000000000000000000000001");
550        assert_eq!(value, "2");
551    }
552
553    #[test]
554    fn test_address_value_override_error() {
555        let text = "invalid_value";
556        let error = address_value_override(text).unwrap_err();
557        assert_eq!(error.to_string(), "Invalid override invalid_value. Expected <address>:<value>");
558    }
559
560    #[test]
561    fn test_address_slot_value_override_success() {
562        let text = "0x0000000000000000000000000000000000000001:2:3";
563        let (address, slot, value) = address_slot_value_override(text).unwrap();
564        assert_eq!(*address, fixed_bytes!("0x0000000000000000000000000000000000000001"));
565        assert_eq!(slot, U256::from(2));
566        assert_eq!(value, U256::from(3));
567    }
568
569    #[test]
570    fn test_address_slot_value_override_error() {
571        let text = "invalid_value";
572        let error = address_slot_value_override(text).unwrap_err();
573        assert_eq!(
574            error.to_string(),
575            "Invalid override invalid_value. Expected <address>:<slot>:<value>"
576        );
577    }
578
579    #[test]
580    fn can_parse_call_data() {
581        let data = hex::encode("hello");
582        let args = CallArgs::parse_from(["foundry-cli", "--data", data.as_str()]);
583        assert_eq!(args.data, Some(data));
584
585        let data = hex::encode_prefixed("hello");
586        let args = CallArgs::parse_from(["foundry-cli", "--data", data.as_str()]);
587        assert_eq!(args.data, Some(data));
588    }
589
590    #[test]
591    fn can_parse_state_overrides() {
592        let args = CallArgs::parse_from([
593            "foundry-cli",
594            "--override-balance",
595            "0x123:0x1234",
596            "--override-nonce",
597            "0x123:1",
598            "--override-code",
599            "0x123:0x1234",
600            "--override-state",
601            "0x123:0x1:0x1234",
602        ]);
603
604        assert_eq!(args.balance_overrides, Some(vec!["0x123:0x1234".to_string()]));
605        assert_eq!(args.nonce_overrides, Some(vec!["0x123:1".to_string()]));
606        assert_eq!(args.code_overrides, Some(vec!["0x123:0x1234".to_string()]));
607        assert_eq!(args.state_overrides, Some(vec!["0x123:0x1:0x1234".to_string()]));
608    }
609
610    #[test]
611    fn can_parse_multiple_state_overrides() {
612        let args = CallArgs::parse_from([
613            "foundry-cli",
614            "--override-balance",
615            "0x123:0x1234",
616            "--override-balance",
617            "0x456:0x5678",
618            "--override-nonce",
619            "0x123:1",
620            "--override-nonce",
621            "0x456:2",
622            "--override-code",
623            "0x123:0x1234",
624            "--override-code",
625            "0x456:0x5678",
626            "--override-state",
627            "0x123:0x1:0x1234",
628            "--override-state",
629            "0x456:0x2:0x5678",
630        ]);
631
632        assert_eq!(
633            args.balance_overrides,
634            Some(vec!["0x123:0x1234".to_string(), "0x456:0x5678".to_string()])
635        );
636        assert_eq!(args.nonce_overrides, Some(vec!["0x123:1".to_string(), "0x456:2".to_string()]));
637        assert_eq!(
638            args.code_overrides,
639            Some(vec!["0x123:0x1234".to_string(), "0x456:0x5678".to_string()])
640        );
641        assert_eq!(
642            args.state_overrides,
643            Some(vec!["0x123:0x1:0x1234".to_string(), "0x456:0x2:0x5678".to_string()])
644        );
645    }
646}