Skip to main content

cast/cmd/
trace.rs

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/// CLI arguments for `cast trace`.
13#[derive(Debug, Parser)]
14pub struct TraceArgs {
15    /// Transaction hash (for trace_transaction) or raw tx hex/JSON (for trace_rawTransaction
16    /// with --raw)
17    tx: Option<String>,
18
19    /// Use trace_rawTransaction instead of trace_transaction.
20    /// Required when passing raw transaction hex or JSON instead of a tx hash.
21    #[arg(long)]
22    raw: bool,
23
24    /// Include the basic trace of the transaction.
25    #[arg(long, requires = "raw")]
26    trace: bool,
27
28    /// Include the full trace of the virtual machine's state during transaction execution
29    #[arg(long, requires = "raw")]
30    vm_trace: bool,
31
32    /// Include state changes caused by the transaction (requires --raw).
33    #[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            // trace_rawTransaction: accepts raw hex OR JSON tx
47            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            // trace_transaction: use tx hash directly
76            serde_json::to_string_pretty(&provider.trace_transaction(input.parse()?).await?)?
77        };
78
79        sh_println!("{}", result)?;
80        Ok(())
81    }
82}