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