Skip to main content

cast/
debug.rs

1use std::str::FromStr;
2
3use alloy_chains::Chain;
4use alloy_primitives::{Address, Bytes, map::AddressHashMap};
5use foundry_cli::utils::{TraceResult, print_traces};
6use foundry_common::{ContractsByArtifact, compile::ProjectCompiler, shell};
7use foundry_config::Config;
8use foundry_debugger::Debugger;
9use foundry_evm::{
10    hardforks::TempoHardfork,
11    traces::{
12        CallTraceDecoderBuilder, DebugTraceIdentifier,
13        debug::ContractSources,
14        identifier::{SignaturesIdentifier, TraceIdentifiers},
15    },
16};
17
18/// labels the traces, conditionally prints them or opens the debugger
19#[expect(clippy::too_many_arguments)]
20pub(crate) async fn handle_traces(
21    mut result: TraceResult,
22    config: &Config,
23    chain: Chain,
24    contracts_bytecode: &AddressHashMap<Bytes>,
25    labels: Vec<String>,
26    with_local_artifacts: bool,
27    debug: bool,
28    decode_internal: bool,
29    disable_label: bool,
30    trace_depth: Option<usize>,
31    tempo_hardfork: Option<TempoHardfork>,
32) -> eyre::Result<()> {
33    let (known_contracts, mut sources) = if with_local_artifacts {
34        let _ = sh_println!("Compiling project to generate artifacts");
35        let project = config.project()?;
36        let compiler = ProjectCompiler::new();
37        let output = compiler.compile(&project)?;
38        (
39            Some(ContractsByArtifact::new(
40                output.artifact_ids().map(|(id, artifact)| (id, artifact.clone().into())),
41            )),
42            ContractSources::from_project_output(&output, project.root(), None)?,
43        )
44    } else {
45        (None, ContractSources::default())
46    };
47
48    let labels = labels.iter().filter_map(|label_str| {
49        let mut iter = label_str.split(':');
50
51        if let Some(addr) = iter.next()
52            && let (Ok(address), Some(label)) = (Address::from_str(addr), iter.next())
53        {
54            return Some((address, label.to_string()));
55        }
56        None
57    });
58    let config_labels = config.labels.clone().into_iter();
59
60    let mut builder = CallTraceDecoderBuilder::new()
61        .with_labels(labels.chain(config_labels))
62        .with_signature_identifier(SignaturesIdentifier::from_config(config)?)
63        .with_label_disabled(disable_label)
64        .with_chain_id(Some(chain.id()))
65        .with_tempo_hardfork(
66            tempo_hardfork
67                .or_else(|| chain.is_tempo().then(|| config.evm_spec_id::<TempoHardfork>())),
68        );
69    let mut identifier = TraceIdentifiers::new().with_external(config, Some(chain))?;
70    if let Some(contracts) = &known_contracts {
71        builder = builder.with_known_contracts(contracts);
72        identifier = identifier.with_local_and_bytecodes(contracts, contracts_bytecode);
73    }
74
75    let mut decoder = builder.build();
76
77    for (_, trace) in result.traces.as_deref_mut().unwrap_or_default() {
78        decoder.identify(trace, &mut identifier);
79    }
80
81    if decode_internal || debug {
82        if let Some(ref etherscan_identifier) = identifier.external {
83            sources.merge(etherscan_identifier.get_compiled_contracts().await?);
84        }
85
86        if debug {
87            let mut debugger = Debugger::builder()
88                .traces(result.traces.expect("missing traces"))
89                .decoder(&decoder)
90                .sources(sources)
91                .build();
92            debugger.try_run_tui()?;
93            return Ok(());
94        }
95
96        decoder.debug_identifier = Some(DebugTraceIdentifier::new(sources));
97    }
98
99    print_traces(
100        &mut result,
101        &decoder,
102        shell::verbosity() > 0,
103        shell::verbosity() > 4,
104        trace_depth,
105    )
106    .await?;
107
108    Ok(())
109}