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, NetworkTransactionBuilder, 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 ExitCode, diagnostic,
20 json::{JsonEnvelope, JsonMessage, print_json},
21 opts::{ChainValueParser, RpcOpts, TransactionOpts},
22 utils::{LoadConfig, TraceResult, parse_ether_value},
23};
24use foundry_common::{
25 FoundryTransactionBuilder,
26 abi::{encode_function_args, get_func},
27 provider::{ProviderBuilder, curl_transport::generate_curl_command},
28 sh_println, shell,
29};
30use foundry_compilers::artifacts::EvmVersion;
31use foundry_config::{
32 Chain, Config,
33 figment::{
34 self, Metadata, Profile,
35 value::{Dict, Map},
36 },
37};
38#[cfg(feature = "optimism")]
39use foundry_evm::core::evm::OpEvmNetwork;
40use foundry_evm::{
41 core::{
42 FoundryBlock, FoundryTransaction,
43 evm::{EthEvmNetwork, FoundryEvmNetwork, TempoEvmNetwork},
44 },
45 executors::TracingExecutor,
46 opts::EvmOpts,
47 traces::{InternalTraceMode, TraceMode},
48};
49use foundry_wallets::WalletOpts;
50use regex::Regex;
51use serde::Serialize;
52use std::{str::FromStr, sync::LazyLock};
53
54#[derive(Clone, Debug, Serialize)]
56pub struct CallData {
57 pub raw: String,
59}
60
61fn rpc_failure(err: &eyre::Report) -> ! {
63 let cause_chain: Vec<String> = err.chain().map(ToString::to_string).collect();
64 let message = cause_chain.first().cloned().unwrap_or_else(|| err.to_string());
65 let envelope = JsonEnvelope::error(
66 JsonMessage::error(diagnostic::network::RPC_ERROR, message)
67 .with_details(serde_json::json!({ "cause_chain": cause_chain })),
68 );
69 let _ = print_json(&envelope);
70 std::process::exit(ExitCode::Network.to_i32());
71}
72
73static OVERRIDE_PATTERN: LazyLock<Regex> =
76 LazyLock::new(|| Regex::new(r"^([^:]+):([^:]+):([^:]+)$").unwrap());
77
78#[derive(Debug, Parser)]
100pub struct CallArgs {
101 #[arg(value_parser = NameOrAddress::from_str)]
103 to: Option<NameOrAddress>,
104
105 sig: Option<String>,
107
108 #[arg(allow_negative_numbers = true)]
110 args: Vec<String>,
111
112 #[arg(
114 long,
115 conflicts_with_all = &["sig", "args"]
116 )]
117 data: Option<String>,
118
119 #[arg(long, default_value_t = false)]
121 trace: bool,
122
123 #[arg(long, default_value_t = false, requires = "trace")]
126 disable_labels: bool,
127
128 #[arg(long, requires = "trace")]
131 debug: bool,
132
133 #[arg(long, requires = "trace")]
140 decode_internal: bool,
141
142 #[arg(long, requires = "trace")]
145 labels: Vec<String>,
146
147 #[arg(long, requires = "trace")]
150 evm_version: Option<EvmVersion>,
151
152 #[arg(long, short)]
156 block: Option<BlockId>,
157
158 #[command(subcommand)]
159 command: Option<CallSubcommands>,
160
161 #[command(flatten)]
162 tx: TransactionOpts,
163
164 #[command(flatten)]
165 rpc: RpcOpts,
166
167 #[command(flatten)]
168 wallet: WalletOpts,
169
170 #[arg(
171 short,
172 long,
173 alias = "chain-id",
174 env = "CHAIN",
175 value_parser = ChainValueParser::default(),
176 )]
177 pub chain: Option<Chain>,
178
179 #[arg(long, visible_alias = "la")]
181 pub with_local_artifacts: bool,
182
183 #[arg(long = "override-balance", value_name = "ADDRESS:BALANCE", value_delimiter = ',')]
186 pub balance_overrides: Option<Vec<String>>,
187
188 #[arg(long = "override-nonce", value_name = "ADDRESS:NONCE", value_delimiter = ',')]
191 pub nonce_overrides: Option<Vec<String>>,
192
193 #[arg(long = "override-code", value_name = "ADDRESS:CODE", value_delimiter = ',')]
196 pub code_overrides: Option<Vec<String>>,
197
198 #[arg(long = "override-state", value_name = "ADDRESS:SLOT:VALUE", value_delimiter = ',')]
201 pub state_overrides: Option<Vec<String>>,
202
203 #[arg(long = "override-state-diff", value_name = "ADDRESS:SLOT:VALUE", value_delimiter = ',')]
206 pub state_diff_overrides: Option<Vec<String>>,
207
208 #[arg(long = "block.time", value_name = "TIME")]
210 pub block_time: Option<u64>,
211
212 #[arg(long = "block.number", value_name = "NUMBER")]
214 pub block_number: Option<u64>,
215}
216
217#[derive(Debug, Parser)]
218pub enum CallSubcommands {
219 #[command(name = "--create")]
221 Create {
222 code: String,
224
225 sig: Option<String>,
227
228 #[arg(allow_negative_numbers = true)]
230 args: Vec<String>,
231
232 #[arg(long, value_parser = parse_ether_value)]
238 value: Option<U256>,
239 },
240}
241
242impl CallArgs {
243 pub async fn run(self) -> Result<()> {
244 if foundry_cli::is_machine() {
246 let unsupported = [
250 ("--trace", self.trace),
251 ("--debug", self.debug),
252 ("--decode-internal", self.decode_internal),
253 ("--curl", self.rpc.curl),
254 ("--interactive", self.wallet.raw.interactive),
255 ("--ledger", self.wallet.ledger),
256 ("--trezor", self.wallet.trezor),
257 ("--aws", self.wallet.aws),
258 ("--gcp", self.wallet.gcp),
259 ]
260 .into_iter()
261 .filter_map(|(name, on)| on.then_some(name))
262 .collect::<Vec<_>>();
263 if !unsupported.is_empty() {
264 foundry_cli::machine::bail_machine_usage_with_details(
265 format!(
266 "`cast call` under `--machine` does not yet support {}; \
267 run without `--machine` or omit those flags.",
268 unsupported.join(", ")
269 ),
270 serde_json::json!({ "unsupported_flags": unsupported }),
271 );
272 }
273 let has_keystore =
275 self.wallet.keystore_path.is_some() || self.wallet.keystore_account_name.is_some();
276 let has_noninteractive_password = self.wallet.keystore_password.is_some()
277 || self.wallet.keystore_password_file.is_some();
278 if has_keystore && !has_noninteractive_password {
279 foundry_cli::machine::bail_machine_usage(
280 "`cast call --machine` requires `--password`, `--password-file`, or the \
281 `ETH_PASSWORD` env var when `--keystore` / `--account` is set; an unlocked \
282 keystore would prompt on stdin and corrupt the envelope.",
283 );
284 }
285 }
286
287 if self.rpc.curl {
289 return self.run_curl().await;
290 }
291
292 if self.tx.tempo.is_tempo() {
293 return self.run_with_network::<TempoEvmNetwork>().await;
294 }
295
296 let figment = self.rpc.clone().into_figment(self.with_local_artifacts).merge(&self);
297 let mut evm_opts = figment.extract::<EvmOpts>()?;
298 if let Some(chain) = self.chain {
299 evm_opts.networks = evm_opts.networks.with_chain_id(chain.id());
300 }
301 evm_opts.infer_network_from_fork().await;
302
303 if evm_opts.networks.is_tempo() {
304 return self.run_with_network::<TempoEvmNetwork>().await;
305 }
306
307 #[cfg(feature = "optimism")]
308 if evm_opts.networks.is_optimism() {
309 return self.run_with_network::<OpEvmNetwork>().await;
310 }
311
312 self.run_with_network::<EthEvmNetwork>().await
313 }
314
315 pub async fn run_with_network<FEN: FoundryEvmNetwork>(self) -> Result<()>
316 where
317 <FEN::Network as Network>::TransactionRequest: FoundryTransactionBuilder<FEN::Network>,
318 {
319 let figment = self.rpc.clone().into_figment(self.with_local_artifacts).merge(&self);
320 let evm_opts = figment.extract::<EvmOpts>()?;
321 let mut config = Config::from_provider(figment)?.sanitized();
322 let state_overrides = self.get_state_overrides()?;
323 let block_overrides = self.get_block_overrides()?;
324
325 let Self {
326 to,
327 mut sig,
328 mut args,
329 mut tx,
330 command,
331 block,
332 trace,
333 evm_version,
334 debug,
335 decode_internal,
336 labels,
337 data,
338 with_local_artifacts,
339 disable_labels,
340 wallet,
341 ..
342 } = self;
343
344 if let Some(data) = data {
345 sig = Some(data);
346 }
347
348 let machine_mode = foundry_cli::is_machine();
351 let provider =
352 match ProviderBuilder::<FEN::Network>::from_config(&config).and_then(|b| b.build()) {
353 Ok(p) => p,
354 Err(err) if machine_mode => rpc_failure(&err),
355 Err(err) => return Err(err),
356 };
357 let sender = SenderKind::from_wallet_opts(wallet).await?;
358 let from = sender.address();
359
360 let code = if let Some(CallSubcommands::Create {
361 code,
362 sig: create_sig,
363 args: create_args,
364 value,
365 }) = command
366 {
367 sig = create_sig;
368 args = create_args;
369 if let Some(value) = value {
370 tx.value = Some(value);
371 }
372 Some(code)
373 } else {
374 None
375 };
376
377 let builder_result: Result<_> = async {
380 CastTxBuilder::new(&provider, tx, &config)
381 .await?
382 .with_to(to)
383 .await?
384 .with_code_sig_and_args(code, sig, args)
385 .await?
386 .raw()
387 .build(sender)
388 .await
389 }
390 .await;
391 let (tx, func) = match builder_result {
392 Ok(v) => v,
393 Err(err) if machine_mode => rpc_failure(&err),
394 Err(err) => return Err(err),
395 };
396
397 if trace {
398 if let Some(BlockId::Number(BlockNumberOrTag::Number(block_number))) = self.block {
399 config.fork_block_number = Some(block_number);
401 }
402
403 let create2_deployer = evm_opts.create2_deployer;
404 let (mut evm_env, tx_env, fork, chain, networks) =
405 TracingExecutor::<FEN>::get_fork_material(&mut config, evm_opts).await?;
406
407 evm_env.cfg_env.disable_block_gas_limit = true;
409 evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX);
410 evm_env.block_env.set_gas_limit(u64::MAX);
411
412 if let Some(block_overrides) = block_overrides {
414 if let Some(number) = block_overrides.number {
415 evm_env.block_env.set_number(number.to());
416 }
417 if let Some(time) = block_overrides.time {
418 evm_env.block_env.set_timestamp(U256::from(time));
419 }
420 }
421
422 let trace_mode = TraceMode::Call
423 .with_debug(debug)
424 .with_decode_internal(if decode_internal {
425 InternalTraceMode::Full
426 } else {
427 InternalTraceMode::None
428 })
429 .with_state_changes(shell::verbosity() > 4);
430 let mut executor = TracingExecutor::<FEN>::new(
431 (evm_env, tx_env),
432 fork,
433 evm_version,
434 trace_mode,
435 networks,
436 create2_deployer,
437 state_overrides,
438 )?;
439
440 let value = tx.value().unwrap_or_default();
441 let input = tx.input().cloned().unwrap_or_default();
442 let tx_kind = tx.kind().expect("set by builder");
443 let env_tx = executor.tx_env_mut();
444
445 if let Some(gas_limit) = tx.gas_limit() {
447 env_tx.set_gas_limit(gas_limit);
448 }
449
450 if let Some(gas_price) = tx.gas_price() {
451 env_tx.set_gas_price(gas_price);
452 }
453
454 if let Some(max_fee_per_gas) = tx.max_fee_per_gas() {
455 env_tx.set_gas_price(max_fee_per_gas);
456 }
457
458 if let Some(max_priority_fee_per_gas) = tx.max_priority_fee_per_gas() {
459 env_tx.set_gas_priority_fee(Some(max_priority_fee_per_gas));
460 }
461
462 if let Some(max_fee_per_blob_gas) = tx.max_fee_per_blob_gas() {
463 env_tx.set_max_fee_per_blob_gas(max_fee_per_blob_gas);
464 }
465
466 if let Some(nonce) = tx.nonce() {
467 env_tx.set_nonce(nonce);
468 }
469
470 env_tx.set_tx_type(tx.output_tx_type().into());
471
472 if let Some(access_list) = tx.access_list().cloned() {
473 env_tx.set_access_list(access_list);
474 }
475
476 if let Some(auth) = tx.authorization_list().cloned() {
477 env_tx.set_signed_authorization(auth);
478 }
479
480 let trace = match tx_kind {
481 TxKind::Create => {
482 let deploy_result = executor.deploy(from, input, value, None);
483 TraceResult::try_from(deploy_result)?
484 }
485 TxKind::Call(to) => TraceResult::from_raw(
486 executor.transact_raw(from, to, input, value)?,
487 TraceKind::Execution,
488 ),
489 };
490
491 let contracts_bytecode = fetch_contracts_bytecode_from_trace(&executor, &trace)?;
492 handle_traces(
493 trace,
494 &config,
495 chain,
496 &contracts_bytecode,
497 labels,
498 with_local_artifacts,
499 debug,
500 decode_internal,
501 disable_labels,
502 None,
503 None,
504 )
505 .await?;
506
507 return Ok(());
508 }
509
510 let decode_func = (!machine_mode).then_some(func.as_ref()).flatten();
513 let response = match Cast::new(&provider)
514 .call(&tx, decode_func, block, state_overrides, block_overrides)
515 .await
516 {
517 Ok(r) => r,
518 Err(err) if machine_mode => rpc_failure(&err),
519 Err(err) => return Err(err),
520 };
521
522 if !machine_mode
524 && response == "0x"
525 && let Some(contract_address) = tx.to()
526 {
527 let code = provider.get_code_at(contract_address).await?;
528 if code.is_empty() {
529 sh_warn!("Contract code is empty")?;
530 }
531 }
532
533 if machine_mode {
534 print_json(&JsonEnvelope::success(CallData { raw: response }))?;
535 } else {
536 sh_println!("{}", response)?;
537 }
538
539 Ok(())
540 }
541
542 async fn run_curl(self) -> Result<()> {
544 let config = self.rpc.load_config()?;
545 let url = config.get_rpc_url_or_localhost_http()?;
546 let jwt = config.get_rpc_jwt_secret()?;
547
548 let data = if let Some(data) = &self.data {
550 hex::decode(data)?
551 } else if let Some(sig) = &self.sig {
552 if let Ok(data) = hex::decode(sig) {
554 data
555 } else {
556 let func = get_func(sig)?;
558 encode_function_args(&func, &self.args)?
559 }
560 } else {
561 Vec::new()
562 };
563
564 let to = self.to.as_ref().map(|n| match n {
566 NameOrAddress::Address(addr) => Ok(*addr),
567 NameOrAddress::Name(name) => {
568 eyre::bail!("ENS names are not supported with --curl. Please use a raw address instead of '{}'", name)
569 }
570 }).transpose()?;
571
572 let call_object = serde_json::json!({
574 "to": to,
575 "data": format!("0x{}", hex::encode(&data)),
576 });
577
578 let block_param = self
579 .block
580 .map(|b| serde_json::to_value(b).unwrap_or(serde_json::json!("latest")))
581 .unwrap_or(serde_json::json!("latest"));
582
583 let params = serde_json::json!([call_object, block_param]);
584
585 let curl_cmd = generate_curl_command(
586 url.as_ref(),
587 "eth_call",
588 params,
589 config.eth_rpc_headers.as_deref(),
590 jwt.as_deref(),
591 );
592
593 sh_println!("{}", curl_cmd)?;
594 Ok(())
595 }
596
597 pub fn get_state_overrides(&self) -> eyre::Result<Option<StateOverride>> {
599 if [
601 self.balance_overrides.as_ref(),
602 self.nonce_overrides.as_ref(),
603 self.code_overrides.as_ref(),
604 self.state_overrides.as_ref(),
605 self.state_diff_overrides.as_ref(),
606 ]
607 .iter()
608 .all(Option::is_none)
609 {
610 return Ok(None);
611 }
612
613 let mut state_overrides_builder = StateOverridesBuilder::default();
614
615 for override_str in self.balance_overrides.iter().flatten() {
617 let (addr, balance) = address_value_override(override_str)?;
618 state_overrides_builder =
619 state_overrides_builder.with_balance(addr.parse()?, balance.parse()?);
620 }
621
622 for override_str in self.nonce_overrides.iter().flatten() {
624 let (addr, nonce) = address_value_override(override_str)?;
625 state_overrides_builder =
626 state_overrides_builder.with_nonce(addr.parse()?, nonce.parse()?);
627 }
628
629 for override_str in self.code_overrides.iter().flatten() {
631 let (addr, code_str) = address_value_override(override_str)?;
632 state_overrides_builder =
633 state_overrides_builder.with_code(addr.parse()?, Bytes::from_str(code_str)?);
634 }
635
636 type StateOverrides = HashMap<Address, HashMap<B256, B256>>;
637 let parse_state_overrides =
638 |overrides: &Option<Vec<String>>| -> Result<StateOverrides, eyre::Report> {
639 let mut state_overrides: StateOverrides = StateOverrides::default();
640
641 overrides.iter().flatten().try_for_each(|s| -> Result<(), eyre::Report> {
642 let (addr, slot, value) = address_slot_value_override(s)?;
643 state_overrides.entry(addr).or_default().insert(slot.into(), value.into());
644 Ok(())
645 })?;
646
647 Ok(state_overrides)
648 };
649
650 for (addr, entries) in parse_state_overrides(&self.state_overrides)? {
652 state_overrides_builder = state_overrides_builder.with_state(addr, entries);
653 }
654
655 for (addr, entries) in parse_state_overrides(&self.state_diff_overrides)? {
657 state_overrides_builder = state_overrides_builder.with_state_diff(addr, entries)
658 }
659
660 Ok(Some(state_overrides_builder.build()))
661 }
662
663 pub fn get_block_overrides(&self) -> eyre::Result<Option<BlockOverrides>> {
665 let mut overrides = BlockOverrides::default();
666 if let Some(number) = self.block_number {
667 overrides = overrides.with_number(U256::from(number));
668 }
669 if let Some(time) = self.block_time {
670 overrides = overrides.with_time(time);
671 }
672 if overrides.is_empty() { Ok(None) } else { Ok(Some(overrides)) }
673 }
674}
675
676impl figment::Provider for CallArgs {
677 fn metadata(&self) -> Metadata {
678 Metadata::named("CallArgs")
679 }
680
681 fn data(&self) -> Result<Map<Profile, Dict>, figment::Error> {
682 let mut map = Map::new();
683
684 if let Some(evm_version) = self.evm_version {
685 map.insert("evm_version".into(), figment::value::Value::serialize(evm_version)?);
686 }
687
688 Ok(Map::from([(Config::selected_profile(), map)]))
689 }
690}
691
692fn address_value_override(address_override: &str) -> Result<(&str, &str)> {
694 address_override.split_once(':').ok_or_else(|| {
695 eyre::eyre!("Invalid override {address_override}. Expected <address>:<value>")
696 })
697}
698
699fn address_slot_value_override(address_override: &str) -> Result<(Address, U256, U256)> {
701 let captures = OVERRIDE_PATTERN.captures(address_override).ok_or_else(|| {
702 eyre::eyre!("Invalid override {address_override}. Expected <address>:<slot>:<value>")
703 })?;
704
705 Ok((
706 captures[1].parse()?, captures[2].parse()?, captures[3].parse()?, ))
710}
711
712#[cfg(test)]
713mod tests {
714 use super::*;
715 use alloy_primitives::{U64, address, b256, fixed_bytes};
716
717 #[test]
718 fn test_get_state_overrides() {
719 let call_args = CallArgs::parse_from([
720 "foundry-cli",
721 "--override-balance",
722 "0x0000000000000000000000000000000000000001:2",
723 "--override-nonce",
724 "0x0000000000000000000000000000000000000001:3",
725 "--override-code",
726 "0x0000000000000000000000000000000000000001:0x04",
727 "--override-state",
728 "0x0000000000000000000000000000000000000001:5:6",
729 "--override-state-diff",
730 "0x0000000000000000000000000000000000000001:7:8",
731 ]);
732 let overrides = call_args.get_state_overrides().unwrap().unwrap();
733 let address = address!("0x0000000000000000000000000000000000000001");
734 if let Some(account_override) = overrides.get(&address) {
735 if let Some(balance) = account_override.balance {
736 assert_eq!(balance, U256::from(2));
737 }
738 if let Some(nonce) = account_override.nonce {
739 assert_eq!(nonce, 3);
740 }
741 if let Some(code) = &account_override.code {
742 assert_eq!(*code, Bytes::from([0x04]));
743 }
744 if let Some(state) = &account_override.state
745 && let Some(value) = state.get(&b256!(
746 "0x0000000000000000000000000000000000000000000000000000000000000005"
747 ))
748 {
749 assert_eq!(
750 *value,
751 b256!("0x0000000000000000000000000000000000000000000000000000000000000006")
752 );
753 }
754 if let Some(state_diff) = &account_override.state_diff
755 && let Some(value) = state_diff.get(&b256!(
756 "0x0000000000000000000000000000000000000000000000000000000000000007"
757 ))
758 {
759 assert_eq!(
760 *value,
761 b256!("0x0000000000000000000000000000000000000000000000000000000000000008")
762 );
763 }
764 }
765 }
766
767 #[test]
768 fn test_get_state_overrides_empty() {
769 let call_args = CallArgs::parse_from([""]);
770 let overrides = call_args.get_state_overrides().unwrap();
771 assert_eq!(overrides, None);
772 }
773
774 #[test]
775 fn test_get_block_overrides() {
776 let mut call_args = CallArgs::parse_from([""]);
777 call_args.block_number = Some(1);
778 call_args.block_time = Some(2);
779 let overrides = call_args.get_block_overrides().unwrap().unwrap();
780 assert_eq!(overrides.number, Some(U256::from(1)));
781 assert_eq!(overrides.time, Some(2));
782 }
783
784 #[test]
785 fn test_get_block_overrides_empty() {
786 let call_args = CallArgs::parse_from([""]);
787 let overrides = call_args.get_block_overrides().unwrap();
788 assert_eq!(overrides, None);
789 }
790
791 #[test]
792 fn test_address_value_override_success() {
793 let text = "0x0000000000000000000000000000000000000001:2";
794 let (address, value) = address_value_override(text).unwrap();
795 assert_eq!(address, "0x0000000000000000000000000000000000000001");
796 assert_eq!(value, "2");
797 }
798
799 #[test]
800 fn test_address_value_override_error() {
801 let text = "invalid_value";
802 let error = address_value_override(text).unwrap_err();
803 assert_eq!(error.to_string(), "Invalid override invalid_value. Expected <address>:<value>");
804 }
805
806 #[test]
807 fn test_address_slot_value_override_success() {
808 let text = "0x0000000000000000000000000000000000000001:2:3";
809 let (address, slot, value) = address_slot_value_override(text).unwrap();
810 assert_eq!(*address, fixed_bytes!("0x0000000000000000000000000000000000000001"));
811 assert_eq!(slot, U256::from(2));
812 assert_eq!(value, U256::from(3));
813 }
814
815 #[test]
816 fn test_address_slot_value_override_error() {
817 let text = "invalid_value";
818 let error = address_slot_value_override(text).unwrap_err();
819 assert_eq!(
820 error.to_string(),
821 "Invalid override invalid_value. Expected <address>:<slot>:<value>"
822 );
823 }
824
825 #[test]
826 fn can_parse_call_data() {
827 let data = hex::encode("hello");
828 let args = CallArgs::parse_from(["foundry-cli", "--data", data.as_str()]);
829 assert_eq!(args.data, Some(data));
830
831 let data = hex::encode_prefixed("hello");
832 let args = CallArgs::parse_from(["foundry-cli", "--data", data.as_str()]);
833 assert_eq!(args.data, Some(data));
834 }
835
836 #[test]
837 fn can_parse_state_overrides() {
838 let args = CallArgs::parse_from([
839 "foundry-cli",
840 "--override-balance",
841 "0x123:0x1234",
842 "--override-nonce",
843 "0x123:1",
844 "--override-code",
845 "0x123:0x1234",
846 "--override-state",
847 "0x123:0x1:0x1234",
848 ]);
849
850 assert_eq!(args.balance_overrides, Some(vec!["0x123:0x1234".to_string()]));
851 assert_eq!(args.nonce_overrides, Some(vec!["0x123:1".to_string()]));
852 assert_eq!(args.code_overrides, Some(vec!["0x123:0x1234".to_string()]));
853 assert_eq!(args.state_overrides, Some(vec!["0x123:0x1:0x1234".to_string()]));
854 }
855
856 #[test]
857 fn can_parse_multiple_state_overrides() {
858 let args = CallArgs::parse_from([
859 "foundry-cli",
860 "--override-balance",
861 "0x123:0x1234",
862 "--override-balance",
863 "0x456:0x5678",
864 "--override-nonce",
865 "0x123:1",
866 "--override-nonce",
867 "0x456:2",
868 "--override-code",
869 "0x123:0x1234",
870 "--override-code",
871 "0x456:0x5678",
872 "--override-state",
873 "0x123:0x1:0x1234",
874 "--override-state",
875 "0x456:0x2:0x5678",
876 ]);
877
878 assert_eq!(
879 args.balance_overrides,
880 Some(vec!["0x123:0x1234".to_string(), "0x456:0x5678".to_string()])
881 );
882 assert_eq!(args.nonce_overrides, Some(vec!["0x123:1".to_string(), "0x456:2".to_string()]));
883 assert_eq!(
884 args.code_overrides,
885 Some(vec!["0x123:0x1234".to_string(), "0x456:0x5678".to_string()])
886 );
887 assert_eq!(
888 args.state_overrides,
889 Some(vec!["0x123:0x1:0x1234".to_string(), "0x456:0x2:0x5678".to_string()])
890 );
891 }
892
893 #[test]
894 fn test_negative_args_with_flags() {
895 let args = CallArgs::parse_from([
897 "foundry-cli",
898 "--trace",
899 "0xDeaDBeeFcAfEbAbEfAcEfEeDcBaDbEeFcAfEbAbE",
900 "process(int256)",
901 "-999999",
902 "--debug",
903 ]);
904
905 assert!(args.trace);
906 assert!(args.debug);
907 assert_eq!(args.args, vec!["-999999"]);
908 }
909
910 #[test]
911 fn test_transaction_opts_with_trace() {
912 let args = CallArgs::parse_from([
914 "foundry-cli",
915 "--trace",
916 "--gas-limit",
917 "1000000",
918 "--gas-price",
919 "20000000000",
920 "--priority-gas-price",
921 "2000000000",
922 "--nonce",
923 "42",
924 "--value",
925 "1000000000000000000", "--blob-gas-price",
927 "10000000000",
928 "0xDeaDBeeFcAfEbAbEfAcEfEeDcBaDbEeFcAfEbAbE",
929 "balanceOf(address)",
930 "0x123456789abcdef123456789abcdef123456789a",
931 ]);
932
933 assert!(args.trace);
934 assert_eq!(args.tx.gas_limit, Some(U256::from(1000000u32)));
935 assert_eq!(args.tx.gas_price, Some(U256::from(20000000000u64)));
936 assert_eq!(args.tx.priority_gas_price, Some(U256::from(2000000000u64)));
937 assert_eq!(args.tx.nonce, Some(U64::from(42)));
938 assert_eq!(args.tx.value, Some(U256::from(1000000000000000000u64)));
939 assert_eq!(args.tx.blob_gas_price, Some(U256::from(10000000000u64)));
940 }
941}