1use alloy_eips::Encodable2718;
2use alloy_network::AnyRpcTransaction;
3use alloy_primitives::hex;
4use alloy_provider::ext::TraceApi;
5use clap::Parser;
6use eyre::Result;
7use foundry_cli::{
8 opts::RpcOpts,
9 utils::{self, LoadConfig},
10};
11use foundry_common::stdin;
12
13#[derive(Debug, Parser)]
15pub struct TraceArgs {
16 tx: Option<String>,
19
20 #[arg(long)]
23 raw: bool,
24
25 #[arg(long, requires = "raw")]
27 trace: bool,
28
29 #[arg(long, requires = "raw")]
31 vm_trace: bool,
32
33 #[arg(long, requires = "raw")]
35 state_diff: bool,
36
37 #[command(flatten)]
38 rpc: RpcOpts,
39}
40
41impl TraceArgs {
42 pub async fn run(self) -> Result<()> {
43 let config = self.rpc.load_config()?;
44 let provider = utils::get_provider(&config)?;
45 let input = stdin::unwrap_line(self.tx)?;
46
47 let trimmed = input.trim();
48 let is_json = trimmed.starts_with('{');
49 let is_raw_hex = trimmed.starts_with("0x") && trimmed.len() > 66;
50
51 let result = if self.raw {
52 let raw_bytes = if is_raw_hex {
54 hex::decode(trimmed.strip_prefix("0x").unwrap_or(trimmed))?
55 } else if is_json {
56 let tx: AnyRpcTransaction = serde_json::from_str(trimmed)?;
57 tx.inner.inner.encoded_2718().to_vec()
58 } else {
59 hex::decode(trimmed)?
60 };
61
62 let mut trace_builder = provider.trace_raw_transaction(&raw_bytes);
63
64 if self.trace {
65 trace_builder = trace_builder.trace();
66 }
67 if self.vm_trace {
68 trace_builder = trace_builder.vm_trace();
69 }
70 if self.state_diff {
71 trace_builder = trace_builder.state_diff();
72 }
73
74 if trace_builder.get_trace_types().map(|t| t.is_empty()).unwrap_or(true) {
75 eyre::bail!("No trace type specified. Use --trace, --vm-trace, or --state-diff");
76 }
77
78 serde_json::to_string_pretty(&trace_builder.await?)?
79 } else {
80 let hash = input.parse()?;
82 let traces = provider.trace_transaction(hash).await?;
83 serde_json::to_string_pretty(&traces)?
84 };
85
86 sh_println!("{}", result)?;
87 Ok(())
88 }
89}