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: 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    trace_depth: Option<usize>,
28) -> eyre::Result<()> {
29    let (known_contracts, mut sources) = if with_local_artifacts {
30        let _ = sh_println!("Compiling project to generate artifacts");
31        let project = config.project()?;
32        let compiler = ProjectCompiler::new();
33        let output = compiler.compile(&project)?;
34        (
35            Some(ContractsByArtifact::new(
36                output.artifact_ids().map(|(id, artifact)| (id, artifact.clone().into())),
37            )),
38            ContractSources::from_project_output(&output, project.root(), None)?,
39        )
40    } else {
41        (None, ContractSources::default())
42    };
43
44    let labels = labels.iter().filter_map(|label_str| {
45        let mut iter = label_str.split(':');
46
47        if let Some(addr) = iter.next()
48            && let (Ok(address), Some(label)) = (Address::from_str(addr), iter.next())
49        {
50            return Some((address, label.to_string()));
51        }
52        None
53    });
54    let config_labels = config.labels.clone().into_iter();
55
56    let mut builder = CallTraceDecoderBuilder::new()
57        .with_labels(labels.chain(config_labels))
58        .with_signature_identifier(SignaturesIdentifier::from_config(config)?)
59        .with_label_disabled(disable_label);
60    let mut identifier = TraceIdentifiers::new().with_external(config, Some(chain))?;
61    if let Some(contracts) = &known_contracts {
62        builder = builder.with_known_contracts(contracts);
63        identifier = identifier.with_local_and_bytecodes(contracts, contracts_bytecode);
64    }
65
66    let mut decoder = builder.build();
67
68    for (_, trace) in result.traces.as_deref_mut().unwrap_or_default() {
69        decoder.identify(trace, &mut identifier);
70    }
71
72    if decode_internal || debug {
73        if let Some(ref etherscan_identifier) = identifier.external {
74            sources.merge(etherscan_identifier.get_compiled_contracts().await?);
75        }
76
77        if debug {
78            let mut debugger = Debugger::builder()
79                .traces(result.traces.expect("missing traces"))
80                .decoder(&decoder)
81                .sources(sources)
82                .build();
83            debugger.try_run_tui()?;
84            return Ok(());
85        }
86
87        decoder.debug_identifier = Some(DebugTraceIdentifier::new(sources));
88    }
89
90    print_traces(
91        &mut result,
92        &decoder,
93        shell::verbosity() > 0,
94        shell::verbosity() > 4,
95        trace_depth,
96    )
97    .await?;
98
99    Ok(())
100}