Skip to main content

cast/cmd/
call.rs

1use super::run::fetch_contracts_bytecode_from_trace;
2use crate::{
3    Cast,
4    debug::handle_traces,
5    traces::TraceKind,
6    tx::{CastTxBuilder, SenderKind},
7};
8use alloy_ens::NameOrAddress;
9use alloy_network::{Network, TransactionBuilder};
10use alloy_primitives::{Address, B256, Bytes, TxKind, U256, hex, map::HashMap};
11use alloy_provider::Provider;
12use alloy_rpc_types::{
13    BlockId, BlockNumberOrTag, BlockOverrides,
14    state::{StateOverride, StateOverridesBuilder},
15};
16use clap::Parser;
17use eyre::Result;
18use foundry_cli::{
19    opts::{ChainValueParser, RpcOpts, TransactionOpts},
20    utils::{LoadConfig, TraceResult, parse_ether_value},
21};
22use foundry_common::{
23    FoundryTransactionBuilder,
24    abi::{encode_function_args, get_func},
25    provider::{ProviderBuilder, curl_transport::generate_curl_command},
26    sh_println, shell,
27};
28use foundry_compilers::artifacts::EvmVersion;
29use foundry_config::{
30    Chain, Config,
31    figment::{
32        self, Metadata, Profile,
33        value::{Dict, Map},
34    },
35};
36use foundry_evm::{
37    core::{
38        FoundryBlock, FoundryTransaction,
39        evm::{EthEvmNetwork, FoundryEvmNetwork, OpEvmNetwork, TempoEvmNetwork},
40    },
41    executors::TracingExecutor,
42    opts::EvmOpts,
43    traces::{InternalTraceMode, TraceMode},
44};
45use foundry_wallets::WalletOpts;
46use regex::Regex;
47use std::{str::FromStr, sync::LazyLock};
48
49// matches override pattern <address>:<slot>:<value>
50// e.g. 0x123:0x1:0x1234
51static OVERRIDE_PATTERN: LazyLock<Regex> =
52    LazyLock::new(|| Regex::new(r"^([^:]+):([^:]+):([^:]+)$").unwrap());
53
54/// CLI arguments for `cast call`.
55///
56/// ## State Override Flags
57///
58/// The following flags can be used to override the state for the call:
59///
60/// * `--override-balance <address>:<balance>` - Override the balance of an account
61/// * `--override-nonce <address>:<nonce>` - Override the nonce of an account
62/// * `--override-code <address>:<code>` - Override the code of an account
63/// * `--override-state <address>:<slot>:<value>` - Override a storage slot of an account
64///
65/// Multiple overrides can be specified for the same account. For example:
66///
67/// ```bash
68/// cast call 0x... "transfer(address,uint256)" 0x... 100 \
69///   --override-balance 0x123:0x1234 \
70///   --override-nonce 0x123:1 \
71///   --override-code 0x123:0x1234 \
72///   --override-state 0x123:0x1:0x1234
73///   --override-state-diff 0x123:0x1:0x1234
74/// ```
75#[derive(Debug, Parser)]
76pub struct CallArgs {
77    /// The destination of the transaction.
78    #[arg(value_parser = NameOrAddress::from_str)]
79    to: Option<NameOrAddress>,
80
81    /// The signature of the function to call.
82    sig: Option<String>,
83
84    /// The arguments of the function to call.
85    #[arg(allow_negative_numbers = true)]
86    args: Vec<String>,
87
88    /// Raw hex-encoded data for the transaction. Used instead of \[SIG\] and \[ARGS\].
89    #[arg(
90        long,
91        conflicts_with_all = &["sig", "args"]
92    )]
93    data: Option<String>,
94
95    /// Forks the remote rpc, executes the transaction locally and prints a trace
96    #[arg(long, default_value_t = false)]
97    trace: bool,
98
99    /// Disables the labels in the traces.
100    /// Can only be set with `--trace`.
101    #[arg(long, default_value_t = false, requires = "trace")]
102    disable_labels: bool,
103
104    /// Opens an interactive debugger.
105    /// Can only be used with `--trace`.
106    #[arg(long, requires = "trace")]
107    debug: bool,
108
109    /// Identify internal functions in traces.
110    ///
111    /// This will trace internal functions and decode stack parameters.
112    ///
113    /// Parameters stored in memory (such as bytes or arrays) are currently decoded only when a
114    /// single function is matched, similarly to `--debug`, for performance reasons.
115    #[arg(long, requires = "trace")]
116    decode_internal: bool,
117
118    /// Labels to apply to the traces; format: `address:label`.
119    /// Can only be used with `--trace`.
120    #[arg(long, requires = "trace")]
121    labels: Vec<String>,
122
123    /// The EVM Version to use.
124    /// Can only be used with `--trace`.
125    #[arg(long, requires = "trace")]
126    evm_version: Option<EvmVersion>,
127
128    /// The block height to query at.
129    ///
130    /// Can also be the tags earliest, finalized, safe, latest, or pending.
131    #[arg(long, short)]
132    block: Option<BlockId>,
133
134    #[command(subcommand)]
135    command: Option<CallSubcommands>,
136
137    #[command(flatten)]
138    tx: TransactionOpts,
139
140    #[command(flatten)]
141    rpc: RpcOpts,
142
143    #[command(flatten)]
144    wallet: WalletOpts,
145
146    #[arg(
147        short,
148        long,
149        alias = "chain-id",
150        env = "CHAIN",
151        value_parser = ChainValueParser::default(),
152    )]
153    pub chain: Option<Chain>,
154
155    /// Use current project artifacts for trace decoding.
156    #[arg(long, visible_alias = "la")]
157    pub with_local_artifacts: bool,
158
159    /// Override the accounts balance.
160    /// Format: "address:balance,address:balance"
161    #[arg(long = "override-balance", value_name = "ADDRESS:BALANCE", value_delimiter = ',')]
162    pub balance_overrides: Option<Vec<String>>,
163
164    /// Override the accounts nonce.
165    /// Format: "address:nonce,address:nonce"
166    #[arg(long = "override-nonce", value_name = "ADDRESS:NONCE", value_delimiter = ',')]
167    pub nonce_overrides: Option<Vec<String>>,
168
169    /// Override the accounts code.
170    /// Format: "address:code,address:code"
171    #[arg(long = "override-code", value_name = "ADDRESS:CODE", value_delimiter = ',')]
172    pub code_overrides: Option<Vec<String>>,
173
174    /// Override the accounts state and replace the current state entirely with the new one.
175    /// Format: "address:slot:value,address:slot:value"
176    #[arg(long = "override-state", value_name = "ADDRESS:SLOT:VALUE", value_delimiter = ',')]
177    pub state_overrides: Option<Vec<String>>,
178
179    /// Override the accounts state specific slots and preserve the rest of the state.
180    /// Format: "address:slot:value,address:slot:value"
181    #[arg(long = "override-state-diff", value_name = "ADDRESS:SLOT:VALUE", value_delimiter = ',')]
182    pub state_diff_overrides: Option<Vec<String>>,
183
184    /// Override the block timestamp.
185    #[arg(long = "block.time", value_name = "TIME")]
186    pub block_time: Option<u64>,
187
188    /// Override the block number.
189    #[arg(long = "block.number", value_name = "NUMBER")]
190    pub block_number: Option<u64>,
191}
192
193#[derive(Debug, Parser)]
194pub enum CallSubcommands {
195    /// ignores the address field and simulates creating a contract
196    #[command(name = "--create")]
197    Create {
198        /// Bytecode of contract.
199        code: String,
200
201        /// The signature of the constructor.
202        sig: Option<String>,
203
204        /// The arguments of the constructor.
205        #[arg(allow_negative_numbers = true)]
206        args: Vec<String>,
207
208        /// Ether to send in the transaction.
209        ///
210        /// Either specified in wei, or as a string with a unit type.
211        ///
212        /// Examples: 1ether, 10gwei, 0.01ether
213        #[arg(long, value_parser = parse_ether_value)]
214        value: Option<U256>,
215    },
216}
217
218impl CallArgs {
219    pub async fn run(self) -> Result<()> {
220        // Handle --curl mode early, before any provider interaction
221        if self.rpc.curl {
222            return self.run_curl().await;
223        }
224        if self.tx.tempo.is_tempo() {
225            self.run_with_network::<TempoEvmNetwork>().await
226        } else {
227            let figment = self.rpc.clone().into_figment(self.with_local_artifacts).merge(&self);
228            let mut evm_opts = figment.extract::<EvmOpts>()?;
229            evm_opts.infer_network_from_fork().await;
230
231            if evm_opts.networks.is_optimism() {
232                self.run_with_network::<OpEvmNetwork>().await
233            } else {
234                self.run_with_network::<EthEvmNetwork>().await
235            }
236        }
237    }
238
239    pub async fn run_with_network<FEN: FoundryEvmNetwork>(self) -> Result<()>
240    where
241        <FEN::Network as Network>::TransactionRequest: FoundryTransactionBuilder<FEN::Network>,
242    {
243        let figment = self.rpc.clone().into_figment(self.with_local_artifacts).merge(&self);
244        let evm_opts = figment.extract::<EvmOpts>()?;
245        let mut config = Config::from_provider(figment)?.sanitized();
246        let state_overrides = self.get_state_overrides()?;
247        let block_overrides = self.get_block_overrides()?;
248
249        let Self {
250            to,
251            mut sig,
252            mut args,
253            mut tx,
254            command,
255            block,
256            trace,
257            evm_version,
258            debug,
259            decode_internal,
260            labels,
261            data,
262            with_local_artifacts,
263            disable_labels,
264            wallet,
265            ..
266        } = self;
267
268        if let Some(data) = data {
269            sig = Some(data);
270        }
271
272        let provider = ProviderBuilder::<FEN::Network>::from_config(&config)?.build()?;
273        let sender = SenderKind::from_wallet_opts(wallet).await?;
274        let from = sender.address();
275
276        let code = if let Some(CallSubcommands::Create {
277            code,
278            sig: create_sig,
279            args: create_args,
280            value,
281        }) = command
282        {
283            sig = create_sig;
284            args = create_args;
285            if let Some(value) = value {
286                tx.value = Some(value);
287            }
288            Some(code)
289        } else {
290            None
291        };
292
293        let (tx, func) = CastTxBuilder::new(&provider, tx, &config)
294            .await?
295            .with_to(to)
296            .await?
297            .with_code_sig_and_args(code, sig, args)
298            .await?
299            .raw()
300            .build(sender)
301            .await?;
302
303        if trace {
304            if let Some(BlockId::Number(BlockNumberOrTag::Number(block_number))) = self.block {
305                // Override Config `fork_block_number` (if set) with CLI value.
306                config.fork_block_number = Some(block_number);
307            }
308
309            let create2_deployer = evm_opts.create2_deployer;
310            let (mut evm_env, tx_env, fork, chain, networks) =
311                TracingExecutor::<FEN>::get_fork_material(&mut config, evm_opts).await?;
312
313            // modify settings that usually set in eth_call
314            evm_env.cfg_env.disable_block_gas_limit = true;
315            evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX);
316            evm_env.block_env.set_gas_limit(u64::MAX);
317
318            // Apply the block overrides.
319            if let Some(block_overrides) = block_overrides {
320                if let Some(number) = block_overrides.number {
321                    evm_env.block_env.set_number(number.to());
322                }
323                if let Some(time) = block_overrides.time {
324                    evm_env.block_env.set_timestamp(U256::from(time));
325                }
326            }
327
328            let trace_mode = TraceMode::Call
329                .with_debug(debug)
330                .with_decode_internal(if decode_internal {
331                    InternalTraceMode::Full
332                } else {
333                    InternalTraceMode::None
334                })
335                .with_state_changes(shell::verbosity() > 4);
336            let mut executor = TracingExecutor::<FEN>::new(
337                (evm_env, tx_env),
338                fork,
339                evm_version,
340                trace_mode,
341                networks,
342                create2_deployer,
343                state_overrides,
344            )?;
345
346            let value = tx.value().unwrap_or_default();
347            let input = tx.input().cloned().unwrap_or_default();
348            let tx_kind = tx.kind().expect("set by builder");
349            let env_tx = executor.tx_env_mut();
350
351            // Set transaction options with --trace
352            if let Some(gas_limit) = tx.gas_limit() {
353                env_tx.set_gas_limit(gas_limit);
354            }
355
356            if let Some(gas_price) = tx.gas_price() {
357                env_tx.set_gas_price(gas_price);
358            }
359
360            if let Some(max_fee_per_gas) = tx.max_fee_per_gas() {
361                env_tx.set_gas_price(max_fee_per_gas);
362            }
363
364            if let Some(max_priority_fee_per_gas) = tx.max_priority_fee_per_gas() {
365                env_tx.set_gas_priority_fee(Some(max_priority_fee_per_gas));
366            }
367
368            if let Some(max_fee_per_blob_gas) = tx.max_fee_per_blob_gas() {
369                env_tx.set_max_fee_per_blob_gas(max_fee_per_blob_gas);
370            }
371
372            if let Some(nonce) = tx.nonce() {
373                env_tx.set_nonce(nonce);
374            }
375
376            env_tx.set_tx_type(tx.output_tx_type().into());
377
378            if let Some(access_list) = tx.access_list().cloned() {
379                env_tx.set_access_list(access_list);
380            }
381
382            if let Some(auth) = tx.authorization_list().cloned() {
383                env_tx.set_signed_authorization(auth);
384            }
385
386            let trace = match tx_kind {
387                TxKind::Create => {
388                    let deploy_result = executor.deploy(from, input, value, None);
389                    TraceResult::try_from(deploy_result)?
390                }
391                TxKind::Call(to) => TraceResult::from_raw(
392                    executor.transact_raw(from, to, input, value)?,
393                    TraceKind::Execution,
394                ),
395            };
396
397            let contracts_bytecode = fetch_contracts_bytecode_from_trace(&executor, &trace)?;
398            handle_traces(
399                trace,
400                &config,
401                chain,
402                &contracts_bytecode,
403                labels,
404                with_local_artifacts,
405                debug,
406                decode_internal,
407                disable_labels,
408                None,
409            )
410            .await?;
411
412            return Ok(());
413        }
414
415        let response = Cast::new(&provider)
416            .call(&tx, func.as_ref(), block, state_overrides, block_overrides)
417            .await?;
418
419        if response == "0x"
420            && let Some(contract_address) = tx.to()
421        {
422            let code = provider.get_code_at(contract_address).await?;
423            if code.is_empty() {
424                sh_warn!("Contract code is empty")?;
425            }
426        }
427        sh_println!("{}", response)?;
428
429        Ok(())
430    }
431
432    /// Handle --curl mode by generating curl command without any RPC interaction.
433    async fn run_curl(self) -> Result<()> {
434        let config = self.rpc.load_config()?;
435        let url = config.get_rpc_url_or_localhost_http()?;
436        let jwt = config.get_rpc_jwt_secret()?;
437
438        // Get call data - either from --data or from sig + args
439        let data = if let Some(data) = &self.data {
440            hex::decode(data)?
441        } else if let Some(sig) = &self.sig {
442            // If sig is already hex data, use it directly
443            if let Ok(data) = hex::decode(sig) {
444                data
445            } else {
446                // Parse function signature and encode args
447                let func = get_func(sig)?;
448                encode_function_args(&func, &self.args)?
449            }
450        } else {
451            Vec::new()
452        };
453
454        // Resolve the destination address (must be a raw address for curl mode)
455        let to = self.to.as_ref().map(|n| match n {
456            NameOrAddress::Address(addr) => Ok(*addr),
457            NameOrAddress::Name(name) => {
458                eyre::bail!("ENS names are not supported with --curl. Please use a raw address instead of '{}'", name)
459            }
460        }).transpose()?;
461
462        // Build eth_call params
463        let call_object = serde_json::json!({
464            "to": to,
465            "data": format!("0x{}", hex::encode(&data)),
466        });
467
468        let block_param = self
469            .block
470            .map(|b| serde_json::to_value(b).unwrap_or(serde_json::json!("latest")))
471            .unwrap_or(serde_json::json!("latest"));
472
473        let params = serde_json::json!([call_object, block_param]);
474
475        let curl_cmd = generate_curl_command(
476            url.as_ref(),
477            "eth_call",
478            params,
479            config.eth_rpc_headers.as_deref(),
480            jwt.as_deref(),
481        );
482
483        sh_println!("{}", curl_cmd)?;
484        Ok(())
485    }
486
487    /// Parse state overrides from command line arguments.
488    pub fn get_state_overrides(&self) -> eyre::Result<Option<StateOverride>> {
489        // Early return if no override set - <https://github.com/foundry-rs/foundry/issues/10705>
490        if [
491            self.balance_overrides.as_ref(),
492            self.nonce_overrides.as_ref(),
493            self.code_overrides.as_ref(),
494            self.state_overrides.as_ref(),
495            self.state_diff_overrides.as_ref(),
496        ]
497        .iter()
498        .all(Option::is_none)
499        {
500            return Ok(None);
501        }
502
503        let mut state_overrides_builder = StateOverridesBuilder::default();
504
505        // Parse balance overrides
506        for override_str in self.balance_overrides.iter().flatten() {
507            let (addr, balance) = address_value_override(override_str)?;
508            state_overrides_builder =
509                state_overrides_builder.with_balance(addr.parse()?, balance.parse()?);
510        }
511
512        // Parse nonce overrides
513        for override_str in self.nonce_overrides.iter().flatten() {
514            let (addr, nonce) = address_value_override(override_str)?;
515            state_overrides_builder =
516                state_overrides_builder.with_nonce(addr.parse()?, nonce.parse()?);
517        }
518
519        // Parse code overrides
520        for override_str in self.code_overrides.iter().flatten() {
521            let (addr, code_str) = address_value_override(override_str)?;
522            state_overrides_builder =
523                state_overrides_builder.with_code(addr.parse()?, Bytes::from_str(code_str)?);
524        }
525
526        type StateOverrides = HashMap<Address, HashMap<B256, B256>>;
527        let parse_state_overrides =
528            |overrides: &Option<Vec<String>>| -> Result<StateOverrides, eyre::Report> {
529                let mut state_overrides: StateOverrides = StateOverrides::default();
530
531                overrides.iter().flatten().try_for_each(|s| -> Result<(), eyre::Report> {
532                    let (addr, slot, value) = address_slot_value_override(s)?;
533                    state_overrides.entry(addr).or_default().insert(slot.into(), value.into());
534                    Ok(())
535                })?;
536
537                Ok(state_overrides)
538            };
539
540        // Parse and apply state overrides
541        for (addr, entries) in parse_state_overrides(&self.state_overrides)? {
542            state_overrides_builder = state_overrides_builder.with_state(addr, entries);
543        }
544
545        // Parse and apply state diff overrides
546        for (addr, entries) in parse_state_overrides(&self.state_diff_overrides)? {
547            state_overrides_builder = state_overrides_builder.with_state_diff(addr, entries)
548        }
549
550        Ok(Some(state_overrides_builder.build()))
551    }
552
553    /// Parse block overrides from command line arguments.
554    pub fn get_block_overrides(&self) -> eyre::Result<Option<BlockOverrides>> {
555        let mut overrides = BlockOverrides::default();
556        if let Some(number) = self.block_number {
557            overrides = overrides.with_number(U256::from(number));
558        }
559        if let Some(time) = self.block_time {
560            overrides = overrides.with_time(time);
561        }
562        if overrides.is_empty() { Ok(None) } else { Ok(Some(overrides)) }
563    }
564}
565
566impl figment::Provider for CallArgs {
567    fn metadata(&self) -> Metadata {
568        Metadata::named("CallArgs")
569    }
570
571    fn data(&self) -> Result<Map<Profile, Dict>, figment::Error> {
572        let mut map = Map::new();
573
574        if let Some(evm_version) = self.evm_version {
575            map.insert("evm_version".into(), figment::value::Value::serialize(evm_version)?);
576        }
577
578        Ok(Map::from([(Config::selected_profile(), map)]))
579    }
580}
581
582/// Parse an override string in the format address:value.
583fn address_value_override(address_override: &str) -> Result<(&str, &str)> {
584    address_override.split_once(':').ok_or_else(|| {
585        eyre::eyre!("Invalid override {address_override}. Expected <address>:<value>")
586    })
587}
588
589/// Parse an override string in the format address:slot:value.
590fn address_slot_value_override(address_override: &str) -> Result<(Address, U256, U256)> {
591    let captures = OVERRIDE_PATTERN.captures(address_override).ok_or_else(|| {
592        eyre::eyre!("Invalid override {address_override}. Expected <address>:<slot>:<value>")
593    })?;
594
595    Ok((
596        captures[1].parse()?, // Address
597        captures[2].parse()?, // Slot (U256)
598        captures[3].parse()?, // Value (U256)
599    ))
600}
601
602#[cfg(test)]
603mod tests {
604    use super::*;
605    use alloy_primitives::{U64, address, b256, fixed_bytes};
606
607    #[test]
608    fn test_get_state_overrides() {
609        let call_args = CallArgs::parse_from([
610            "foundry-cli",
611            "--override-balance",
612            "0x0000000000000000000000000000000000000001:2",
613            "--override-nonce",
614            "0x0000000000000000000000000000000000000001:3",
615            "--override-code",
616            "0x0000000000000000000000000000000000000001:0x04",
617            "--override-state",
618            "0x0000000000000000000000000000000000000001:5:6",
619            "--override-state-diff",
620            "0x0000000000000000000000000000000000000001:7:8",
621        ]);
622        let overrides = call_args.get_state_overrides().unwrap().unwrap();
623        let address = address!("0x0000000000000000000000000000000000000001");
624        if let Some(account_override) = overrides.get(&address) {
625            if let Some(balance) = account_override.balance {
626                assert_eq!(balance, U256::from(2));
627            }
628            if let Some(nonce) = account_override.nonce {
629                assert_eq!(nonce, 3);
630            }
631            if let Some(code) = &account_override.code {
632                assert_eq!(*code, Bytes::from([0x04]));
633            }
634            if let Some(state) = &account_override.state
635                && let Some(value) = state.get(&b256!(
636                    "0x0000000000000000000000000000000000000000000000000000000000000005"
637                ))
638            {
639                assert_eq!(
640                    *value,
641                    b256!("0x0000000000000000000000000000000000000000000000000000000000000006")
642                );
643            }
644            if let Some(state_diff) = &account_override.state_diff
645                && let Some(value) = state_diff.get(&b256!(
646                    "0x0000000000000000000000000000000000000000000000000000000000000007"
647                ))
648            {
649                assert_eq!(
650                    *value,
651                    b256!("0x0000000000000000000000000000000000000000000000000000000000000008")
652                );
653            }
654        }
655    }
656
657    #[test]
658    fn test_get_state_overrides_empty() {
659        let call_args = CallArgs::parse_from([""]);
660        let overrides = call_args.get_state_overrides().unwrap();
661        assert_eq!(overrides, None);
662    }
663
664    #[test]
665    fn test_get_block_overrides() {
666        let mut call_args = CallArgs::parse_from([""]);
667        call_args.block_number = Some(1);
668        call_args.block_time = Some(2);
669        let overrides = call_args.get_block_overrides().unwrap().unwrap();
670        assert_eq!(overrides.number, Some(U256::from(1)));
671        assert_eq!(overrides.time, Some(2));
672    }
673
674    #[test]
675    fn test_get_block_overrides_empty() {
676        let call_args = CallArgs::parse_from([""]);
677        let overrides = call_args.get_block_overrides().unwrap();
678        assert_eq!(overrides, None);
679    }
680
681    #[test]
682    fn test_address_value_override_success() {
683        let text = "0x0000000000000000000000000000000000000001:2";
684        let (address, value) = address_value_override(text).unwrap();
685        assert_eq!(address, "0x0000000000000000000000000000000000000001");
686        assert_eq!(value, "2");
687    }
688
689    #[test]
690    fn test_address_value_override_error() {
691        let text = "invalid_value";
692        let error = address_value_override(text).unwrap_err();
693        assert_eq!(error.to_string(), "Invalid override invalid_value. Expected <address>:<value>");
694    }
695
696    #[test]
697    fn test_address_slot_value_override_success() {
698        let text = "0x0000000000000000000000000000000000000001:2:3";
699        let (address, slot, value) = address_slot_value_override(text).unwrap();
700        assert_eq!(*address, fixed_bytes!("0x0000000000000000000000000000000000000001"));
701        assert_eq!(slot, U256::from(2));
702        assert_eq!(value, U256::from(3));
703    }
704
705    #[test]
706    fn test_address_slot_value_override_error() {
707        let text = "invalid_value";
708        let error = address_slot_value_override(text).unwrap_err();
709        assert_eq!(
710            error.to_string(),
711            "Invalid override invalid_value. Expected <address>:<slot>:<value>"
712        );
713    }
714
715    #[test]
716    fn can_parse_call_data() {
717        let data = hex::encode("hello");
718        let args = CallArgs::parse_from(["foundry-cli", "--data", data.as_str()]);
719        assert_eq!(args.data, Some(data));
720
721        let data = hex::encode_prefixed("hello");
722        let args = CallArgs::parse_from(["foundry-cli", "--data", data.as_str()]);
723        assert_eq!(args.data, Some(data));
724    }
725
726    #[test]
727    fn can_parse_state_overrides() {
728        let args = CallArgs::parse_from([
729            "foundry-cli",
730            "--override-balance",
731            "0x123:0x1234",
732            "--override-nonce",
733            "0x123:1",
734            "--override-code",
735            "0x123:0x1234",
736            "--override-state",
737            "0x123:0x1:0x1234",
738        ]);
739
740        assert_eq!(args.balance_overrides, Some(vec!["0x123:0x1234".to_string()]));
741        assert_eq!(args.nonce_overrides, Some(vec!["0x123:1".to_string()]));
742        assert_eq!(args.code_overrides, Some(vec!["0x123:0x1234".to_string()]));
743        assert_eq!(args.state_overrides, Some(vec!["0x123:0x1:0x1234".to_string()]));
744    }
745
746    #[test]
747    fn can_parse_multiple_state_overrides() {
748        let args = CallArgs::parse_from([
749            "foundry-cli",
750            "--override-balance",
751            "0x123:0x1234",
752            "--override-balance",
753            "0x456:0x5678",
754            "--override-nonce",
755            "0x123:1",
756            "--override-nonce",
757            "0x456:2",
758            "--override-code",
759            "0x123:0x1234",
760            "--override-code",
761            "0x456:0x5678",
762            "--override-state",
763            "0x123:0x1:0x1234",
764            "--override-state",
765            "0x456:0x2:0x5678",
766        ]);
767
768        assert_eq!(
769            args.balance_overrides,
770            Some(vec!["0x123:0x1234".to_string(), "0x456:0x5678".to_string()])
771        );
772        assert_eq!(args.nonce_overrides, Some(vec!["0x123:1".to_string(), "0x456:2".to_string()]));
773        assert_eq!(
774            args.code_overrides,
775            Some(vec!["0x123:0x1234".to_string(), "0x456:0x5678".to_string()])
776        );
777        assert_eq!(
778            args.state_overrides,
779            Some(vec!["0x123:0x1:0x1234".to_string(), "0x456:0x2:0x5678".to_string()])
780        );
781    }
782
783    #[test]
784    fn test_negative_args_with_flags() {
785        // Test that negative args work with flags
786        let args = CallArgs::parse_from([
787            "foundry-cli",
788            "--trace",
789            "0xDeaDBeeFcAfEbAbEfAcEfEeDcBaDbEeFcAfEbAbE",
790            "process(int256)",
791            "-999999",
792            "--debug",
793        ]);
794
795        assert!(args.trace);
796        assert!(args.debug);
797        assert_eq!(args.args, vec!["-999999"]);
798    }
799
800    #[test]
801    fn test_transaction_opts_with_trace() {
802        // Test that transaction options are correctly parsed when using --trace
803        let args = CallArgs::parse_from([
804            "foundry-cli",
805            "--trace",
806            "--gas-limit",
807            "1000000",
808            "--gas-price",
809            "20000000000",
810            "--priority-gas-price",
811            "2000000000",
812            "--nonce",
813            "42",
814            "--value",
815            "1000000000000000000", // 1 ETH
816            "--blob-gas-price",
817            "10000000000",
818            "0xDeaDBeeFcAfEbAbEfAcEfEeDcBaDbEeFcAfEbAbE",
819            "balanceOf(address)",
820            "0x123456789abcdef123456789abcdef123456789a",
821        ]);
822
823        assert!(args.trace);
824        assert_eq!(args.tx.gas_limit, Some(U256::from(1000000u32)));
825        assert_eq!(args.tx.gas_price, Some(U256::from(20000000000u64)));
826        assert_eq!(args.tx.priority_gas_price, Some(U256::from(2000000000u64)));
827        assert_eq!(args.tx.nonce, Some(U64::from(42)));
828        assert_eq!(args.tx.value, Some(U256::from(1000000000000000000u64)));
829        assert_eq!(args.tx.blob_gas_price, Some(U256::from(10000000000u64)));
830    }
831}