use super::{
Cheatcodes, CheatsConfig, ChiselState, CoverageCollector, Fuzzer, LogCollector,
TracingInspector,
};
use alloy_primitives::{map::AddressHashMap, Address, Bytes, Log, TxKind, U256};
use foundry_cheatcodes::{CheatcodesExecutor, Wallets};
use foundry_evm_core::{backend::DatabaseExt, InspectorExt};
use foundry_evm_coverage::HitMaps;
use foundry_evm_traces::{SparsedTraceArena, TraceMode};
use revm::{
inspectors::CustomPrintTracer,
interpreter::{
CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, EOFCreateInputs,
EOFCreateKind, Gas, InstructionResult, Interpreter, InterpreterResult,
},
primitives::{
Account, AccountStatus, BlockEnv, CreateScheme, Env, EnvWithHandlerCfg, ExecutionResult,
HashMap, Output, TransactTo,
},
EvmContext, Inspector,
};
use std::{
ops::{Deref, DerefMut},
sync::Arc,
};
#[derive(Clone, Debug, Default)]
#[must_use = "builders do nothing unless you call `build` on them"]
pub struct InspectorStackBuilder {
pub block: Option<BlockEnv>,
pub gas_price: Option<U256>,
pub cheatcodes: Option<Arc<CheatsConfig>>,
pub fuzzer: Option<Fuzzer>,
pub trace_mode: TraceMode,
pub logs: Option<bool>,
pub coverage: Option<bool>,
pub print: Option<bool>,
pub chisel_state: Option<usize>,
pub enable_isolation: bool,
pub odyssey: bool,
pub wallets: Option<Wallets>,
pub create2_deployer: Address,
}
impl InspectorStackBuilder {
#[inline]
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn block(mut self, block: BlockEnv) -> Self {
self.block = Some(block);
self
}
#[inline]
pub fn gas_price(mut self, gas_price: U256) -> Self {
self.gas_price = Some(gas_price);
self
}
#[inline]
pub fn cheatcodes(mut self, config: Arc<CheatsConfig>) -> Self {
self.cheatcodes = Some(config);
self
}
#[inline]
pub fn wallets(mut self, wallets: Wallets) -> Self {
self.wallets = Some(wallets);
self
}
#[inline]
pub fn fuzzer(mut self, fuzzer: Fuzzer) -> Self {
self.fuzzer = Some(fuzzer);
self
}
#[inline]
pub fn chisel_state(mut self, final_pc: usize) -> Self {
self.chisel_state = Some(final_pc);
self
}
#[inline]
pub fn logs(mut self, yes: bool) -> Self {
self.logs = Some(yes);
self
}
#[inline]
pub fn coverage(mut self, yes: bool) -> Self {
self.coverage = Some(yes);
self
}
#[inline]
pub fn print(mut self, yes: bool) -> Self {
self.print = Some(yes);
self
}
#[inline]
pub fn trace_mode(mut self, mode: TraceMode) -> Self {
if self.trace_mode < mode {
self.trace_mode = mode
}
self
}
#[inline]
pub fn enable_isolation(mut self, yes: bool) -> Self {
self.enable_isolation = yes;
self
}
#[inline]
pub fn odyssey(mut self, yes: bool) -> Self {
self.odyssey = yes;
self
}
#[inline]
pub fn create2_deployer(mut self, create2_deployer: Address) -> Self {
self.create2_deployer = create2_deployer;
self
}
pub fn build(self) -> InspectorStack {
let Self {
block,
gas_price,
cheatcodes,
fuzzer,
trace_mode,
logs,
coverage,
print,
chisel_state,
enable_isolation,
odyssey,
wallets,
create2_deployer,
} = self;
let mut stack = InspectorStack::new();
if let Some(config) = cheatcodes {
let mut cheatcodes = Cheatcodes::new(config);
if let Some(wallets) = wallets {
cheatcodes.set_wallets(wallets);
}
stack.set_cheatcodes(cheatcodes);
}
if let Some(fuzzer) = fuzzer {
stack.set_fuzzer(fuzzer);
}
if let Some(chisel_state) = chisel_state {
stack.set_chisel(chisel_state);
}
stack.collect_coverage(coverage.unwrap_or(false));
stack.collect_logs(logs.unwrap_or(true));
stack.print(print.unwrap_or(false));
stack.tracing(trace_mode);
stack.enable_isolation(enable_isolation);
stack.odyssey(odyssey);
stack.set_create2_deployer(create2_deployer);
if let Some(block) = block {
stack.set_block(&block);
}
if let Some(gas_price) = gas_price {
stack.set_gas_price(gas_price);
}
stack
}
}
#[macro_export]
macro_rules! call_inspectors {
([$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $call:expr $(,)?) => {
$(
if let Some($id) = $inspector {
({ #[inline(always)] #[cold] || $call })();
}
)+
};
(#[ret] [$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $call:expr $(,)?) => {
$(
if let Some($id) = $inspector {
if let Some(result) = ({ #[inline(always)] #[cold] || $call })() {
return result;
}
}
)+
};
}
pub struct InspectorData {
pub logs: Vec<Log>,
pub labels: AddressHashMap<String>,
pub traces: Option<SparsedTraceArena>,
pub coverage: Option<HitMaps>,
pub cheatcodes: Option<Cheatcodes>,
pub chisel_state: Option<(Vec<U256>, Vec<u8>, InstructionResult)>,
}
#[derive(Debug, Clone)]
pub struct InnerContextData {
original_origin: Address,
}
#[derive(Clone, Debug, Default)]
pub struct InspectorStack {
pub cheatcodes: Option<Cheatcodes>,
pub inner: InspectorStackInner,
}
#[derive(Default, Clone, Debug)]
pub struct InspectorStackInner {
pub chisel_state: Option<ChiselState>,
pub coverage: Option<CoverageCollector>,
pub fuzzer: Option<Fuzzer>,
pub log_collector: Option<LogCollector>,
pub printer: Option<CustomPrintTracer>,
pub tracer: Option<TracingInspector>,
pub enable_isolation: bool,
pub odyssey: bool,
pub create2_deployer: Address,
pub in_inner_context: bool,
pub inner_context_data: Option<InnerContextData>,
pub top_frame_journal: HashMap<Address, Account>,
}
pub struct InspectorStackRefMut<'a> {
pub cheatcodes: Option<&'a mut Cheatcodes>,
pub inner: &'a mut InspectorStackInner,
}
impl CheatcodesExecutor for InspectorStackInner {
fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a> {
Box::new(InspectorStackRefMut { cheatcodes: Some(cheats), inner: self })
}
fn tracing_inspector(&mut self) -> Option<&mut Option<TracingInspector>> {
Some(&mut self.tracer)
}
}
impl InspectorStack {
#[inline]
pub fn new() -> Self {
Self::default()
}
pub fn log_status(&self) {
trace!(enabled=%{
let mut enabled = Vec::with_capacity(16);
macro_rules! push {
($($id:ident),* $(,)?) => {
$(
if self.$id.is_some() {
enabled.push(stringify!($id));
}
)*
};
}
push!(cheatcodes, chisel_state, coverage, fuzzer, log_collector, printer, tracer);
if self.enable_isolation {
enabled.push("isolation");
}
format!("[{}]", enabled.join(", "))
});
}
#[inline]
pub fn set_env(&mut self, env: &Env) {
self.set_block(&env.block);
self.set_gas_price(env.tx.gas_price);
}
#[inline]
pub fn set_block(&mut self, block: &BlockEnv) {
if let Some(cheatcodes) = &mut self.cheatcodes {
cheatcodes.block = Some(block.clone());
}
}
#[inline]
pub fn set_gas_price(&mut self, gas_price: U256) {
if let Some(cheatcodes) = &mut self.cheatcodes {
cheatcodes.gas_price = Some(gas_price);
}
}
#[inline]
pub fn set_cheatcodes(&mut self, cheatcodes: Cheatcodes) {
self.cheatcodes = Some(cheatcodes);
}
#[inline]
pub fn set_fuzzer(&mut self, fuzzer: Fuzzer) {
self.fuzzer = Some(fuzzer);
}
#[inline]
pub fn set_chisel(&mut self, final_pc: usize) {
self.chisel_state = Some(ChiselState::new(final_pc));
}
#[inline]
pub fn collect_coverage(&mut self, yes: bool) {
self.coverage = yes.then(Default::default);
}
#[inline]
pub fn enable_isolation(&mut self, yes: bool) {
self.enable_isolation = yes;
}
#[inline]
pub fn odyssey(&mut self, yes: bool) {
self.odyssey = yes;
}
#[inline]
pub fn set_create2_deployer(&mut self, deployer: Address) {
self.create2_deployer = deployer;
}
#[inline]
pub fn collect_logs(&mut self, yes: bool) {
self.log_collector = yes.then(Default::default);
}
#[inline]
pub fn print(&mut self, yes: bool) {
self.printer = yes.then(Default::default);
}
#[inline]
pub fn tracing(&mut self, mode: TraceMode) {
if let Some(config) = mode.into_config() {
*self.tracer.get_or_insert_with(Default::default).config_mut() = config;
} else {
self.tracer = None;
}
}
#[inline]
pub fn collect(self) -> InspectorData {
let Self {
mut cheatcodes,
inner: InspectorStackInner { chisel_state, coverage, log_collector, tracer, .. },
} = self;
let traces = tracer.map(|tracer| tracer.into_traces()).map(|arena| {
let ignored = cheatcodes
.as_mut()
.map(|cheatcodes| {
let mut ignored = std::mem::take(&mut cheatcodes.ignored_traces.ignored);
if let Some(last_pause_call) = cheatcodes.ignored_traces.last_pause_call {
ignored.insert(last_pause_call, (arena.nodes().len(), 0));
}
ignored
})
.unwrap_or_default();
SparsedTraceArena { arena, ignored }
});
InspectorData {
logs: log_collector.map(|logs| logs.logs).unwrap_or_default(),
labels: cheatcodes
.as_ref()
.map(|cheatcodes| cheatcodes.labels.clone())
.unwrap_or_default(),
traces,
coverage: coverage.map(|coverage| coverage.finish()),
cheatcodes,
chisel_state: chisel_state.and_then(|state| state.state),
}
}
#[inline(always)]
fn as_mut(&mut self) -> InspectorStackRefMut<'_> {
InspectorStackRefMut { cheatcodes: self.cheatcodes.as_mut(), inner: &mut self.inner }
}
}
impl InspectorStackRefMut<'_> {
fn adjust_evm_data_for_inner_context(&mut self, ecx: &mut EvmContext<&mut dyn DatabaseExt>) {
let inner_context_data =
self.inner_context_data.as_ref().expect("should be called in inner context");
ecx.env.tx.caller = inner_context_data.original_origin;
}
fn do_call_end(
&mut self,
ecx: &mut EvmContext<&mut dyn DatabaseExt>,
inputs: &CallInputs,
outcome: CallOutcome,
) -> CallOutcome {
let result = outcome.result.result;
call_inspectors!(
#[ret]
[&mut self.fuzzer, &mut self.tracer, &mut self.cheatcodes, &mut self.printer],
|inspector| {
let new_outcome = inspector.call_end(ecx, inputs, outcome.clone());
let different = new_outcome.result.result != result ||
(new_outcome.result.result == InstructionResult::Revert &&
new_outcome.output() != outcome.output());
different.then_some(new_outcome)
},
);
outcome
}
fn do_create_end(
&mut self,
ecx: &mut EvmContext<&mut dyn DatabaseExt>,
call: &CreateInputs,
outcome: CreateOutcome,
) -> CreateOutcome {
let result = outcome.result.result;
call_inspectors!(
#[ret]
[&mut self.tracer, &mut self.cheatcodes, &mut self.printer],
|inspector| {
let new_outcome = inspector.create_end(ecx, call, outcome.clone());
let different = new_outcome.result.result != result ||
(new_outcome.result.result == InstructionResult::Revert &&
new_outcome.output() != outcome.output());
different.then_some(new_outcome)
},
);
outcome
}
fn do_eofcreate_end(
&mut self,
ecx: &mut EvmContext<&mut dyn DatabaseExt>,
call: &EOFCreateInputs,
outcome: CreateOutcome,
) -> CreateOutcome {
let result = outcome.result.result;
call_inspectors!(
#[ret]
[&mut self.tracer, &mut self.cheatcodes, &mut self.printer],
|inspector| {
let new_outcome = inspector.eofcreate_end(ecx, call, outcome.clone());
let different = new_outcome.result.result != result ||
(new_outcome.result.result == InstructionResult::Revert &&
new_outcome.output() != outcome.output());
different.then_some(new_outcome)
},
);
outcome
}
fn transact_inner(
&mut self,
ecx: &mut EvmContext<&mut dyn DatabaseExt>,
transact_to: TransactTo,
caller: Address,
input: Bytes,
gas_limit: u64,
value: U256,
) -> (InterpreterResult, Option<Address>) {
let ecx = &mut ecx.inner;
let cached_env = ecx.env.clone();
ecx.env.block.basefee = U256::ZERO;
ecx.env.tx.caller = caller;
ecx.env.tx.transact_to = transact_to;
ecx.env.tx.data = input;
ecx.env.tx.value = value;
ecx.env.tx.gas_limit = gas_limit + 21000;
if !ecx.env.cfg.disable_block_gas_limit {
ecx.env.tx.gas_limit =
std::cmp::min(ecx.env.tx.gas_limit, ecx.env.block.gas_limit.to());
}
ecx.env.tx.gas_price = U256::ZERO;
self.inner_context_data = Some(InnerContextData { original_origin: cached_env.tx.caller });
self.in_inner_context = true;
let env = EnvWithHandlerCfg::new_with_spec_id(ecx.env.clone(), ecx.spec_id());
let res = self.with_stack(|inspector| {
let mut evm = crate::utils::new_evm_with_inspector(&mut ecx.db, env, inspector);
evm.context.evm.inner.journaled_state.state = {
let mut state = ecx.journaled_state.state.clone();
for (addr, acc_mut) in &mut state {
if !ecx.journaled_state.warm_preloaded_addresses.contains(addr) {
acc_mut.mark_cold();
}
for slot_mut in acc_mut.storage.values_mut() {
slot_mut.is_cold = true;
slot_mut.original_value = slot_mut.present_value;
}
}
state
};
evm.context.evm.inner.journaled_state.depth = 1;
let res = evm.transact();
ecx.env = evm.context.evm.inner.env;
res
});
self.in_inner_context = false;
self.inner_context_data = None;
ecx.env.tx = cached_env.tx;
ecx.env.block.basefee = cached_env.block.basefee;
let mut gas = Gas::new(gas_limit);
let Ok(res) = res else {
let result =
InterpreterResult { result: InstructionResult::Revert, output: Bytes::new(), gas };
return (result, None);
};
for (addr, mut acc) in res.state {
let Some(acc_mut) = ecx.journaled_state.state.get_mut(&addr) else {
ecx.journaled_state.state.insert(addr, acc);
continue
};
if acc.status.contains(AccountStatus::Cold) &&
!acc_mut.status.contains(AccountStatus::Cold)
{
acc.status -= AccountStatus::Cold;
}
acc_mut.info = acc.info;
acc_mut.status |= acc.status;
for (key, val) in acc.storage {
let Some(slot_mut) = acc_mut.storage.get_mut(&key) else {
acc_mut.storage.insert(key, val);
continue
};
slot_mut.present_value = val.present_value;
slot_mut.is_cold &= val.is_cold;
}
}
let (result, address, output) = match res.result {
ExecutionResult::Success { reason, gas_used, gas_refunded, logs: _, output } => {
gas.set_refund(gas_refunded as i64);
let _ = gas.record_cost(gas_used);
let address = match output {
Output::Create(_, address) => address,
Output::Call(_) => None,
};
(reason.into(), address, output.into_data())
}
ExecutionResult::Halt { reason, gas_used } => {
let _ = gas.record_cost(gas_used);
(reason.into(), None, Bytes::new())
}
ExecutionResult::Revert { gas_used, output } => {
let _ = gas.record_cost(gas_used);
(InstructionResult::Revert, None, output)
}
};
(InterpreterResult { result, output, gas }, address)
}
fn with_stack<O>(&mut self, f: impl FnOnce(&mut InspectorStack) -> O) -> O {
let mut stack = InspectorStack {
cheatcodes: self.cheatcodes.as_deref_mut().map(std::mem::take),
inner: std::mem::take(self.inner),
};
let out = f(&mut stack);
if let Some(cheats) = self.cheatcodes.as_deref_mut() {
*cheats = stack.cheatcodes.take().unwrap();
}
*self.inner = stack.inner;
out
}
fn top_level_frame_start(&mut self, ecx: &mut EvmContext<&mut dyn DatabaseExt>) {
if self.enable_isolation {
self.top_frame_journal = ecx.journaled_state.state.clone();
}
}
fn top_level_frame_end(
&mut self,
ecx: &mut EvmContext<&mut dyn DatabaseExt>,
result: InstructionResult,
) {
if !result.is_revert() {
return;
}
if let Some(cheats) = self.cheatcodes.as_mut() {
cheats.on_revert(ecx);
}
if self.enable_isolation {
ecx.journaled_state.state = std::mem::take(&mut self.top_frame_journal);
}
}
}
impl Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'_> {
fn initialize_interp(
&mut self,
interpreter: &mut Interpreter,
ecx: &mut EvmContext<&mut dyn DatabaseExt>,
) {
call_inspectors!(
[&mut self.coverage, &mut self.tracer, &mut self.cheatcodes, &mut self.printer],
|inspector| inspector.initialize_interp(interpreter, ecx),
);
}
fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut dyn DatabaseExt>) {
call_inspectors!(
[
&mut self.fuzzer,
&mut self.tracer,
&mut self.coverage,
&mut self.cheatcodes,
&mut self.printer,
],
|inspector| inspector.step(interpreter, ecx),
);
}
fn step_end(
&mut self,
interpreter: &mut Interpreter,
ecx: &mut EvmContext<&mut dyn DatabaseExt>,
) {
call_inspectors!(
[&mut self.tracer, &mut self.cheatcodes, &mut self.chisel_state, &mut self.printer],
|inspector| inspector.step_end(interpreter, ecx),
);
}
fn log(
&mut self,
interpreter: &mut Interpreter,
ecx: &mut EvmContext<&mut dyn DatabaseExt>,
log: &Log,
) {
call_inspectors!(
[&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer],
|inspector| inspector.log(interpreter, ecx, log),
);
}
fn call(
&mut self,
ecx: &mut EvmContext<&mut dyn DatabaseExt>,
call: &mut CallInputs,
) -> Option<CallOutcome> {
if self.in_inner_context && ecx.journaled_state.depth == 1 {
self.adjust_evm_data_for_inner_context(ecx);
return None;
}
if ecx.journaled_state.depth == 0 {
self.top_level_frame_start(ecx);
}
call_inspectors!(
#[ret]
[&mut self.fuzzer, &mut self.tracer, &mut self.log_collector, &mut self.printer],
|inspector| {
let mut out = None;
if let Some(output) = inspector.call(ecx, call) {
if output.result.result != InstructionResult::Continue {
out = Some(Some(output));
}
}
out
},
);
if let Some(cheatcodes) = self.cheatcodes.as_deref_mut() {
if let Some(mocks) = cheatcodes.mocked_functions.get(&call.target_address) {
if let Some(target) = mocks
.get(&call.input)
.or_else(|| call.input.get(..4).and_then(|selector| mocks.get(selector)))
{
call.bytecode_address = *target;
}
}
if let Some(output) = cheatcodes.call_with_executor(ecx, call, self.inner) {
if output.result.result != InstructionResult::Continue {
return Some(output);
}
}
}
if self.enable_isolation &&
call.scheme == CallScheme::Call &&
!self.in_inner_context &&
ecx.journaled_state.depth == 1
{
let (result, _) = self.transact_inner(
ecx,
TxKind::Call(call.target_address),
call.caller,
call.input.clone(),
call.gas_limit,
call.value.get(),
);
return Some(CallOutcome { result, memory_offset: call.return_memory_offset.clone() });
}
None
}
fn call_end(
&mut self,
ecx: &mut EvmContext<&mut dyn DatabaseExt>,
inputs: &CallInputs,
outcome: CallOutcome,
) -> CallOutcome {
if self.in_inner_context && ecx.journaled_state.depth == 1 {
return outcome;
}
let outcome = self.do_call_end(ecx, inputs, outcome);
if ecx.journaled_state.depth == 0 {
self.top_level_frame_end(ecx, outcome.result.result);
}
outcome
}
fn create(
&mut self,
ecx: &mut EvmContext<&mut dyn DatabaseExt>,
create: &mut CreateInputs,
) -> Option<CreateOutcome> {
if self.in_inner_context && ecx.journaled_state.depth == 1 {
self.adjust_evm_data_for_inner_context(ecx);
return None;
}
if ecx.journaled_state.depth == 0 {
self.top_level_frame_start(ecx);
}
call_inspectors!(
#[ret]
[&mut self.tracer, &mut self.coverage, &mut self.cheatcodes],
|inspector| inspector.create(ecx, create).map(Some),
);
if !matches!(create.scheme, CreateScheme::Create2 { .. }) &&
self.enable_isolation &&
!self.in_inner_context &&
ecx.journaled_state.depth == 1
{
let (result, address) = self.transact_inner(
ecx,
TxKind::Create,
create.caller,
create.init_code.clone(),
create.gas_limit,
create.value,
);
return Some(CreateOutcome { result, address });
}
None
}
fn create_end(
&mut self,
ecx: &mut EvmContext<&mut dyn DatabaseExt>,
call: &CreateInputs,
outcome: CreateOutcome,
) -> CreateOutcome {
if self.in_inner_context && ecx.journaled_state.depth == 1 {
return outcome;
}
let outcome = self.do_create_end(ecx, call, outcome);
if ecx.journaled_state.depth == 0 {
self.top_level_frame_end(ecx, outcome.result.result);
}
outcome
}
fn eofcreate(
&mut self,
ecx: &mut EvmContext<&mut dyn DatabaseExt>,
create: &mut EOFCreateInputs,
) -> Option<CreateOutcome> {
if self.in_inner_context && ecx.journaled_state.depth == 1 {
self.adjust_evm_data_for_inner_context(ecx);
return None;
}
if ecx.journaled_state.depth == 0 {
self.top_level_frame_start(ecx);
}
call_inspectors!(
#[ret]
[&mut self.tracer, &mut self.coverage, &mut self.cheatcodes],
|inspector| inspector.eofcreate(ecx, create).map(Some),
);
if matches!(create.kind, EOFCreateKind::Tx { .. }) &&
self.enable_isolation &&
!self.in_inner_context &&
ecx.journaled_state.depth == 1
{
let init_code = match &mut create.kind {
EOFCreateKind::Tx { initdata } => initdata.clone(),
EOFCreateKind::Opcode { .. } => unreachable!(),
};
let (result, address) = self.transact_inner(
ecx,
TxKind::Create,
create.caller,
init_code,
create.gas_limit,
create.value,
);
return Some(CreateOutcome { result, address });
}
None
}
fn eofcreate_end(
&mut self,
ecx: &mut EvmContext<&mut dyn DatabaseExt>,
call: &EOFCreateInputs,
outcome: CreateOutcome,
) -> CreateOutcome {
if self.in_inner_context && ecx.journaled_state.depth == 1 {
return outcome;
}
let outcome = self.do_eofcreate_end(ecx, call, outcome);
if ecx.journaled_state.depth == 0 {
self.top_level_frame_end(ecx, outcome.result.result);
}
outcome
}
fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
call_inspectors!([&mut self.tracer, &mut self.printer], |inspector| {
Inspector::<&mut dyn DatabaseExt>::selfdestruct(inspector, contract, target, value)
});
}
}
impl InspectorExt for InspectorStackRefMut<'_> {
fn should_use_create2_factory(
&mut self,
ecx: &mut EvmContext<&mut dyn DatabaseExt>,
inputs: &mut CreateInputs,
) -> bool {
call_inspectors!(
#[ret]
[&mut self.cheatcodes],
|inspector| { inspector.should_use_create2_factory(ecx, inputs).then_some(true) },
);
false
}
fn console_log(&mut self, input: String) {
call_inspectors!([&mut self.log_collector], |inspector| InspectorExt::console_log(
inspector, input
));
}
fn is_odyssey(&self) -> bool {
self.inner.odyssey
}
fn create2_deployer(&self) -> Address {
self.inner.create2_deployer
}
}
impl Inspector<&mut dyn DatabaseExt> for InspectorStack {
#[inline]
fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut dyn DatabaseExt>) {
self.as_mut().step(interpreter, ecx)
}
#[inline]
fn step_end(
&mut self,
interpreter: &mut Interpreter,
ecx: &mut EvmContext<&mut dyn DatabaseExt>,
) {
self.as_mut().step_end(interpreter, ecx)
}
fn call(
&mut self,
context: &mut EvmContext<&mut dyn DatabaseExt>,
inputs: &mut CallInputs,
) -> Option<CallOutcome> {
self.as_mut().call(context, inputs)
}
fn call_end(
&mut self,
context: &mut EvmContext<&mut dyn DatabaseExt>,
inputs: &CallInputs,
outcome: CallOutcome,
) -> CallOutcome {
self.as_mut().call_end(context, inputs, outcome)
}
fn create(
&mut self,
context: &mut EvmContext<&mut dyn DatabaseExt>,
create: &mut CreateInputs,
) -> Option<CreateOutcome> {
self.as_mut().create(context, create)
}
fn create_end(
&mut self,
context: &mut EvmContext<&mut dyn DatabaseExt>,
call: &CreateInputs,
outcome: CreateOutcome,
) -> CreateOutcome {
self.as_mut().create_end(context, call, outcome)
}
fn eofcreate(
&mut self,
context: &mut EvmContext<&mut dyn DatabaseExt>,
create: &mut EOFCreateInputs,
) -> Option<CreateOutcome> {
self.as_mut().eofcreate(context, create)
}
fn eofcreate_end(
&mut self,
context: &mut EvmContext<&mut dyn DatabaseExt>,
call: &EOFCreateInputs,
outcome: CreateOutcome,
) -> CreateOutcome {
self.as_mut().eofcreate_end(context, call, outcome)
}
fn initialize_interp(
&mut self,
interpreter: &mut Interpreter,
ecx: &mut EvmContext<&mut dyn DatabaseExt>,
) {
self.as_mut().initialize_interp(interpreter, ecx)
}
fn log(
&mut self,
interpreter: &mut Interpreter,
ecx: &mut EvmContext<&mut dyn DatabaseExt>,
log: &Log,
) {
self.as_mut().log(interpreter, ecx, log)
}
fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
Inspector::<&mut dyn DatabaseExt>::selfdestruct(&mut self.as_mut(), contract, target, value)
}
}
impl InspectorExt for InspectorStack {
fn should_use_create2_factory(
&mut self,
ecx: &mut EvmContext<&mut dyn DatabaseExt>,
inputs: &mut CreateInputs,
) -> bool {
self.as_mut().should_use_create2_factory(ecx, inputs)
}
fn is_odyssey(&self) -> bool {
self.odyssey
}
fn create2_deployer(&self) -> Address {
self.create2_deployer
}
}
impl<'a> Deref for InspectorStackRefMut<'a> {
type Target = &'a mut InspectorStackInner;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl DerefMut for InspectorStackRefMut<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl Deref for InspectorStack {
type Target = InspectorStackInner;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl DerefMut for InspectorStack {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}