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 pub fn get_state_overrides(&self) -> eyre::Result<StateOverride> {
318 let mut state_overrides_builder = StateOverridesBuilder::default();
319
320 for override_str in self.balance_overrides.iter().flatten() {
322 let (addr, balance) = address_value_override(override_str)?;
323 state_overrides_builder =
324 state_overrides_builder.with_balance(addr.parse()?, balance.parse()?);
325 }
326
327 for override_str in self.nonce_overrides.iter().flatten() {
329 let (addr, nonce) = address_value_override(override_str)?;
330 state_overrides_builder =
331 state_overrides_builder.with_nonce(addr.parse()?, nonce.parse()?);
332 }
333
334 for override_str in self.code_overrides.iter().flatten() {
336 let (addr, code_str) = address_value_override(override_str)?;
337 state_overrides_builder =
338 state_overrides_builder.with_code(addr.parse()?, Bytes::from_str(code_str)?);
339 }
340
341 for override_str in self.state_overrides.iter().flatten() {
343 let (addr, slot, value) = address_slot_value_override(override_str)?;
344 state_overrides_builder =
345 state_overrides_builder.with_state(addr, [(slot.into(), value.into())]);
346 }
347
348 for override_str in self.state_diff_overrides.iter().flatten() {
350 let (addr, slot, value) = address_slot_value_override(override_str)?;
351 state_overrides_builder =
352 state_overrides_builder.with_state_diff(addr, [(slot.into(), value.into())]);
353 }
354
355 Ok(state_overrides_builder.build())
356 }
357}
358
359impl figment::Provider for CallArgs {
360 fn metadata(&self) -> Metadata {
361 Metadata::named("CallArgs")
362 }
363
364 fn data(&self) -> Result<Map<Profile, Dict>, figment::Error> {
365 let mut map = Map::new();
366
367 if self.odyssey {
368 map.insert("odyssey".into(), self.odyssey.into());
369 }
370
371 if let Some(evm_version) = self.evm_version {
372 map.insert("evm_version".into(), figment::value::Value::serialize(evm_version)?);
373 }
374
375 Ok(Map::from([(Config::selected_profile(), map)]))
376 }
377}
378
379fn address_value_override(address_override: &str) -> Result<(&str, &str)> {
381 address_override.split_once(':').ok_or_else(|| {
382 eyre::eyre!("Invalid override {address_override}. Expected <address>:<value>")
383 })
384}
385
386fn address_slot_value_override(address_override: &str) -> Result<(Address, U256, U256)> {
388 let captures = OVERRIDE_PATTERN.captures(address_override).ok_or_else(|| {
389 eyre::eyre!("Invalid override {address_override}. Expected <address>:<slot>:<value>")
390 })?;
391
392 Ok((
393 captures[1].parse()?, captures[2].parse()?, captures[3].parse()?, ))
397}
398
399#[cfg(test)]
400mod tests {
401 use super::*;
402 use alloy_primitives::hex;
403
404 #[test]
405 fn can_parse_call_data() {
406 let data = hex::encode("hello");
407 let args = CallArgs::parse_from(["foundry-cli", "--data", data.as_str()]);
408 assert_eq!(args.data, Some(data));
409
410 let data = hex::encode_prefixed("hello");
411 let args = CallArgs::parse_from(["foundry-cli", "--data", data.as_str()]);
412 assert_eq!(args.data, Some(data));
413 }
414
415 #[test]
416 fn can_parse_state_overrides() {
417 let args = CallArgs::parse_from([
418 "foundry-cli",
419 "--override-balance",
420 "0x123:0x1234",
421 "--override-nonce",
422 "0x123:1",
423 "--override-code",
424 "0x123:0x1234",
425 "--override-state",
426 "0x123:0x1:0x1234",
427 ]);
428
429 assert_eq!(args.balance_overrides, Some(vec!["0x123:0x1234".to_string()]));
430 assert_eq!(args.nonce_overrides, Some(vec!["0x123:1".to_string()]));
431 assert_eq!(args.code_overrides, Some(vec!["0x123:0x1234".to_string()]));
432 assert_eq!(args.state_overrides, Some(vec!["0x123:0x1:0x1234".to_string()]));
433 }
434
435 #[test]
436 fn can_parse_multiple_state_overrides() {
437 let args = CallArgs::parse_from([
438 "foundry-cli",
439 "--override-balance",
440 "0x123:0x1234",
441 "--override-balance",
442 "0x456:0x5678",
443 "--override-nonce",
444 "0x123:1",
445 "--override-nonce",
446 "0x456:2",
447 "--override-code",
448 "0x123:0x1234",
449 "--override-code",
450 "0x456:0x5678",
451 "--override-state",
452 "0x123:0x1:0x1234",
453 "--override-state",
454 "0x456:0x2:0x5678",
455 ]);
456
457 assert_eq!(
458 args.balance_overrides,
459 Some(vec!["0x123:0x1234".to_string(), "0x456:0x5678".to_string()])
460 );
461 assert_eq!(args.nonce_overrides, Some(vec!["0x123:1".to_string(), "0x456:2".to_string()]));
462 assert_eq!(
463 args.code_overrides,
464 Some(vec!["0x123:0x1234".to_string(), "0x456:0x5678".to_string()])
465 );
466 assert_eq!(
467 args.state_overrides,
468 Some(vec!["0x123:0x1:0x1234".to_string(), "0x456:0x2:0x5678".to_string()])
469 );
470 }
471}