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#[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 .with_chain_id(Some(chain.id()));
61 let mut identifier = TraceIdentifiers::new().with_external(config, Some(chain))?;
62 if let Some(contracts) = &known_contracts {
63 builder = builder.with_known_contracts(contracts);
64 identifier = identifier.with_local_and_bytecodes(contracts, contracts_bytecode);
65 }
66
67 let mut decoder = builder.build();
68
69 for (_, trace) in result.traces.as_deref_mut().unwrap_or_default() {
70 decoder.identify(trace, &mut identifier);
71 }
72
73 if decode_internal || debug {
74 if let Some(ref etherscan_identifier) = identifier.external {
75 sources.merge(etherscan_identifier.get_compiled_contracts().await?);
76 }
77
78 if debug {
79 let mut debugger = Debugger::builder()
80 .traces(result.traces.expect("missing traces"))
81 .decoder(&decoder)
82 .sources(sources)
83 .build();
84 debugger.try_run_tui()?;
85 return Ok(());
86 }
87
88 decoder.debug_identifier = Some(DebugTraceIdentifier::new(sources));
89 }
90
91 print_traces(
92 &mut result,
93 &decoder,
94 shell::verbosity() > 0,
95 shell::verbosity() > 4,
96 trace_depth,
97 )
98 .await?;
99
100 Ok(())
101}