1use alloy_chains::Chain;
2use alloy_primitives::{Bytes, map::AddressHashMap};
3use foundry_cli::utils::{TraceResult, print_traces};
4use foundry_common::{ContractsByArtifact, compile::ProjectCompiler};
5use foundry_config::{Config, TracingConfig};
6use foundry_debugger::Debugger;
7use foundry_evm::{
8 hardforks::TempoHardfork,
9 traces::{
10 CallTraceDecoderBuilder, DebugTraceIdentifier,
11 debug::ContractSources,
12 identifier::{SignaturesIdentifier, TraceIdentifiers},
13 },
14};
15
16#[expect(clippy::too_many_arguments)]
18pub(crate) async fn handle_traces(
19 mut result: TraceResult,
20 config: &Config,
21 chain: Chain,
22 contracts_bytecode: &AddressHashMap<Bytes>,
23 tracing: &TracingConfig,
24 with_local_artifacts: bool,
25 debug: bool,
26 tempo_hardfork: Option<TempoHardfork>,
27) -> eyre::Result<()> {
28 let (known_contracts, mut sources) = if with_local_artifacts {
29 let _ = sh_status!("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 is_tempo = tempo_hardfork.is_some() || chain.is_tempo();
45 let mut builder = CallTraceDecoderBuilder::new()
46 .with_tracing_config(tracing)
47 .with_signature_identifier(SignaturesIdentifier::from_config(config)?)
48 .with_chain_id((!is_tempo).then(|| chain.id()))
49 .with_tempo_hardfork(
50 tempo_hardfork
51 .or_else(|| chain.is_tempo().then(|| config.evm_spec_id::<TempoHardfork>())),
52 );
53 let mut identifier = TraceIdentifiers::new().with_external(config, Some(chain))?;
54 if let Some(contracts) = &known_contracts {
55 builder = builder.with_known_contracts(contracts);
56 identifier = identifier.with_local_and_bytecodes(contracts, contracts_bytecode);
57 }
58
59 let mut decoder = builder.build();
60
61 for (_, trace) in result.traces.as_deref_mut().unwrap_or_default() {
62 decoder.identify(trace, &mut identifier);
63 }
64
65 if tracing.decode_internal || debug {
66 if let Some(ref etherscan_identifier) = identifier.external {
67 sources.merge(etherscan_identifier.get_compiled_contracts().await?);
68 }
69
70 if debug {
71 let mut debugger = Debugger::builder()
72 .traces(result.traces.expect("missing traces"))
73 .decoder(&decoder)
74 .sources(sources)
75 .build();
76 debugger.try_run_tui()?;
77 return Ok(());
78 }
79
80 decoder.debug_identifier = Some(DebugTraceIdentifier::new(sources));
81 }
82
83 print_traces(
84 &mut result,
85 &decoder,
86 tracing.verbosity > 0,
87 tracing.verbosity > 4,
88 tracing.trace_depth,
89 )
90 .await?;
91
92 Ok(())
93}