1use crate::cmd::rpc_provider;
2use alloy_consensus::Typed2718;
3use alloy_network::AnyRpcTransaction;
4use alloy_primitives::hex;
5use alloy_provider::ext::TraceApi;
6use clap::Parser;
7use eyre::{Result, WrapErr};
8use foundry_cli::opts::RpcOpts;
9use foundry_common::stdin;
10use foundry_primitives::FoundryTxEnvelope;
11
12#[derive(Debug, Parser)]
14pub struct TraceArgs {
15 tx: Option<String>,
18
19 #[arg(long)]
22 raw: bool,
23
24 #[arg(long, requires = "raw")]
26 trace: bool,
27
28 #[arg(long, requires = "raw")]
30 vm_trace: bool,
31
32 #[arg(long, requires = "raw")]
34 state_diff: bool,
35
36 #[command(flatten)]
37 rpc: RpcOpts,
38}
39
40impl TraceArgs {
41 pub async fn run(self) -> Result<()> {
42 let provider = rpc_provider(&self.rpc)?;
43 let input = stdin::unwrap_line(self.tx)?;
44
45 let result = if self.raw {
46 let trimmed = input.trim();
48 let raw_bytes = if trimmed.starts_with('{') {
49 let tx: AnyRpcTransaction = serde_json::from_str(trimmed)?;
50 FoundryTxEnvelope::encode_rpc_2718(&tx)
51 .wrap_err_with(|| {
52 format!("Cannot EIP-2718 encode transaction type 0x{:x}", tx.ty())
53 })?
54 .to_vec()
55 } else {
56 hex::decode(trimmed)?
57 };
58
59 let mut trace_builder = provider.trace_raw_transaction(&raw_bytes);
60 if self.trace {
61 trace_builder = trace_builder.trace();
62 }
63 if self.vm_trace {
64 trace_builder = trace_builder.vm_trace();
65 }
66 if self.state_diff {
67 trace_builder = trace_builder.state_diff();
68 }
69 if trace_builder.get_trace_types().is_none_or(|t| t.is_empty()) {
70 eyre::bail!("No trace type specified. Use --trace, --vm-trace, or --state-diff");
71 }
72
73 serde_json::to_string_pretty(&trace_builder.await?)?
74 } else {
75 serde_json::to_string_pretty(&provider.trace_transaction(input.parse()?).await?)?
77 };
78
79 sh_println!("{}", result)?;
80 Ok(())
81 }
82}