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 debug steps.
18    pub steps: Vec<CallTraceStep>,
19}
20
21impl DebugNode {
22    /// Creates a new debug node.
23    pub fn new(
24        address: Address,
25        kind: CallKind,
26        steps: Vec<CallTraceStep>,
27        calldata: Bytes,
28    ) -> Self {
29        Self { address, kind, steps, calldata }
30    }
31}
32
33/// Flattens given [CallTraceArena] into a list of [DebugNode]s.
34///
35/// This is done by recursively traversing the call tree and collecting the steps in-between the
36/// calls.
37pub fn flatten_call_trace(arena: CallTraceArena, out: &mut Vec<DebugNode>) {
38    #[derive(Debug, Clone, Copy)]
39    struct PendingNode {
40        node_idx: usize,
41        steps_count: usize,
42    }
43
44    fn inner(arena: &CallTraceArena, node_idx: usize, out: &mut Vec<PendingNode>) {
45        let mut pending = PendingNode { node_idx, steps_count: 0 };
46        let node = &arena.nodes()[node_idx];
47        for order in &node.ordering {
48            match order {
49                TraceMemberOrder::Call(idx) => {
50                    out.push(pending);
51                    pending.steps_count = 0;
52                    inner(arena, node.children[*idx], out);
53                }
54                TraceMemberOrder::Step(_) => {
55                    pending.steps_count += 1;
56                }
57                _ => {}
58            }
59        }
60        out.push(pending);
61    }
62    let mut nodes = Vec::new();
63    inner(&arena, 0, &mut nodes);
64
65    let mut arena_nodes = arena.into_nodes();
66
67    for pending in nodes {
68        let steps = {
69            let other_steps =
70                arena_nodes[pending.node_idx].trace.steps.split_off(pending.steps_count);
71            std::mem::replace(&mut arena_nodes[pending.node_idx].trace.steps, other_steps)
72        };
73
74        // Skip nodes with empty steps as there's nothing to display for them.
75        if steps.is_empty() {
76            continue
77        }
78
79        let call = &arena_nodes[pending.node_idx].trace;
80        let calldata = if call.kind.is_any_create() { Bytes::new() } else { call.data.clone() };
81        let node = DebugNode::new(call.address, call.kind, steps, calldata);
82
83        out.push(node);
84    }
85}