use alloy_primitives::{Address, Bytes};
use foundry_evm_traces::{CallKind, CallTraceArena};
use revm_inspectors::tracing::types::{CallTraceStep, TraceMemberOrder};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct DebugNode {
pub address: Address,
pub kind: CallKind,
pub calldata: Bytes,
pub steps: Vec<CallTraceStep>,
}
impl DebugNode {
pub fn new(
address: Address,
kind: CallKind,
steps: Vec<CallTraceStep>,
calldata: Bytes,
) -> Self {
Self { address, kind, steps, calldata }
}
}
pub fn flatten_call_trace(arena: CallTraceArena, out: &mut Vec<DebugNode>) {
#[derive(Debug, Clone, Copy)]
struct PendingNode {
node_idx: usize,
steps_count: usize,
}
fn inner(arena: &CallTraceArena, node_idx: usize, out: &mut Vec<PendingNode>) {
let mut pending = PendingNode { node_idx, steps_count: 0 };
let node = &arena.nodes()[node_idx];
for order in node.ordering.iter() {
match order {
TraceMemberOrder::Call(idx) => {
out.push(pending);
pending.steps_count = 0;
inner(arena, node.children[*idx], out);
}
TraceMemberOrder::Step(_) => {
pending.steps_count += 1;
}
_ => {}
}
}
out.push(pending);
}
let mut nodes = Vec::new();
inner(&arena, 0, &mut nodes);
let mut arena_nodes = arena.into_nodes();
for pending in nodes {
let steps = {
let other_steps =
arena_nodes[pending.node_idx].trace.steps.split_off(pending.steps_count);
std::mem::replace(&mut arena_nodes[pending.node_idx].trace.steps, other_steps)
};
if steps.is_empty() {
continue
}
let call = &arena_nodes[pending.node_idx].trace;
let calldata = if call.kind.is_any_create() { Bytes::new() } else { call.data.clone() };
let node = DebugNode::new(call.address, call.kind, steps, calldata);
out.push(node);
}
}