1use crate::{
2 traces::TraceKind,
3 tx::{CastTxBuilder, SenderKind},
4 Cast,
5};
6use alloy_ens::NameOrAddress;
7use alloy_primitives::{Address, Bytes, TxKind, U256};
8use alloy_rpc_types::{
9 state::{StateOverride, StateOverridesBuilder},
10 BlockId, BlockNumberOrTag,
11};
12use clap::Parser;
13use eyre::Result;
14use foundry_cli::{
15 opts::{EthereumOpts, TransactionOpts},
16 utils::{self, handle_traces, parse_ether_value, TraceResult},
17};
18use foundry_common::shell;
19use foundry_compilers::artifacts::EvmVersion;
20use foundry_config::{
21 figment::{
22 self,
23 value::{Dict, Map},
24 Figment, Metadata, Profile,
25 },
26 Config,
27};
28use foundry_evm::{
29 executors::TracingExecutor,
30 opts::EvmOpts,
31 traces::{InternalTraceMode, TraceMode},
32};
33use regex::Regex;
34use revm::context::TransactionType;
35use std::{str::FromStr, sync::LazyLock};
36
37static OVERRIDE_PATTERN: LazyLock<Regex> =
40 LazyLock::new(|| Regex::new(r"^([^:]+):([^:]+):([^:]+)$").unwrap());
41
42#[derive(Debug, Parser)]
64pub struct CallArgs {
65 #[arg(value_parser = NameOrAddress::from_str)]
67 to: Option<NameOrAddress>,
68
69 sig: Option<String>,
71
72 args: Vec<String>,
74
75 #[arg(
77 long,
78 conflicts_with_all = &["sig", "args"]
79 )]
80 data: Option<String>,
81
82 #[arg(long, default_value_t = false)]
84 trace: bool,
85
86 #[arg(long, requires = "trace")]
89 debug: bool,
90
91 #[arg(long, requires = "trace")]
92 decode_internal: bool,
93
94 #[arg(long, requires = "trace")]
97 labels: Vec<String>,
98
99 #[arg(long, requires = "trace")]
102 evm_version: Option<EvmVersion>,
103
104 #[arg(long, short)]
108 block: Option<BlockId>,
109
110 #[arg(long, alias = "alphanet")]
112 pub odyssey: bool,
113
114 #[command(subcommand)]
115 command: Option<CallSubcommands>,
116
117 #[command(flatten)]
118 tx: TransactionOpts,
119
120 #[command(flatten)]
121 eth: EthereumOpts,
122
123 #[arg(long, visible_alias = "la")]
125 pub with_local_artifacts: bool,
126
127 #[arg(long = "override-balance", value_name = "ADDRESS:BALANCE")]
130 pub balance_overrides: Option<Vec<String>>,
131
132 #[arg(long = "override-nonce", value_name = "ADDRESS:NONCE")]
135 pub nonce_overrides: Option<Vec<String>>,
136
137 #[arg(long = "override-code", value_name = "ADDRESS:CODE")]
140 pub code_overrides: Option<Vec<String>>,
141
142 #[arg(long = "override-state", value_name = "ADDRESS:SLOT:VALUE")]
145 pub state_overrides: Option<Vec<String>>,
146
147 #[arg(long = "override-state-diff", value_name = "ADDRESS:SLOT:VALUE")]
150 pub state_diff_overrides: Option<Vec<String>>,
151}
152
153#[derive(Debug, Parser)]
154pub enum CallSubcommands {
155 #[command(name = "--create")]
157 Create {
158 code: String,
160
161 sig: Option<String>,
163
164 args: Vec<String>,
166
167 #[arg(long, value_parser = parse_ether_value)]
173 value: Option<U256>,
174 },
175}
176
177impl CallArgs {
178 pub async fn run(self) -> Result<()> {
179 let figment = Into::<Figment>::into(&self.eth).merge(&self);
180 let evm_opts = figment.extract::<EvmOpts>()?;
181 let mut config = Config::from_provider(figment)?.sanitized();
182 let state_overrides = self.get_state_overrides()?;
183
184 let Self {
185 to,
186 mut sig,
187 mut args,
188 mut tx,
189 eth,
190 command,
191 block,
192 trace,
193 evm_version,
194 debug,
195 decode_internal,
196 labels,
197 data,
198 with_local_artifacts,
199 ..
200 } = self;
201
202 if let Some(data) = data {
203 sig = Some(data);
204 }
205
206 let provider = utils::get_provider(&config)?;
207 let sender = SenderKind::from_wallet_opts(eth.wallet).await?;
208 let from = sender.address();
209
210 let code = if let Some(CallSubcommands::Create {
211 code,
212 sig: create_sig,
213 args: create_args,
214 value,
215 }) = command
216 {
217 sig = create_sig;
218 args = create_args;
219 if let Some(value) = value {
220 tx.value = Some(value);
221 }
222 Some(code)
223 } else {
224 None
225 };
226
227 let (tx, func) = CastTxBuilder::new(&provider, tx, &config)
228 .await?
229 .with_to(to)
230 .await?
231 .with_code_sig_and_args(code, sig, args)
232 .await?
233 .build_raw(sender)
234 .await?;
235
236 if trace {
237 if let Some(BlockId::Number(BlockNumberOrTag::Number(block_number))) = self.block {
238 config.fork_block_number = Some(block_number);
240 }
241
242 let create2_deployer = evm_opts.create2_deployer;
243 let (mut env, fork, chain, odyssey) =
244 TracingExecutor::get_fork_material(&config, evm_opts).await?;
245
246 env.evm_env.cfg_env.disable_block_gas_limit = true;
248 env.evm_env.block_env.gas_limit = u64::MAX;
249
250 let trace_mode = TraceMode::Call
251 .with_debug(debug)
252 .with_decode_internal(if decode_internal {
253 InternalTraceMode::Full
254 } else {
255 InternalTraceMode::None
256 })
257 .with_state_changes(shell::verbosity() > 4);
258 let mut executor = TracingExecutor::new(
259 env,
260 fork,
261 evm_version,
262 trace_mode,
263 odyssey,
264 create2_deployer,
265 )?;
266
267 let value = tx.value.unwrap_or_default();
268 let input = tx.inner.input.into_input().unwrap_or_default();
269 let tx_kind = tx.inner.to.expect("set by builder");
270 let env_tx = &mut executor.env_mut().tx;
271
272 if let Some(tx_type) = tx.inner.transaction_type {
273 env_tx.tx_type = tx_type;
274 }
275
276 if let Some(access_list) = tx.inner.access_list {
277 env_tx.access_list = access_list;
278
279 if env_tx.tx_type == TransactionType::Legacy as u8 {
280 env_tx.tx_type = TransactionType::Eip2930 as u8;
281 }
282 }
283
284 let trace = match tx_kind {
285 TxKind::Create => {
286 let deploy_result = executor.deploy(from, input, value, None);
287 TraceResult::try_from(deploy_result)?
288 }
289 TxKind::Call(to) => TraceResult::from_raw(
290 executor.transact_raw(from, to, input, value)?,
291 TraceKind::Execution,
292 ),
293 };
294
295 handle_traces(
296 trace,
297 &config,
298 chain,
299 labels,
300 with_local_artifacts,
301 debug,
302 decode_internal,
303 )
304 .await?;
305
306 return Ok(());
307 }
308
309 sh_println!(
310 "{}",
311 Cast::new(provider).call(&tx, func.as_ref(), block, state_overrides).await?
312 )?;
313
314 Ok(())
315 }
316
317 pub fn get_state_overrides(&self) -> eyre::Result<Option<StateOverride>> {
319 if [
321 self.balance_overrides.as_ref(),
322 self.nonce_overrides.as_ref(),
323 self.code_overrides.as_ref(),
324 self.state_overrides.as_ref(),
325 ]
326 .iter()
327 .all(Option::is_none)
328 {
329 return Ok(None);
330 }
331
332 let mut state_overrides_builder = StateOverridesBuilder::default();
333
334 for override_str in self.balance_overrides.iter().flatten() {
336 let (addr, balance) = address_value_override(override_str)?;
337 state_overrides_builder =
338 state_overrides_builder.with_balance(addr.parse()?, balance.parse()?);
339 }
340
341 for override_str in self.nonce_overrides.iter().flatten() {
343 let (addr, nonce) = address_value_override(override_str)?;
344 state_overrides_builder =
345 state_overrides_builder.with_nonce(addr.parse()?, nonce.parse()?);
346 }
347
348 for override_str in self.code_overrides.iter().flatten() {
350 let (addr, code_str) = address_value_override(override_str)?;
351 state_overrides_builder =
352 state_overrides_builder.with_code(addr.parse()?, Bytes::from_str(code_str)?);
353 }
354
355 for override_str in self.state_overrides.iter().flatten() {
357 let (addr, slot, value) = address_slot_value_override(override_str)?;
358 state_overrides_builder =
359 state_overrides_builder.with_state(addr, [(slot.into(), value.into())]);
360 }
361
362 for override_str in self.state_diff_overrides.iter().flatten() {
364 let (addr, slot, value) = address_slot_value_override(override_str)?;
365 state_overrides_builder =
366 state_overrides_builder.with_state_diff(addr, [(slot.into(), value.into())]);
367 }
368
369 Ok(Some(state_overrides_builder.build()))
370 }
371}
372
373impl figment::Provider for CallArgs {
374 fn metadata(&self) -> Metadata {
375 Metadata::named("CallArgs")
376 }
377
378 fn data(&self) -> Result<Map<Profile, Dict>, figment::Error> {
379 let mut map = Map::new();
380
381 if self.odyssey {
382 map.insert("odyssey".into(), self.odyssey.into());
383 }
384
385 if let Some(evm_version) = self.evm_version {
386 map.insert("evm_version".into(), figment::value::Value::serialize(evm_version)?);
387 }
388
389 Ok(Map::from([(Config::selected_profile(), map)]))
390 }
391}
392
393fn address_value_override(address_override: &str) -> Result<(&str, &str)> {
395 address_override.split_once(':').ok_or_else(|| {
396 eyre::eyre!("Invalid override {address_override}. Expected <address>:<value>")
397 })
398}
399
400fn address_slot_value_override(address_override: &str) -> Result<(Address, U256, U256)> {
402 let captures = OVERRIDE_PATTERN.captures(address_override).ok_or_else(|| {
403 eyre::eyre!("Invalid override {address_override}. Expected <address>:<slot>:<value>")
404 })?;
405
406 Ok((
407 captures[1].parse()?, captures[2].parse()?, captures[3].parse()?, ))
411}
412
413#[cfg(test)]
414mod tests {
415 use super::*;
416 use alloy_primitives::hex;
417
418 #[test]
419 fn can_parse_call_data() {
420 let data = hex::encode("hello");
421 let args = CallArgs::parse_from(["foundry-cli", "--data", data.as_str()]);
422 assert_eq!(args.data, Some(data));
423
424 let data = hex::encode_prefixed("hello");
425 let args = CallArgs::parse_from(["foundry-cli", "--data", data.as_str()]);
426 assert_eq!(args.data, Some(data));
427 }
428
429 #[test]
430 fn can_parse_state_overrides() {
431 let args = CallArgs::parse_from([
432 "foundry-cli",
433 "--override-balance",
434 "0x123:0x1234",
435 "--override-nonce",
436 "0x123:1",
437 "--override-code",
438 "0x123:0x1234",
439 "--override-state",
440 "0x123:0x1:0x1234",
441 ]);
442
443 assert_eq!(args.balance_overrides, Some(vec!["0x123:0x1234".to_string()]));
444 assert_eq!(args.nonce_overrides, Some(vec!["0x123:1".to_string()]));
445 assert_eq!(args.code_overrides, Some(vec!["0x123:0x1234".to_string()]));
446 assert_eq!(args.state_overrides, Some(vec!["0x123:0x1:0x1234".to_string()]));
447 }
448
449 #[test]
450 fn can_parse_multiple_state_overrides() {
451 let args = CallArgs::parse_from([
452 "foundry-cli",
453 "--override-balance",
454 "0x123:0x1234",
455 "--override-balance",
456 "0x456:0x5678",
457 "--override-nonce",
458 "0x123:1",
459 "--override-nonce",
460 "0x456:2",
461 "--override-code",
462 "0x123:0x1234",
463 "--override-code",
464 "0x456:0x5678",
465 "--override-state",
466 "0x123:0x1:0x1234",
467 "--override-state",
468 "0x456:0x2:0x5678",
469 ]);
470
471 assert_eq!(
472 args.balance_overrides,
473 Some(vec!["0x123:0x1234".to_string(), "0x456:0x5678".to_string()])
474 );
475 assert_eq!(args.nonce_overrides, Some(vec!["0x123:1".to_string(), "0x456:2".to_string()]));
476 assert_eq!(
477 args.code_overrides,
478 Some(vec!["0x123:0x1234".to_string(), "0x456:0x5678".to_string()])
479 );
480 assert_eq!(
481 args.state_overrides,
482 Some(vec!["0x123:0x1:0x1234".to_string(), "0x456:0x2:0x5678".to_string()])
483 );
484 }
485}