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, hex, 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::{ChainValueParser, RpcOpts, TransactionOpts},
19 utils::{LoadConfig, TraceResult, get_provider_with_curl, parse_ether_value},
20};
21use foundry_common::{
22 abi::{encode_function_args, get_func},
23 provider::curl_transport::generate_curl_command,
24 sh_println, shell,
25};
26use foundry_compilers::artifacts::EvmVersion;
27use foundry_config::{
28 Chain, Config,
29 figment::{
30 self, Metadata, Profile,
31 value::{Dict, Map},
32 },
33};
34use foundry_evm::{
35 executors::TracingExecutor,
36 opts::EvmOpts,
37 traces::{InternalTraceMode, TraceMode},
38};
39use foundry_wallets::WalletOpts;
40use itertools::Either;
41use regex::Regex;
42use revm::context::TransactionType;
43use std::{str::FromStr, sync::LazyLock};
44
45static OVERRIDE_PATTERN: LazyLock<Regex> =
48 LazyLock::new(|| Regex::new(r"^([^:]+):([^:]+):([^:]+)$").unwrap());
49
50#[derive(Debug, Parser)]
72pub struct CallArgs {
73 #[arg(value_parser = NameOrAddress::from_str)]
75 to: Option<NameOrAddress>,
76
77 sig: Option<String>,
79
80 #[arg(allow_negative_numbers = true)]
82 args: Vec<String>,
83
84 #[arg(
86 long,
87 conflicts_with_all = &["sig", "args"]
88 )]
89 data: Option<String>,
90
91 #[arg(long, default_value_t = false)]
93 trace: bool,
94
95 #[arg(long, default_value_t = false, requires = "trace")]
98 disable_labels: bool,
99
100 #[arg(long, requires = "trace")]
103 debug: bool,
104
105 #[arg(long, requires = "trace")]
112 decode_internal: bool,
113
114 #[arg(long, requires = "trace")]
117 labels: Vec<String>,
118
119 #[arg(long, requires = "trace")]
122 evm_version: Option<EvmVersion>,
123
124 #[arg(long, short)]
128 block: Option<BlockId>,
129
130 #[command(subcommand)]
131 command: Option<CallSubcommands>,
132
133 #[command(flatten)]
134 tx: TransactionOpts,
135
136 #[command(flatten)]
137 rpc: RpcOpts,
138
139 #[command(flatten)]
140 wallet: WalletOpts,
141
142 #[arg(
143 short,
144 long,
145 alias = "chain-id",
146 env = "CHAIN",
147 value_parser = ChainValueParser::default(),
148 )]
149 pub chain: Option<Chain>,
150
151 #[arg(long, visible_alias = "la")]
153 pub with_local_artifacts: bool,
154
155 #[arg(long = "override-balance", value_name = "ADDRESS:BALANCE", value_delimiter = ',')]
158 pub balance_overrides: Option<Vec<String>>,
159
160 #[arg(long = "override-nonce", value_name = "ADDRESS:NONCE", value_delimiter = ',')]
163 pub nonce_overrides: Option<Vec<String>>,
164
165 #[arg(long = "override-code", value_name = "ADDRESS:CODE", value_delimiter = ',')]
168 pub code_overrides: Option<Vec<String>>,
169
170 #[arg(long = "override-state", value_name = "ADDRESS:SLOT:VALUE", value_delimiter = ',')]
173 pub state_overrides: Option<Vec<String>>,
174
175 #[arg(long = "override-state-diff", value_name = "ADDRESS:SLOT:VALUE", value_delimiter = ',')]
178 pub state_diff_overrides: Option<Vec<String>>,
179
180 #[arg(long = "block.time", value_name = "TIME")]
182 pub block_time: Option<u64>,
183
184 #[arg(long = "block.number", value_name = "NUMBER")]
186 pub block_number: Option<u64>,
187}
188
189#[derive(Debug, Parser)]
190pub enum CallSubcommands {
191 #[command(name = "--create")]
193 Create {
194 code: String,
196
197 sig: Option<String>,
199
200 #[arg(allow_negative_numbers = true)]
202 args: Vec<String>,
203
204 #[arg(long, value_parser = parse_ether_value)]
210 value: Option<U256>,
211 },
212}
213
214impl CallArgs {
215 pub async fn run(self) -> Result<()> {
216 if self.rpc.curl {
218 return self.run_curl().await;
219 }
220
221 let figment = self.rpc.clone().into_figment(self.with_local_artifacts).merge(&self);
222 let evm_opts = figment.extract::<EvmOpts>()?;
223 let mut config = Config::from_provider(figment)?.sanitized();
224 let state_overrides = self.get_state_overrides()?;
225 let block_overrides = self.get_block_overrides()?;
226
227 let Self {
228 to,
229 mut sig,
230 mut args,
231 mut tx,
232 command,
233 block,
234 trace,
235 evm_version,
236 debug,
237 decode_internal,
238 labels,
239 data,
240 with_local_artifacts,
241 disable_labels,
242 wallet,
243 ..
244 } = self;
245
246 if let Some(data) = data {
247 sig = Some(data);
248 }
249
250 let provider = get_provider_with_curl(&config, false)?;
251 let sender = SenderKind::from_wallet_opts(wallet).await?;
252 let from = sender.address();
253
254 let code = if let Some(CallSubcommands::Create {
255 code,
256 sig: create_sig,
257 args: create_args,
258 value,
259 }) = command
260 {
261 sig = create_sig;
262 args = create_args;
263 if let Some(value) = value {
264 tx.value = Some(value);
265 }
266 Some(code)
267 } else {
268 None
269 };
270
271 let (tx, func) = CastTxBuilder::new(&provider, tx, &config)
272 .await?
273 .with_to(to)
274 .await?
275 .with_code_sig_and_args(code, sig, args)
276 .await?
277 .build_raw(sender)
278 .await?;
279
280 if trace {
281 if let Some(BlockId::Number(BlockNumberOrTag::Number(block_number))) = self.block {
282 config.fork_block_number = Some(block_number);
284 }
285
286 let create2_deployer = evm_opts.create2_deployer;
287 let (mut env, fork, chain, networks) =
288 TracingExecutor::get_fork_material(&mut config, evm_opts).await?;
289
290 env.evm_env.cfg_env.disable_block_gas_limit = true;
292 env.evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX);
293 env.evm_env.block_env.gas_limit = u64::MAX;
294
295 if let Some(block_overrides) = block_overrides {
297 if let Some(number) = block_overrides.number {
298 env.evm_env.block_env.number = number.to();
299 }
300 if let Some(time) = block_overrides.time {
301 env.evm_env.block_env.timestamp = U256::from(time);
302 }
303 }
304
305 let trace_mode = TraceMode::Call
306 .with_debug(debug)
307 .with_decode_internal(if decode_internal {
308 InternalTraceMode::Full
309 } else {
310 InternalTraceMode::None
311 })
312 .with_state_changes(shell::verbosity() > 4);
313 let mut executor = TracingExecutor::new(
314 env,
315 fork,
316 evm_version,
317 trace_mode,
318 networks,
319 create2_deployer,
320 state_overrides,
321 )?;
322
323 let value = tx.value.unwrap_or_default();
324 let input = tx.inner.input.into_input().unwrap_or_default();
325 let tx_kind = tx.inner.to.expect("set by builder");
326 let env_tx = &mut executor.env_mut().tx;
327
328 if let Some(gas_limit) = tx.inner.gas {
330 env_tx.gas_limit = gas_limit;
331 }
332
333 if let Some(gas_price) = tx.inner.gas_price {
334 env_tx.gas_price = gas_price;
335 }
336
337 if let Some(max_fee_per_gas) = tx.inner.max_fee_per_gas {
338 env_tx.gas_price = max_fee_per_gas;
339 }
340
341 if let Some(max_priority_fee_per_gas) = tx.inner.max_priority_fee_per_gas {
342 env_tx.gas_priority_fee = Some(max_priority_fee_per_gas);
343 }
344
345 if let Some(max_fee_per_blob_gas) = tx.inner.max_fee_per_blob_gas {
346 env_tx.max_fee_per_blob_gas = max_fee_per_blob_gas;
347 }
348
349 if let Some(nonce) = tx.inner.nonce {
350 env_tx.nonce = nonce;
351 }
352
353 if let Some(tx_type) = tx.inner.transaction_type {
354 env_tx.tx_type = tx_type;
355 }
356
357 if let Some(access_list) = tx.inner.access_list {
358 env_tx.access_list = access_list;
359
360 if env_tx.tx_type == TransactionType::Legacy as u8 {
361 env_tx.tx_type = TransactionType::Eip2930 as u8;
362 }
363 }
364
365 if let Some(auth) = tx.inner.authorization_list {
366 env_tx.authorization_list = auth.into_iter().map(Either::Left).collect();
367
368 env_tx.tx_type = TransactionType::Eip7702 as u8;
369 }
370
371 let trace = match tx_kind {
372 TxKind::Create => {
373 let deploy_result = executor.deploy(from, input, value, None);
374 TraceResult::try_from(deploy_result)?
375 }
376 TxKind::Call(to) => TraceResult::from_raw(
377 executor.transact_raw(from, to, input, value)?,
378 TraceKind::Execution,
379 ),
380 };
381
382 let contracts_bytecode = fetch_contracts_bytecode_from_trace(&executor, &trace)?;
383 handle_traces(
384 trace,
385 &config,
386 chain,
387 &contracts_bytecode,
388 labels,
389 with_local_artifacts,
390 debug,
391 decode_internal,
392 disable_labels,
393 None,
394 )
395 .await?;
396
397 return Ok(());
398 }
399
400 let response = Cast::new(&provider)
401 .call(&tx, func.as_ref(), block, state_overrides, block_overrides)
402 .await?;
403
404 if response == "0x"
405 && let Some(contract_address) = tx.to.and_then(|tx_kind| tx_kind.into_to())
406 {
407 let code = provider.get_code_at(contract_address).await?;
408 if code.is_empty() {
409 sh_warn!("Contract code is empty")?;
410 }
411 }
412 sh_println!("{}", response)?;
413
414 Ok(())
415 }
416
417 async fn run_curl(self) -> Result<()> {
419 let config = self.rpc.load_config()?;
420 let url = config.get_rpc_url_or_localhost_http()?;
421 let jwt = config.get_rpc_jwt_secret()?;
422
423 let data = if let Some(data) = &self.data {
425 hex::decode(data)?
426 } else if let Some(sig) = &self.sig {
427 if let Ok(data) = hex::decode(sig) {
429 data
430 } else {
431 let func = get_func(sig)?;
433 encode_function_args(&func, &self.args)?
434 }
435 } else {
436 Vec::new()
437 };
438
439 let to = self.to.as_ref().map(|n| match n {
441 NameOrAddress::Address(addr) => Ok(*addr),
442 NameOrAddress::Name(name) => {
443 eyre::bail!("ENS names are not supported with --curl. Please use a raw address instead of '{}'", name)
444 }
445 }).transpose()?;
446
447 let call_object = serde_json::json!({
449 "to": to,
450 "data": format!("0x{}", hex::encode(&data)),
451 });
452
453 let block_param = self
454 .block
455 .map(|b| serde_json::to_value(b).unwrap_or(serde_json::json!("latest")))
456 .unwrap_or(serde_json::json!("latest"));
457
458 let params = serde_json::json!([call_object, block_param]);
459
460 let curl_cmd = generate_curl_command(
461 url.as_ref(),
462 "eth_call",
463 params,
464 config.eth_rpc_headers.as_deref(),
465 jwt.as_deref(),
466 );
467
468 sh_println!("{}", curl_cmd)?;
469 Ok(())
470 }
471
472 pub fn get_state_overrides(&self) -> eyre::Result<Option<StateOverride>> {
474 if [
476 self.balance_overrides.as_ref(),
477 self.nonce_overrides.as_ref(),
478 self.code_overrides.as_ref(),
479 self.state_overrides.as_ref(),
480 self.state_diff_overrides.as_ref(),
481 ]
482 .iter()
483 .all(Option::is_none)
484 {
485 return Ok(None);
486 }
487
488 let mut state_overrides_builder = StateOverridesBuilder::default();
489
490 for override_str in self.balance_overrides.iter().flatten() {
492 let (addr, balance) = address_value_override(override_str)?;
493 state_overrides_builder =
494 state_overrides_builder.with_balance(addr.parse()?, balance.parse()?);
495 }
496
497 for override_str in self.nonce_overrides.iter().flatten() {
499 let (addr, nonce) = address_value_override(override_str)?;
500 state_overrides_builder =
501 state_overrides_builder.with_nonce(addr.parse()?, nonce.parse()?);
502 }
503
504 for override_str in self.code_overrides.iter().flatten() {
506 let (addr, code_str) = address_value_override(override_str)?;
507 state_overrides_builder =
508 state_overrides_builder.with_code(addr.parse()?, Bytes::from_str(code_str)?);
509 }
510
511 type StateOverrides = HashMap<Address, HashMap<B256, B256>>;
512 let parse_state_overrides =
513 |overrides: &Option<Vec<String>>| -> Result<StateOverrides, eyre::Report> {
514 let mut state_overrides: StateOverrides = StateOverrides::default();
515
516 overrides.iter().flatten().try_for_each(|s| -> Result<(), eyre::Report> {
517 let (addr, slot, value) = address_slot_value_override(s)?;
518 state_overrides.entry(addr).or_default().insert(slot.into(), value.into());
519 Ok(())
520 })?;
521
522 Ok(state_overrides)
523 };
524
525 for (addr, entries) in parse_state_overrides(&self.state_overrides)? {
527 state_overrides_builder = state_overrides_builder.with_state(addr, entries.into_iter());
528 }
529
530 for (addr, entries) in parse_state_overrides(&self.state_diff_overrides)? {
532 state_overrides_builder =
533 state_overrides_builder.with_state_diff(addr, entries.into_iter())
534 }
535
536 Ok(Some(state_overrides_builder.build()))
537 }
538
539 pub fn get_block_overrides(&self) -> eyre::Result<Option<BlockOverrides>> {
541 let mut overrides = BlockOverrides::default();
542 if let Some(number) = self.block_number {
543 overrides = overrides.with_number(U256::from(number));
544 }
545 if let Some(time) = self.block_time {
546 overrides = overrides.with_time(time);
547 }
548 if overrides.is_empty() { Ok(None) } else { Ok(Some(overrides)) }
549 }
550}
551
552impl figment::Provider for CallArgs {
553 fn metadata(&self) -> Metadata {
554 Metadata::named("CallArgs")
555 }
556
557 fn data(&self) -> Result<Map<Profile, Dict>, figment::Error> {
558 let mut map = Map::new();
559
560 if let Some(evm_version) = self.evm_version {
561 map.insert("evm_version".into(), figment::value::Value::serialize(evm_version)?);
562 }
563
564 Ok(Map::from([(Config::selected_profile(), map)]))
565 }
566}
567
568fn address_value_override(address_override: &str) -> Result<(&str, &str)> {
570 address_override.split_once(':').ok_or_else(|| {
571 eyre::eyre!("Invalid override {address_override}. Expected <address>:<value>")
572 })
573}
574
575fn address_slot_value_override(address_override: &str) -> Result<(Address, U256, U256)> {
577 let captures = OVERRIDE_PATTERN.captures(address_override).ok_or_else(|| {
578 eyre::eyre!("Invalid override {address_override}. Expected <address>:<slot>:<value>")
579 })?;
580
581 Ok((
582 captures[1].parse()?, captures[2].parse()?, captures[3].parse()?, ))
586}
587
588#[cfg(test)]
589mod tests {
590 use super::*;
591 use alloy_primitives::{U64, address, b256, fixed_bytes};
592
593 #[test]
594 fn test_get_state_overrides() {
595 let call_args = CallArgs::parse_from([
596 "foundry-cli",
597 "--override-balance",
598 "0x0000000000000000000000000000000000000001:2",
599 "--override-nonce",
600 "0x0000000000000000000000000000000000000001:3",
601 "--override-code",
602 "0x0000000000000000000000000000000000000001:0x04",
603 "--override-state",
604 "0x0000000000000000000000000000000000000001:5:6",
605 "--override-state-diff",
606 "0x0000000000000000000000000000000000000001:7:8",
607 ]);
608 let overrides = call_args.get_state_overrides().unwrap().unwrap();
609 let address = address!("0x0000000000000000000000000000000000000001");
610 if let Some(account_override) = overrides.get(&address) {
611 if let Some(balance) = account_override.balance {
612 assert_eq!(balance, U256::from(2));
613 }
614 if let Some(nonce) = account_override.nonce {
615 assert_eq!(nonce, 3);
616 }
617 if let Some(code) = &account_override.code {
618 assert_eq!(*code, Bytes::from([0x04]));
619 }
620 if let Some(state) = &account_override.state
621 && let Some(value) = state.get(&b256!(
622 "0x0000000000000000000000000000000000000000000000000000000000000005"
623 ))
624 {
625 assert_eq!(
626 *value,
627 b256!("0x0000000000000000000000000000000000000000000000000000000000000006")
628 );
629 }
630 if let Some(state_diff) = &account_override.state_diff
631 && let Some(value) = state_diff.get(&b256!(
632 "0x0000000000000000000000000000000000000000000000000000000000000007"
633 ))
634 {
635 assert_eq!(
636 *value,
637 b256!("0x0000000000000000000000000000000000000000000000000000000000000008")
638 );
639 }
640 }
641 }
642
643 #[test]
644 fn test_get_state_overrides_empty() {
645 let call_args = CallArgs::parse_from([""]);
646 let overrides = call_args.get_state_overrides().unwrap();
647 assert_eq!(overrides, None);
648 }
649
650 #[test]
651 fn test_get_block_overrides() {
652 let mut call_args = CallArgs::parse_from([""]);
653 call_args.block_number = Some(1);
654 call_args.block_time = Some(2);
655 let overrides = call_args.get_block_overrides().unwrap().unwrap();
656 assert_eq!(overrides.number, Some(U256::from(1)));
657 assert_eq!(overrides.time, Some(2));
658 }
659
660 #[test]
661 fn test_get_block_overrides_empty() {
662 let call_args = CallArgs::parse_from([""]);
663 let overrides = call_args.get_block_overrides().unwrap();
664 assert_eq!(overrides, None);
665 }
666
667 #[test]
668 fn test_address_value_override_success() {
669 let text = "0x0000000000000000000000000000000000000001:2";
670 let (address, value) = address_value_override(text).unwrap();
671 assert_eq!(address, "0x0000000000000000000000000000000000000001");
672 assert_eq!(value, "2");
673 }
674
675 #[test]
676 fn test_address_value_override_error() {
677 let text = "invalid_value";
678 let error = address_value_override(text).unwrap_err();
679 assert_eq!(error.to_string(), "Invalid override invalid_value. Expected <address>:<value>");
680 }
681
682 #[test]
683 fn test_address_slot_value_override_success() {
684 let text = "0x0000000000000000000000000000000000000001:2:3";
685 let (address, slot, value) = address_slot_value_override(text).unwrap();
686 assert_eq!(*address, fixed_bytes!("0x0000000000000000000000000000000000000001"));
687 assert_eq!(slot, U256::from(2));
688 assert_eq!(value, U256::from(3));
689 }
690
691 #[test]
692 fn test_address_slot_value_override_error() {
693 let text = "invalid_value";
694 let error = address_slot_value_override(text).unwrap_err();
695 assert_eq!(
696 error.to_string(),
697 "Invalid override invalid_value. Expected <address>:<slot>:<value>"
698 );
699 }
700
701 #[test]
702 fn can_parse_call_data() {
703 let data = hex::encode("hello");
704 let args = CallArgs::parse_from(["foundry-cli", "--data", data.as_str()]);
705 assert_eq!(args.data, Some(data));
706
707 let data = hex::encode_prefixed("hello");
708 let args = CallArgs::parse_from(["foundry-cli", "--data", data.as_str()]);
709 assert_eq!(args.data, Some(data));
710 }
711
712 #[test]
713 fn can_parse_state_overrides() {
714 let args = CallArgs::parse_from([
715 "foundry-cli",
716 "--override-balance",
717 "0x123:0x1234",
718 "--override-nonce",
719 "0x123:1",
720 "--override-code",
721 "0x123:0x1234",
722 "--override-state",
723 "0x123:0x1:0x1234",
724 ]);
725
726 assert_eq!(args.balance_overrides, Some(vec!["0x123:0x1234".to_string()]));
727 assert_eq!(args.nonce_overrides, Some(vec!["0x123:1".to_string()]));
728 assert_eq!(args.code_overrides, Some(vec!["0x123:0x1234".to_string()]));
729 assert_eq!(args.state_overrides, Some(vec!["0x123:0x1:0x1234".to_string()]));
730 }
731
732 #[test]
733 fn can_parse_multiple_state_overrides() {
734 let args = CallArgs::parse_from([
735 "foundry-cli",
736 "--override-balance",
737 "0x123:0x1234",
738 "--override-balance",
739 "0x456:0x5678",
740 "--override-nonce",
741 "0x123:1",
742 "--override-nonce",
743 "0x456:2",
744 "--override-code",
745 "0x123:0x1234",
746 "--override-code",
747 "0x456:0x5678",
748 "--override-state",
749 "0x123:0x1:0x1234",
750 "--override-state",
751 "0x456:0x2:0x5678",
752 ]);
753
754 assert_eq!(
755 args.balance_overrides,
756 Some(vec!["0x123:0x1234".to_string(), "0x456:0x5678".to_string()])
757 );
758 assert_eq!(args.nonce_overrides, Some(vec!["0x123:1".to_string(), "0x456:2".to_string()]));
759 assert_eq!(
760 args.code_overrides,
761 Some(vec!["0x123:0x1234".to_string(), "0x456:0x5678".to_string()])
762 );
763 assert_eq!(
764 args.state_overrides,
765 Some(vec!["0x123:0x1:0x1234".to_string(), "0x456:0x2:0x5678".to_string()])
766 );
767 }
768
769 #[test]
770 fn test_negative_args_with_flags() {
771 let args = CallArgs::parse_from([
773 "foundry-cli",
774 "--trace",
775 "0xDeaDBeeFcAfEbAbEfAcEfEeDcBaDbEeFcAfEbAbE",
776 "process(int256)",
777 "-999999",
778 "--debug",
779 ]);
780
781 assert!(args.trace);
782 assert!(args.debug);
783 assert_eq!(args.args, vec!["-999999"]);
784 }
785
786 #[test]
787 fn test_transaction_opts_with_trace() {
788 let args = CallArgs::parse_from([
790 "foundry-cli",
791 "--trace",
792 "--gas-limit",
793 "1000000",
794 "--gas-price",
795 "20000000000",
796 "--priority-gas-price",
797 "2000000000",
798 "--nonce",
799 "42",
800 "--value",
801 "1000000000000000000", "--blob-gas-price",
803 "10000000000",
804 "0xDeaDBeeFcAfEbAbEfAcEfEeDcBaDbEeFcAfEbAbE",
805 "balanceOf(address)",
806 "0x123456789abcdef123456789abcdef123456789a",
807 ]);
808
809 assert!(args.trace);
810 assert_eq!(args.tx.gas_limit, Some(U256::from(1000000u32)));
811 assert_eq!(args.tx.gas_price, Some(U256::from(20000000000u64)));
812 assert_eq!(args.tx.priority_gas_price, Some(U256::from(2000000000u64)));
813 assert_eq!(args.tx.nonce, Some(U64::from(42)));
814 assert_eq!(args.tx.value, Some(U256::from(1000000000000000000u64)));
815 assert_eq!(args.tx.blob_gas_price, Some(U256::from(10000000000u64)));
816 }
817}