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, 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(&config)?;
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);
528 }
529
530 for (addr, entries) in parse_state_overrides(&self.state_diff_overrides)? {
532 state_overrides_builder = state_overrides_builder.with_state_diff(addr, entries)
533 }
534
535 Ok(Some(state_overrides_builder.build()))
536 }
537
538 pub fn get_block_overrides(&self) -> eyre::Result<Option<BlockOverrides>> {
540 let mut overrides = BlockOverrides::default();
541 if let Some(number) = self.block_number {
542 overrides = overrides.with_number(U256::from(number));
543 }
544 if let Some(time) = self.block_time {
545 overrides = overrides.with_time(time);
546 }
547 if overrides.is_empty() { Ok(None) } else { Ok(Some(overrides)) }
548 }
549}
550
551impl figment::Provider for CallArgs {
552 fn metadata(&self) -> Metadata {
553 Metadata::named("CallArgs")
554 }
555
556 fn data(&self) -> Result<Map<Profile, Dict>, figment::Error> {
557 let mut map = Map::new();
558
559 if let Some(evm_version) = self.evm_version {
560 map.insert("evm_version".into(), figment::value::Value::serialize(evm_version)?);
561 }
562
563 Ok(Map::from([(Config::selected_profile(), map)]))
564 }
565}
566
567fn address_value_override(address_override: &str) -> Result<(&str, &str)> {
569 address_override.split_once(':').ok_or_else(|| {
570 eyre::eyre!("Invalid override {address_override}. Expected <address>:<value>")
571 })
572}
573
574fn address_slot_value_override(address_override: &str) -> Result<(Address, U256, U256)> {
576 let captures = OVERRIDE_PATTERN.captures(address_override).ok_or_else(|| {
577 eyre::eyre!("Invalid override {address_override}. Expected <address>:<slot>:<value>")
578 })?;
579
580 Ok((
581 captures[1].parse()?, captures[2].parse()?, captures[3].parse()?, ))
585}
586
587#[cfg(test)]
588mod tests {
589 use super::*;
590 use alloy_primitives::{U64, address, b256, fixed_bytes};
591
592 #[test]
593 fn test_get_state_overrides() {
594 let call_args = CallArgs::parse_from([
595 "foundry-cli",
596 "--override-balance",
597 "0x0000000000000000000000000000000000000001:2",
598 "--override-nonce",
599 "0x0000000000000000000000000000000000000001:3",
600 "--override-code",
601 "0x0000000000000000000000000000000000000001:0x04",
602 "--override-state",
603 "0x0000000000000000000000000000000000000001:5:6",
604 "--override-state-diff",
605 "0x0000000000000000000000000000000000000001:7:8",
606 ]);
607 let overrides = call_args.get_state_overrides().unwrap().unwrap();
608 let address = address!("0x0000000000000000000000000000000000000001");
609 if let Some(account_override) = overrides.get(&address) {
610 if let Some(balance) = account_override.balance {
611 assert_eq!(balance, U256::from(2));
612 }
613 if let Some(nonce) = account_override.nonce {
614 assert_eq!(nonce, 3);
615 }
616 if let Some(code) = &account_override.code {
617 assert_eq!(*code, Bytes::from([0x04]));
618 }
619 if let Some(state) = &account_override.state
620 && let Some(value) = state.get(&b256!(
621 "0x0000000000000000000000000000000000000000000000000000000000000005"
622 ))
623 {
624 assert_eq!(
625 *value,
626 b256!("0x0000000000000000000000000000000000000000000000000000000000000006")
627 );
628 }
629 if let Some(state_diff) = &account_override.state_diff
630 && let Some(value) = state_diff.get(&b256!(
631 "0x0000000000000000000000000000000000000000000000000000000000000007"
632 ))
633 {
634 assert_eq!(
635 *value,
636 b256!("0x0000000000000000000000000000000000000000000000000000000000000008")
637 );
638 }
639 }
640 }
641
642 #[test]
643 fn test_get_state_overrides_empty() {
644 let call_args = CallArgs::parse_from([""]);
645 let overrides = call_args.get_state_overrides().unwrap();
646 assert_eq!(overrides, None);
647 }
648
649 #[test]
650 fn test_get_block_overrides() {
651 let mut call_args = CallArgs::parse_from([""]);
652 call_args.block_number = Some(1);
653 call_args.block_time = Some(2);
654 let overrides = call_args.get_block_overrides().unwrap().unwrap();
655 assert_eq!(overrides.number, Some(U256::from(1)));
656 assert_eq!(overrides.time, Some(2));
657 }
658
659 #[test]
660 fn test_get_block_overrides_empty() {
661 let call_args = CallArgs::parse_from([""]);
662 let overrides = call_args.get_block_overrides().unwrap();
663 assert_eq!(overrides, None);
664 }
665
666 #[test]
667 fn test_address_value_override_success() {
668 let text = "0x0000000000000000000000000000000000000001:2";
669 let (address, value) = address_value_override(text).unwrap();
670 assert_eq!(address, "0x0000000000000000000000000000000000000001");
671 assert_eq!(value, "2");
672 }
673
674 #[test]
675 fn test_address_value_override_error() {
676 let text = "invalid_value";
677 let error = address_value_override(text).unwrap_err();
678 assert_eq!(error.to_string(), "Invalid override invalid_value. Expected <address>:<value>");
679 }
680
681 #[test]
682 fn test_address_slot_value_override_success() {
683 let text = "0x0000000000000000000000000000000000000001:2:3";
684 let (address, slot, value) = address_slot_value_override(text).unwrap();
685 assert_eq!(*address, fixed_bytes!("0x0000000000000000000000000000000000000001"));
686 assert_eq!(slot, U256::from(2));
687 assert_eq!(value, U256::from(3));
688 }
689
690 #[test]
691 fn test_address_slot_value_override_error() {
692 let text = "invalid_value";
693 let error = address_slot_value_override(text).unwrap_err();
694 assert_eq!(
695 error.to_string(),
696 "Invalid override invalid_value. Expected <address>:<slot>:<value>"
697 );
698 }
699
700 #[test]
701 fn can_parse_call_data() {
702 let data = hex::encode("hello");
703 let args = CallArgs::parse_from(["foundry-cli", "--data", data.as_str()]);
704 assert_eq!(args.data, Some(data));
705
706 let data = hex::encode_prefixed("hello");
707 let args = CallArgs::parse_from(["foundry-cli", "--data", data.as_str()]);
708 assert_eq!(args.data, Some(data));
709 }
710
711 #[test]
712 fn can_parse_state_overrides() {
713 let args = CallArgs::parse_from([
714 "foundry-cli",
715 "--override-balance",
716 "0x123:0x1234",
717 "--override-nonce",
718 "0x123:1",
719 "--override-code",
720 "0x123:0x1234",
721 "--override-state",
722 "0x123:0x1:0x1234",
723 ]);
724
725 assert_eq!(args.balance_overrides, Some(vec!["0x123:0x1234".to_string()]));
726 assert_eq!(args.nonce_overrides, Some(vec!["0x123:1".to_string()]));
727 assert_eq!(args.code_overrides, Some(vec!["0x123:0x1234".to_string()]));
728 assert_eq!(args.state_overrides, Some(vec!["0x123:0x1:0x1234".to_string()]));
729 }
730
731 #[test]
732 fn can_parse_multiple_state_overrides() {
733 let args = CallArgs::parse_from([
734 "foundry-cli",
735 "--override-balance",
736 "0x123:0x1234",
737 "--override-balance",
738 "0x456:0x5678",
739 "--override-nonce",
740 "0x123:1",
741 "--override-nonce",
742 "0x456:2",
743 "--override-code",
744 "0x123:0x1234",
745 "--override-code",
746 "0x456:0x5678",
747 "--override-state",
748 "0x123:0x1:0x1234",
749 "--override-state",
750 "0x456:0x2:0x5678",
751 ]);
752
753 assert_eq!(
754 args.balance_overrides,
755 Some(vec!["0x123:0x1234".to_string(), "0x456:0x5678".to_string()])
756 );
757 assert_eq!(args.nonce_overrides, Some(vec!["0x123:1".to_string(), "0x456:2".to_string()]));
758 assert_eq!(
759 args.code_overrides,
760 Some(vec!["0x123:0x1234".to_string(), "0x456:0x5678".to_string()])
761 );
762 assert_eq!(
763 args.state_overrides,
764 Some(vec!["0x123:0x1:0x1234".to_string(), "0x456:0x2:0x5678".to_string()])
765 );
766 }
767
768 #[test]
769 fn test_negative_args_with_flags() {
770 let args = CallArgs::parse_from([
772 "foundry-cli",
773 "--trace",
774 "0xDeaDBeeFcAfEbAbEfAcEfEeDcBaDbEeFcAfEbAbE",
775 "process(int256)",
776 "-999999",
777 "--debug",
778 ]);
779
780 assert!(args.trace);
781 assert!(args.debug);
782 assert_eq!(args.args, vec!["-999999"]);
783 }
784
785 #[test]
786 fn test_transaction_opts_with_trace() {
787 let args = CallArgs::parse_from([
789 "foundry-cli",
790 "--trace",
791 "--gas-limit",
792 "1000000",
793 "--gas-price",
794 "20000000000",
795 "--priority-gas-price",
796 "2000000000",
797 "--nonce",
798 "42",
799 "--value",
800 "1000000000000000000", "--blob-gas-price",
802 "10000000000",
803 "0xDeaDBeeFcAfEbAbEfAcEfEeDcBaDbEeFcAfEbAbE",
804 "balanceOf(address)",
805 "0x123456789abcdef123456789abcdef123456789a",
806 ]);
807
808 assert!(args.trace);
809 assert_eq!(args.tx.gas_limit, Some(U256::from(1000000u32)));
810 assert_eq!(args.tx.gas_price, Some(U256::from(20000000000u64)));
811 assert_eq!(args.tx.priority_gas_price, Some(U256::from(2000000000u64)));
812 assert_eq!(args.tx.nonce, Some(U64::from(42)));
813 assert_eq!(args.tx.value, Some(U256::from(1000000000000000000u64)));
814 assert_eq!(args.tx.blob_gas_price, Some(U256::from(10000000000u64)));
815 }
816}