diff --git a/crates/revm/revm-inspectors/src/tracing/builder/geth.rs b/crates/revm/revm-inspectors/src/tracing/builder/geth.rs index 91db8c2af2..255cfdba96 100644 --- a/crates/revm/revm-inspectors/src/tracing/builder/geth.rs +++ b/crates/revm/revm-inspectors/src/tracing/builder/geth.rs @@ -118,7 +118,13 @@ impl GethTraceBuilder { let include_logs = opts.with_log.unwrap_or_default(); // first fill up the root let main_trace_node = &self.nodes[0]; - let root_call_frame = main_trace_node.geth_empty_call_frame(include_logs); + let mut root_call_frame = main_trace_node.geth_empty_call_frame(include_logs); + + // selfdestructs are not recorded as individual call traces but are derived from + // the call trace and are added as additional `CallFrame` objects to the parent call + if let Some(selfdestruct) = main_trace_node.geth_selfdestruct_call_trace() { + root_call_frame.calls.push(selfdestruct); + } if opts.only_top_call.unwrap_or_default() { return root_call_frame @@ -129,7 +135,13 @@ impl GethTraceBuilder { // so we can populate the call frame tree by walking up the call tree let mut call_frames = Vec::with_capacity(self.nodes.len()); call_frames.push((0, root_call_frame)); + for (idx, trace) in self.nodes.iter().enumerate().skip(1) { + // selfdestructs are not recorded as individual call traces but are derived from + // the call trace and are added as additional `CallFrame` objects to the parent call + if let Some(selfdestruct) = trace.geth_selfdestruct_call_trace() { + call_frames.last_mut().expect("not empty").1.calls.push(selfdestruct); + } call_frames.push((idx, trace.geth_empty_call_frame(include_logs))); } diff --git a/crates/revm/revm-inspectors/src/tracing/types.rs b/crates/revm/revm-inspectors/src/tracing/types.rs index 552e7d33a0..6c87783054 100644 --- a/crates/revm/revm-inspectors/src/tracing/types.rs +++ b/crates/revm/revm-inspectors/src/tracing/types.rs @@ -386,6 +386,21 @@ impl CallTraceNode { } } + /// If the trace is a selfdestruct, returns the `CallFrame` for a geth call trace + pub(crate) fn geth_selfdestruct_call_trace(&self) -> Option { + if self.is_selfdestruct() { + Some(CallFrame { + typ: "SELFDESTRUCT".to_string(), + from: self.trace.caller, + to: self.trace.selfdestruct_refund_target, + value: Some(self.trace.value), + ..Default::default() + }) + } else { + None + } + } + /// If the trace is a selfdestruct, returns the `TransactionTrace` for a parity trace. pub(crate) fn parity_selfdestruct_trace( &self,