cast/
debug.rs

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