use crate::TxInspector;
use revm::{
context::ContextTr,
interpreter::{
interpreter_types::{InputsTr, InterpreterTypes, Jumps, StackTr},
CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, Interpreter,
},
Database, Inspector,
};
use crate::types::*;
use alloy::primitives::{Address, Bytes, Log, U256};
impl<CTX, INTR> Inspector<CTX, INTR> for TxInspector
where
CTX: ContextTr,
INTR: InterpreterTypes,
{
fn call(&mut self, context: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> {
let mut from = self.address_stack.last().copied().unwrap_or(inputs.caller);
if from == Address::ZERO {
from = inputs.caller;
}
let to = match inputs.scheme {
CallScheme::DelegateCall => inputs.bytecode_address,
_ => inputs.target_address,
};
if let Some(value) = inputs.transfer_value() {
if value > U256::ZERO
&& (inputs.scheme == CallScheme::Call || inputs.scheme == CallScheme::CallCode)
{
self.transfers.push(TokenTransfer {
token: NATIVE_TOKEN_ADDRESS,
from: inputs.transfer_from(),
to: Some(inputs.transfer_to()),
token_type: TokenType::Native,
id: None,
value,
});
}
}
let next_caller = match inputs.scheme {
CallScheme::DelegateCall => from,
_ => to,
};
self.address_stack.push(next_caller);
let mut trace_address = Vec::new();
if let Some(&parent_index) = self.call_stack.last() {
trace_address = self.call_traces[parent_index].trace_address.clone();
trace_address.push(self.call_traces[parent_index].subtraces.len());
}
let trace = CallTrace {
from,
to,
value: inputs.call_value(),
input: inputs.input.bytes(context),
call_scheme: Some(inputs.scheme),
create_scheme: None,
gas_used: U256::ZERO,
output: Bytes::new(),
status: CallStatus::InProgress,
error_origin: false,
subtraces: Vec::new(),
trace_address,
slot_accesses: Vec::new(), };
self.call_traces.push(trace);
self.call_stack.push(self.call_traces.len() - 1);
None
}
fn create(&mut self, _context: &mut CTX, inputs: &mut CreateInputs) -> Option<CreateOutcome> {
let mut from = self.address_stack.last().copied().unwrap_or(inputs.caller);
if from == Address::ZERO {
from = inputs.caller;
}
let to = Address::ZERO; self.address_stack.push(to);
if inputs.value > U256::ZERO {
let transfer = TokenTransfer {
token: NATIVE_TOKEN_ADDRESS,
from,
to: None, token_type: TokenType::Native,
id: None,
value: inputs.value,
};
self.transfers.push(transfer.clone());
self.pending_create_transfers
.push((self.transfers.len() - 1, transfer));
}
let mut trace_address = Vec::new();
if let Some(&parent_index) = self.call_stack.last() {
trace_address = self.call_traces[parent_index].trace_address.clone();
trace_address.push(self.call_traces[parent_index].subtraces.len());
}
let trace = CallTrace {
from,
to, value: inputs.value,
input: inputs.init_code.clone(),
call_scheme: None,
create_scheme: Some(inputs.scheme),
gas_used: U256::ZERO,
output: Bytes::new(),
status: CallStatus::InProgress,
error_origin: false,
subtraces: Vec::new(),
trace_address,
slot_accesses: Vec::new(), };
self.call_traces.push(trace);
self.call_stack.push(self.call_traces.len() - 1);
None
}
fn call_end(&mut self, _context: &mut CTX, _inputs: &CallInputs, outcome: &mut CallOutcome) {
self.handle_end(
outcome.result.result,
outcome.result.gas.spent(),
outcome.result.output.clone(),
);
self.address_stack.pop();
}
fn create_end(
&mut self,
_context: &mut CTX,
_inputs: &CreateInputs,
outcome: &mut CreateOutcome,
) {
if let Some(address) = outcome.address {
if let Some(trace_index) = self.call_stack.last() {
self.call_traces[*trace_index].to = address;
}
if let Some((transfer_index, mut transfer)) = self.pending_create_transfers.pop() {
transfer.to = Some(address);
self.transfers[transfer_index] = transfer;
}
}
self.handle_end(
outcome.result.result,
outcome.result.gas.spent(),
outcome.result.output.clone(),
);
self.address_stack.pop();
}
fn log(&mut self, _interp: &mut Interpreter<INTR>, _context: &mut CTX, log: Log) {
self.logs.push(log.clone());
let mut transfers = TokenTransfer::get_token_transfers(&log);
self.transfers.append(&mut transfers);
}
fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
if value > U256::ZERO {
self.transfers.push(TokenTransfer {
token: NATIVE_TOKEN_ADDRESS,
from: contract,
to: Some(target),
value,
token_type: TokenType::Native,
id: None,
});
}
}
fn step(&mut self, interp: &mut Interpreter<INTR>, context: &mut CTX) {
let opcode = interp.bytecode.opcode();
if opcode == 0x55 && self.call_stack.last().is_some() {
let slot = interp.stack.pop();
let value = interp.stack.pop();
if let Some(value) = value {
let _ = interp.stack.push(value);
}
if let Some(slot) = slot {
let _ = interp.stack.push(slot);
}
if let (Some(slot), Some(value)) = (slot, value) {
let target = interp.input.target_address();
let cached = self.slot_cache.get(&(target, slot));
let old = if let Some(old) = cached {
*old
} else {
context.db().storage(target, slot).unwrap_or_default()
};
let index = self.call_stack.last().unwrap();
let call_trace = &mut self.call_traces[*index];
call_trace.slot_accesses.push(SlotAccess {
address: target,
slot,
old_value: old,
new_value: value,
is_write: true, });
self.slot_cache.insert((target, slot), value);
}
} else if opcode == 0x54 && self.call_stack.last().is_some() {
let slot = interp.stack.pop();
if let Some(slot) = slot {
let _ = interp.stack.push(slot);
let target = interp.input.target_address();
let cached = self.slot_cache.get(&(target, slot));
let value = if let Some(old) = cached {
*old
} else {
context.db().storage(target, slot).unwrap_or_default()
};
let index = self.call_stack.last().unwrap();
let call_trace = &mut self.call_traces[*index];
call_trace.slot_accesses.push(SlotAccess {
address: target,
slot,
old_value: value,
new_value: value,
is_write: false, });
}
}
}
}