Skip to main content

foundry_debugger/
node.rs

1use alloy_primitives::{Address, Bytes};
2use foundry_evm_traces::{CallKind, CallTraceArena};
3use revm_inspectors::tracing::types::{CallTraceStep, TraceMemberOrder};
4use serde::{Deserialize, Serialize};
5
6/// Represents a part of the execution frame before the next call or end of the execution.
7#[derive(Clone, Debug, Default, Serialize, Deserialize)]
8pub struct DebugNode {
9    /// Execution context.
10    ///
11    /// Note that this is the address of the *code*, not necessarily the address of the storage.
12    pub address: Address,
13    /// The kind of call this is.
14    pub kind: CallKind,
15    /// Calldata of the call.
16    pub calldata: Bytes,
17    /// The gas limit of the call.
18    pub gas_limit: u64,
19    /// The debug steps.
20    pub steps: Vec<CallTraceStep>,
21}
22
23impl DebugNode {
24    /// Creates a new debug node.
25    pub fn new(
26        address: Address,
27        kind: CallKind,
28        steps: Vec<CallTraceStep>,
29        calldata: Bytes,
30        gas_limit: u64,
31    ) -> Self {
32        Self { address, kind, steps, calldata, gas_limit }
33    }
34}
35
36/// Flattens given [CallTraceArena] into a list of [DebugNode]s.
37///
38/// This is done by recursively traversing the call tree and collecting the steps in-between the
39/// calls.
40pub fn flatten_call_trace(arena: CallTraceArena, out: &mut Vec<DebugNode>) {
41    #[derive(Debug, Clone, Copy)]
42    struct PendingNode {
43        node_idx: usize,
44        steps_count: usize,
45    }
46
47    fn inner(arena: &CallTraceArena, node_idx: usize, out: &mut Vec<PendingNode>) {
48        let mut pending = PendingNode { node_idx, steps_count: 0 };
49        let node = &arena.nodes()[node_idx];
50        for order in &node.ordering {
51            match order {
52                TraceMemberOrder::Call(idx) => {
53                    out.push(pending);
54                    pending.steps_count = 0;
55                    inner(arena, node.children[*idx], out);
56                }
57                TraceMemberOrder::Step(_) => {
58                    pending.steps_count += 1;
59                }
60                _ => {}
61            }
62        }
63        out.push(pending);
64    }
65    let mut nodes = Vec::new();
66    inner(&arena, 0, &mut nodes);
67
68    let mut arena_nodes = arena.into_nodes();
69
70    for pending in nodes {
71        let steps = {
72            let other_steps =
73                arena_nodes[pending.node_idx].trace.steps.split_off(pending.steps_count);
74            std::mem::replace(&mut arena_nodes[pending.node_idx].trace.steps, other_steps)
75        };
76
77        // Skip nodes with empty steps as there's nothing to display for them.
78        if steps.is_empty() {
79            continue;
80        }
81
82        let call = &arena_nodes[pending.node_idx].trace;
83        let calldata = if call.kind.is_any_create() { Bytes::new() } else { call.data.clone() };
84        let node = DebugNode::new(call.address, call.kind, steps, calldata, call.gas_limit);
85
86        out.push(node);
87    }
88}