use std::collections::VecDeque;
use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Error, Result, Vm::*};
use alloy_primitives::{
address, hex,
map::{hash_map::Entry, AddressHashMap, HashMap},
Address, Bytes, LogData as RawLog, U256,
};
use alloy_sol_types::{SolError, SolValue};
use foundry_common::ContractsByArtifact;
use foundry_evm_core::decode::RevertDecoder;
use revm::interpreter::{
return_ok, InstructionResult, Interpreter, InterpreterAction, InterpreterResult,
};
use spec::Vm;
static DUMMY_CALL_OUTPUT: Bytes = Bytes::from_static(&[0u8; 8192]);
const DUMMY_CREATE_ADDRESS: Address = address!("0000000000000000000000000000000000000001");
pub type ExpectedCallTracker = HashMap<Address, HashMap<Bytes, (ExpectedCallData, u64)>>;
#[derive(Clone, Debug)]
pub struct ExpectedCallData {
pub value: Option<U256>,
pub gas: Option<u64>,
pub min_gas: Option<u64>,
pub count: u64,
pub call_type: ExpectedCallType,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ExpectedCallType {
NonCount,
Count,
}
#[derive(Clone, Debug)]
pub enum ExpectedRevertKind {
Default,
Cheatcode { pending_processing: bool },
}
#[derive(Clone, Debug)]
pub struct ExpectedRevert {
pub reason: Option<Vec<u8>>,
pub depth: u64,
pub kind: ExpectedRevertKind,
pub partial_match: bool,
pub reverter: Option<Address>,
pub reverted_by: Option<Address>,
pub count: u64,
pub actual_count: u64,
}
#[derive(Clone, Debug)]
pub struct ExpectedEmit {
pub depth: u64,
pub log: Option<RawLog>,
pub checks: [bool; 5],
pub address: Option<Address>,
pub anonymous: bool,
pub found: bool,
pub count: u64,
}
impl Cheatcode for expectCall_0Call {
fn apply(&self, state: &mut Cheatcodes) -> Result {
let Self { callee, data } = self;
expect_call(state, callee, data, None, None, None, 1, ExpectedCallType::NonCount)
}
}
impl Cheatcode for expectCall_1Call {
fn apply(&self, state: &mut Cheatcodes) -> Result {
let Self { callee, data, count } = self;
expect_call(state, callee, data, None, None, None, *count, ExpectedCallType::Count)
}
}
impl Cheatcode for expectCall_2Call {
fn apply(&self, state: &mut Cheatcodes) -> Result {
let Self { callee, msgValue, data } = self;
expect_call(state, callee, data, Some(msgValue), None, None, 1, ExpectedCallType::NonCount)
}
}
impl Cheatcode for expectCall_3Call {
fn apply(&self, state: &mut Cheatcodes) -> Result {
let Self { callee, msgValue, data, count } = self;
expect_call(
state,
callee,
data,
Some(msgValue),
None,
None,
*count,
ExpectedCallType::Count,
)
}
}
impl Cheatcode for expectCall_4Call {
fn apply(&self, state: &mut Cheatcodes) -> Result {
let Self { callee, msgValue, gas, data } = self;
expect_call(
state,
callee,
data,
Some(msgValue),
Some(*gas),
None,
1,
ExpectedCallType::NonCount,
)
}
}
impl Cheatcode for expectCall_5Call {
fn apply(&self, state: &mut Cheatcodes) -> Result {
let Self { callee, msgValue, gas, data, count } = self;
expect_call(
state,
callee,
data,
Some(msgValue),
Some(*gas),
None,
*count,
ExpectedCallType::Count,
)
}
}
impl Cheatcode for expectCallMinGas_0Call {
fn apply(&self, state: &mut Cheatcodes) -> Result {
let Self { callee, msgValue, minGas, data } = self;
expect_call(
state,
callee,
data,
Some(msgValue),
None,
Some(*minGas),
1,
ExpectedCallType::NonCount,
)
}
}
impl Cheatcode for expectCallMinGas_1Call {
fn apply(&self, state: &mut Cheatcodes) -> Result {
let Self { callee, msgValue, minGas, data, count } = self;
expect_call(
state,
callee,
data,
Some(msgValue),
None,
Some(*minGas),
*count,
ExpectedCallType::Count,
)
}
}
impl Cheatcode for expectEmit_0Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { checkTopic1, checkTopic2, checkTopic3, checkData } = *self;
expect_emit(
ccx.state,
ccx.ecx.journaled_state.depth(),
[true, checkTopic1, checkTopic2, checkTopic3, checkData],
None,
false,
1,
)
}
}
impl Cheatcode for expectEmit_1Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self;
expect_emit(
ccx.state,
ccx.ecx.journaled_state.depth(),
[true, checkTopic1, checkTopic2, checkTopic3, checkData],
Some(emitter),
false,
1,
)
}
}
impl Cheatcode for expectEmit_2Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self {} = self;
expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, false, 1)
}
}
impl Cheatcode for expectEmit_3Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { emitter } = *self;
expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], Some(emitter), false, 1)
}
}
impl Cheatcode for expectEmit_4Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { checkTopic1, checkTopic2, checkTopic3, checkData, count } = *self;
expect_emit(
ccx.state,
ccx.ecx.journaled_state.depth(),
[true, checkTopic1, checkTopic2, checkTopic3, checkData],
None,
false,
count,
)
}
}
impl Cheatcode for expectEmit_5Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter, count } = *self;
expect_emit(
ccx.state,
ccx.ecx.journaled_state.depth(),
[true, checkTopic1, checkTopic2, checkTopic3, checkData],
Some(emitter),
false,
count,
)
}
}
impl Cheatcode for expectEmit_6Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { count } = *self;
expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, false, count)
}
}
impl Cheatcode for expectEmit_7Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { emitter, count } = *self;
expect_emit(
ccx.state,
ccx.ecx.journaled_state.depth(),
[true; 5],
Some(emitter),
false,
count,
)
}
}
impl Cheatcode for expectEmitAnonymous_0Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData } = *self;
expect_emit(
ccx.state,
ccx.ecx.journaled_state.depth(),
[checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData],
None,
true,
1,
)
}
}
impl Cheatcode for expectEmitAnonymous_1Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self;
expect_emit(
ccx.state,
ccx.ecx.journaled_state.depth(),
[checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData],
Some(emitter),
true,
1,
)
}
}
impl Cheatcode for expectEmitAnonymous_2Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self {} = self;
expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, true, 1)
}
}
impl Cheatcode for expectEmitAnonymous_3Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { emitter } = *self;
expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], Some(emitter), true, 1)
}
}
impl Cheatcode for expectRevert_0Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self {} = self;
expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), false, false, None, 1)
}
}
impl Cheatcode for expectRevert_1Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { revertData } = self;
expect_revert(
ccx.state,
Some(revertData.as_ref()),
ccx.ecx.journaled_state.depth(),
false,
false,
None,
1,
)
}
}
impl Cheatcode for expectRevert_2Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { revertData } = self;
expect_revert(
ccx.state,
Some(revertData),
ccx.ecx.journaled_state.depth(),
false,
false,
None,
1,
)
}
}
impl Cheatcode for expectRevert_3Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { reverter } = self;
expect_revert(
ccx.state,
None,
ccx.ecx.journaled_state.depth(),
false,
false,
Some(*reverter),
1,
)
}
}
impl Cheatcode for expectRevert_4Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { revertData, reverter } = self;
expect_revert(
ccx.state,
Some(revertData.as_ref()),
ccx.ecx.journaled_state.depth(),
false,
false,
Some(*reverter),
1,
)
}
}
impl Cheatcode for expectRevert_5Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { revertData, reverter } = self;
expect_revert(
ccx.state,
Some(revertData),
ccx.ecx.journaled_state.depth(),
false,
false,
Some(*reverter),
1,
)
}
}
impl Cheatcode for expectRevert_6Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { count } = self;
expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), false, false, None, *count)
}
}
impl Cheatcode for expectRevert_7Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { revertData, count } = self;
expect_revert(
ccx.state,
Some(revertData.as_ref()),
ccx.ecx.journaled_state.depth(),
false,
false,
None,
*count,
)
}
}
impl Cheatcode for expectRevert_8Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { revertData, count } = self;
expect_revert(
ccx.state,
Some(revertData),
ccx.ecx.journaled_state.depth(),
false,
false,
None,
*count,
)
}
}
impl Cheatcode for expectRevert_9Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { reverter, count } = self;
expect_revert(
ccx.state,
None,
ccx.ecx.journaled_state.depth(),
false,
false,
Some(*reverter),
*count,
)
}
}
impl Cheatcode for expectRevert_10Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { revertData, reverter, count } = self;
expect_revert(
ccx.state,
Some(revertData.as_ref()),
ccx.ecx.journaled_state.depth(),
false,
false,
Some(*reverter),
*count,
)
}
}
impl Cheatcode for expectRevert_11Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { revertData, reverter, count } = self;
expect_revert(
ccx.state,
Some(revertData),
ccx.ecx.journaled_state.depth(),
false,
false,
Some(*reverter),
*count,
)
}
}
impl Cheatcode for expectPartialRevert_0Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { revertData } = self;
expect_revert(
ccx.state,
Some(revertData.as_ref()),
ccx.ecx.journaled_state.depth(),
false,
true,
None,
1,
)
}
}
impl Cheatcode for expectPartialRevert_1Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { revertData, reverter } = self;
expect_revert(
ccx.state,
Some(revertData.as_ref()),
ccx.ecx.journaled_state.depth(),
false,
true,
Some(*reverter),
1,
)
}
}
impl Cheatcode for _expectCheatcodeRevert_0Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), true, false, None, 1)
}
}
impl Cheatcode for _expectCheatcodeRevert_1Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { revertData } = self;
expect_revert(
ccx.state,
Some(revertData.as_ref()),
ccx.ecx.journaled_state.depth(),
true,
false,
None,
1,
)
}
}
impl Cheatcode for _expectCheatcodeRevert_2Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { revertData } = self;
expect_revert(
ccx.state,
Some(revertData),
ccx.ecx.journaled_state.depth(),
true,
false,
None,
1,
)
}
}
impl Cheatcode for expectSafeMemoryCall {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { min, max } = *self;
expect_safe_memory(ccx.state, min, max, ccx.ecx.journaled_state.depth())
}
}
impl Cheatcode for stopExpectSafeMemoryCall {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self {} = self;
ccx.state.allowed_mem_writes.remove(&ccx.ecx.journaled_state.depth());
Ok(Default::default())
}
}
impl Cheatcode for expectSafeMemoryCallCall {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { min, max } = *self;
expect_safe_memory(ccx.state, min, max, ccx.ecx.journaled_state.depth() + 1)
}
}
#[allow(clippy::too_many_arguments)] fn expect_call(
state: &mut Cheatcodes,
target: &Address,
calldata: &Bytes,
value: Option<&U256>,
mut gas: Option<u64>,
mut min_gas: Option<u64>,
count: u64,
call_type: ExpectedCallType,
) -> Result {
let expecteds = state.expected_calls.entry(*target).or_default();
if let Some(val) = value {
if *val > U256::ZERO {
let positive_value_cost_stipend = 2300;
if let Some(gas) = &mut gas {
*gas += positive_value_cost_stipend;
}
if let Some(min_gas) = &mut min_gas {
*min_gas += positive_value_cost_stipend;
}
}
}
match call_type {
ExpectedCallType::Count => {
ensure!(
!expecteds.contains_key(calldata),
"counted expected calls can only bet set once"
);
expecteds.insert(
calldata.clone(),
(ExpectedCallData { value: value.copied(), gas, min_gas, count, call_type }, 0),
);
}
ExpectedCallType::NonCount => {
match expecteds.entry(calldata.clone()) {
Entry::Occupied(mut entry) => {
let (expected, _) = entry.get_mut();
ensure!(
expected.call_type == ExpectedCallType::NonCount,
"cannot overwrite a counted expectCall with a non-counted expectCall"
);
expected.count += 1;
}
Entry::Vacant(entry) => {
entry.insert((
ExpectedCallData { value: value.copied(), gas, min_gas, count, call_type },
0,
));
}
}
}
}
Ok(Default::default())
}
fn expect_emit(
state: &mut Cheatcodes,
depth: u64,
checks: [bool; 5],
address: Option<Address>,
anonymous: bool,
count: u64,
) -> Result {
let expected_emit =
ExpectedEmit { depth, checks, address, found: false, log: None, anonymous, count };
if let Some(found_emit_pos) = state.expected_emits.iter().position(|(emit, _)| emit.found) {
state.expected_emits.insert(found_emit_pos, (expected_emit, Default::default()));
} else {
state.expected_emits.push_back((expected_emit, Default::default()));
}
Ok(Default::default())
}
pub(crate) fn handle_expect_emit(
state: &mut Cheatcodes,
log: &alloy_primitives::Log,
interpreter: &mut Interpreter,
) {
if state.expected_emits.iter().all(|(expected, _)| expected.found) {
return
}
let should_fill_logs = state.expected_emits.iter().any(|(expected, _)| expected.log.is_none());
let index_to_fill_or_check = if should_fill_logs {
state
.expected_emits
.iter()
.position(|(emit, _)| emit.found)
.unwrap_or(state.expected_emits.len())
.saturating_sub(1)
} else {
0
};
let (mut event_to_fill_or_check, mut count_map) = state
.expected_emits
.remove(index_to_fill_or_check)
.expect("we should have an emit to fill or check");
let Some(expected) = &event_to_fill_or_check.log else {
if event_to_fill_or_check.anonymous || !log.topics().is_empty() {
event_to_fill_or_check.log = Some(log.data.clone());
state
.expected_emits
.insert(index_to_fill_or_check, (event_to_fill_or_check, count_map));
} else {
interpreter.instruction_result = InstructionResult::Revert;
interpreter.next_action = InterpreterAction::Return {
result: InterpreterResult {
output: Error::encode("use vm.expectEmitAnonymous to match anonymous events"),
gas: interpreter.gas,
result: InstructionResult::Revert,
},
};
}
return
};
match count_map.entry(log.address) {
Entry::Occupied(mut entry) => {
let log_count_map = entry.get_mut();
log_count_map.insert(&log.data);
}
Entry::Vacant(entry) => {
let mut log_count_map = LogCountMap::new(&event_to_fill_or_check);
if log_count_map.satisfies_checks(&log.data) {
log_count_map.insert(&log.data);
entry.insert(log_count_map);
}
}
}
event_to_fill_or_check.found = || -> bool {
if !checks_topics_and_data(event_to_fill_or_check.checks, expected, log) {
return false
}
if event_to_fill_or_check.address.is_some_and(|addr| addr != log.address) {
return false;
}
let expected_count = event_to_fill_or_check.count;
match event_to_fill_or_check.address {
Some(emitter) => count_map
.get(&emitter)
.is_some_and(|log_map| log_map.count(&log.data) >= expected_count),
None => count_map
.values()
.find(|log_map| log_map.satisfies_checks(&log.data))
.is_some_and(|map| map.count(&log.data) >= expected_count),
}
}();
if event_to_fill_or_check.found {
state.expected_emits.push_back((event_to_fill_or_check, count_map));
} else {
state.expected_emits.push_front((event_to_fill_or_check, count_map));
}
}
pub type ExpectedEmitTracker = VecDeque<(ExpectedEmit, AddressHashMap<LogCountMap>)>;
#[derive(Clone, Debug, Default)]
pub struct LogCountMap {
checks: [bool; 5],
expected_log: RawLog,
map: HashMap<RawLog, u64>,
}
impl LogCountMap {
fn new(expected_emit: &ExpectedEmit) -> Self {
Self {
checks: expected_emit.checks,
expected_log: expected_emit.log.clone().expect("log should be filled here"),
map: Default::default(),
}
}
fn insert(&mut self, log: &RawLog) -> bool {
if self.map.contains_key(log) {
self.map.entry(log.clone()).and_modify(|c| *c += 1);
return true
}
if !self.satisfies_checks(log) {
return false
}
self.map.entry(log.clone()).and_modify(|c| *c += 1).or_insert(1);
true
}
fn satisfies_checks(&self, log: &RawLog) -> bool {
checks_topics_and_data(self.checks, &self.expected_log, log)
}
pub fn count(&self, log: &RawLog) -> u64 {
if !self.satisfies_checks(log) {
return 0
}
self.count_unchecked()
}
pub fn count_unchecked(&self) -> u64 {
self.map.values().sum()
}
}
fn expect_revert(
state: &mut Cheatcodes,
reason: Option<&[u8]>,
depth: u64,
cheatcode: bool,
partial_match: bool,
reverter: Option<Address>,
count: u64,
) -> Result {
ensure!(
state.expected_revert.is_none(),
"you must call another function prior to expecting a second revert"
);
state.expected_revert = Some(ExpectedRevert {
reason: reason.map(<[_]>::to_vec),
depth,
kind: if cheatcode {
ExpectedRevertKind::Cheatcode { pending_processing: true }
} else {
ExpectedRevertKind::Default
},
partial_match,
reverter,
reverted_by: None,
count,
actual_count: 0,
});
Ok(Default::default())
}
pub(crate) fn handle_expect_revert(
is_cheatcode: bool,
is_create: bool,
expected_revert: &mut ExpectedRevert,
status: InstructionResult,
retdata: Bytes,
known_contracts: &Option<ContractsByArtifact>,
) -> Result<(Option<Address>, Bytes)> {
let success_return = || {
if is_create {
(Some(DUMMY_CREATE_ADDRESS), Bytes::new())
} else {
(None, DUMMY_CALL_OUTPUT.clone())
}
};
let stringify = |data: &[u8]| {
if let Ok(s) = String::abi_decode(data, true) {
return s;
}
if data.is_ascii() {
return std::str::from_utf8(data).unwrap().to_owned();
}
hex::encode_prefixed(data)
};
if expected_revert.count == 0 {
if expected_revert.reverter.is_none() && expected_revert.reason.is_none() {
ensure!(
matches!(status, return_ok!()),
"call reverted when it was expected not to revert"
);
return Ok(success_return());
}
let mut reason_match = expected_revert.reason.as_ref().map(|_| false);
let mut reverter_match = expected_revert.reverter.as_ref().map(|_| false);
if let (Some(expected_reverter), Some(actual_reverter)) =
(expected_revert.reverter, expected_revert.reverted_by)
{
if expected_reverter == actual_reverter {
reverter_match = Some(true);
}
}
let expected_reason = expected_revert.reason.as_deref();
if let Some(expected_reason) = expected_reason {
let mut actual_revert: Vec<u8> = retdata.into();
actual_revert = decode_revert(actual_revert);
if actual_revert == expected_reason {
reason_match = Some(true);
}
};
match (reason_match, reverter_match) {
(Some(true), Some(true)) => Err(fmt_err!(
"expected 0 reverts with reason: {}, from address: {}, but got one",
&stringify(expected_reason.unwrap_or_default()),
expected_revert.reverter.unwrap()
)),
(Some(true), None) => Err(fmt_err!(
"expected 0 reverts with reason: {}, but got one",
&stringify(expected_reason.unwrap_or_default())
)),
(None, Some(true)) => Err(fmt_err!(
"expected 0 reverts from address: {}, but got one",
expected_revert.reverter.unwrap()
)),
_ => Ok(success_return()),
}
} else {
ensure!(!matches!(status, return_ok!()), "next call did not revert as expected");
if let (Some(expected_reverter), Some(actual_reverter)) =
(expected_revert.reverter, expected_revert.reverted_by)
{
if expected_reverter != actual_reverter {
return Err(fmt_err!(
"Reverter != expected reverter: {} != {}",
actual_reverter,
expected_reverter
));
}
}
let expected_reason = expected_revert.reason.as_deref();
let Some(expected_reason) = expected_reason else {
return Ok(success_return());
};
if !expected_reason.is_empty() && retdata.is_empty() {
bail!("call reverted as expected, but without data");
}
let mut actual_revert: Vec<u8> = retdata.into();
if expected_revert.partial_match && actual_revert.get(..4) == expected_reason.get(..4) {
return Ok(success_return())
}
actual_revert = decode_revert(actual_revert);
if actual_revert == expected_reason ||
(is_cheatcode && memchr::memmem::find(&actual_revert, expected_reason).is_some())
{
Ok(success_return())
} else {
let (actual, expected) = if let Some(contracts) = known_contracts {
let decoder = RevertDecoder::new().with_abis(contracts.iter().map(|(_, c)| &c.abi));
(
&decoder.decode(actual_revert.as_slice(), Some(status)),
&decoder.decode(expected_reason, Some(status)),
)
} else {
(&stringify(&actual_revert), &stringify(expected_reason))
};
Err(fmt_err!("Error != expected error: {} != {}", actual, expected,))
}
}
}
fn checks_topics_and_data(checks: [bool; 5], expected: &RawLog, log: &RawLog) -> bool {
if log.topics().len() != expected.topics().len() {
return false
}
if !log
.topics()
.iter()
.enumerate()
.filter(|(i, _)| checks[*i])
.all(|(i, topic)| topic == &expected.topics()[i])
{
return false
}
if checks[4] && expected.data.as_ref() != log.data.as_ref() {
return false
}
true
}
fn expect_safe_memory(state: &mut Cheatcodes, start: u64, end: u64, depth: u64) -> Result {
ensure!(start < end, "memory range start ({start}) is greater than end ({end})");
#[allow(clippy::single_range_in_vec_init)] let offsets = state.allowed_mem_writes.entry(depth).or_insert_with(|| vec![0..0x60]);
offsets.push(start..end);
Ok(Default::default())
}
fn decode_revert(revert: Vec<u8>) -> Vec<u8> {
if matches!(
revert.get(..4).map(|s| s.try_into().unwrap()),
Some(Vm::CheatcodeError::SELECTOR | alloy_sol_types::Revert::SELECTOR)
) {
if let Ok(decoded) = Vec::<u8>::abi_decode(&revert[4..], false) {
return decoded;
}
}
revert
}