foundry_cheatcodes/test/
expect.rs

1use std::{
2    collections::VecDeque,
3    fmt::{self, Display},
4};
5
6use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Error, Result, Vm::*};
7use alloy_dyn_abi::{DynSolValue, EventExt};
8use alloy_json_abi::Event;
9use alloy_primitives::{
10    Address, Bytes, LogData as RawLog, U256, hex,
11    map::{AddressHashMap, HashMap, hash_map::Entry},
12};
13use foundry_common::{abi::get_indexed_event, fmt::format_token};
14use foundry_evm_traces::DecodedCallLog;
15use revm::{
16    context::JournalTr,
17    interpreter::{
18        InstructionResult, Interpreter, InterpreterAction, interpreter_types::LoopControl,
19    },
20};
21
22use super::revert_handlers::RevertParameters;
23/// Tracks the expected calls per address.
24///
25/// For each address, we track the expected calls per call data. We track it in such manner
26/// so that we don't mix together calldatas that only contain selectors and calldatas that contain
27/// selector and arguments (partial and full matches).
28///
29/// This then allows us to customize the matching behavior for each call data on the
30/// `ExpectedCallData` struct and track how many times we've actually seen the call on the second
31/// element of the tuple.
32pub type ExpectedCallTracker = HashMap<Address, HashMap<Bytes, (ExpectedCallData, u64)>>;
33
34#[derive(Clone, Debug)]
35pub struct ExpectedCallData {
36    /// The expected value sent in the call
37    pub value: Option<U256>,
38    /// The expected gas supplied to the call
39    pub gas: Option<u64>,
40    /// The expected *minimum* gas supplied to the call
41    pub min_gas: Option<u64>,
42    /// The number of times the call is expected to be made.
43    /// If the type of call is `NonCount`, this is the lower bound for the number of calls
44    /// that must be seen.
45    /// If the type of call is `Count`, this is the exact number of calls that must be seen.
46    pub count: u64,
47    /// The type of expected call.
48    pub call_type: ExpectedCallType,
49}
50
51/// The type of expected call.
52#[derive(Clone, Debug, PartialEq, Eq)]
53pub enum ExpectedCallType {
54    /// The call is expected to be made at least once.
55    NonCount,
56    /// The exact number of calls expected.
57    Count,
58}
59
60/// The type of expected revert.
61#[derive(Clone, Debug)]
62pub enum ExpectedRevertKind {
63    /// Expects revert from the next non-cheatcode call.
64    Default,
65    /// Expects revert from the next cheatcode call.
66    ///
67    /// The `pending_processing` flag is used to track whether we have exited
68    /// `expectCheatcodeRevert` context or not.
69    /// We have to track it to avoid expecting `expectCheatcodeRevert` call to revert itself.
70    Cheatcode { pending_processing: bool },
71}
72
73#[derive(Clone, Debug)]
74pub struct ExpectedRevert {
75    /// The expected data returned by the revert, None being any.
76    pub reason: Option<Vec<u8>>,
77    /// The depth at which the revert is expected.
78    pub depth: usize,
79    /// The type of expected revert.
80    pub kind: ExpectedRevertKind,
81    /// If true then only the first 4 bytes of expected data returned by the revert are checked.
82    pub partial_match: bool,
83    /// Contract expected to revert next call.
84    pub reverter: Option<Address>,
85    /// Address that reverted the call.
86    pub reverted_by: Option<Address>,
87    /// Max call depth reached during next call execution.
88    pub max_depth: usize,
89    /// Number of times this revert is expected.
90    pub count: u64,
91    /// Actual number of times this revert has been seen.
92    pub actual_count: u64,
93}
94
95#[derive(Clone, Debug)]
96pub struct ExpectedEmit {
97    /// The depth at which we expect this emit to have occurred
98    pub depth: usize,
99    /// The log we expect
100    pub log: Option<RawLog>,
101    /// The checks to perform:
102    /// ```text
103    /// ┌───────┬───────┬───────┬───────┬────┐
104    /// │topic 0│topic 1│topic 2│topic 3│data│
105    /// └───────┴───────┴───────┴───────┴────┘
106    /// ```
107    pub checks: [bool; 5],
108    /// If present, check originating address against this
109    pub address: Option<Address>,
110    /// If present, relax the requirement that topic 0 must be present. This allows anonymous
111    /// events with no indexed topics to be matched.
112    pub anonymous: bool,
113    /// Whether the log was actually found in the subcalls
114    pub found: bool,
115    /// Number of times the log is expected to be emitted
116    pub count: u64,
117    /// Stores mismatch details if a log didn't match
118    pub mismatch_error: Option<String>,
119}
120
121#[derive(Clone, Debug)]
122pub struct ExpectedCreate {
123    /// The address that deployed the contract
124    pub deployer: Address,
125    /// Runtime bytecode of the contract
126    pub bytecode: Bytes,
127    /// Whether deployed with CREATE or CREATE2
128    pub create_scheme: CreateScheme,
129}
130
131#[derive(Clone, Debug)]
132pub enum CreateScheme {
133    Create,
134    Create2,
135}
136
137impl Display for CreateScheme {
138    fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
139        match self {
140            Self::Create => write!(f, "CREATE"),
141            Self::Create2 => write!(f, "CREATE2"),
142        }
143    }
144}
145
146impl From<revm::context_interface::CreateScheme> for CreateScheme {
147    fn from(scheme: revm::context_interface::CreateScheme) -> Self {
148        match scheme {
149            revm::context_interface::CreateScheme::Create => Self::Create,
150            revm::context_interface::CreateScheme::Create2 { .. } => Self::Create2,
151            _ => unimplemented!("Unsupported create scheme"),
152        }
153    }
154}
155
156impl CreateScheme {
157    pub fn eq(&self, create_scheme: Self) -> bool {
158        matches!(
159            (self, create_scheme),
160            (Self::Create, Self::Create) | (Self::Create2, Self::Create2 { .. })
161        )
162    }
163}
164
165impl Cheatcode for expectCall_0Call {
166    fn apply(&self, state: &mut Cheatcodes) -> Result {
167        let Self { callee, data } = self;
168        expect_call(state, callee, data, None, None, None, 1, ExpectedCallType::NonCount)
169    }
170}
171
172impl Cheatcode for expectCall_1Call {
173    fn apply(&self, state: &mut Cheatcodes) -> Result {
174        let Self { callee, data, count } = self;
175        expect_call(state, callee, data, None, None, None, *count, ExpectedCallType::Count)
176    }
177}
178
179impl Cheatcode for expectCall_2Call {
180    fn apply(&self, state: &mut Cheatcodes) -> Result {
181        let Self { callee, msgValue, data } = self;
182        expect_call(state, callee, data, Some(msgValue), None, None, 1, ExpectedCallType::NonCount)
183    }
184}
185
186impl Cheatcode for expectCall_3Call {
187    fn apply(&self, state: &mut Cheatcodes) -> Result {
188        let Self { callee, msgValue, data, count } = self;
189        expect_call(
190            state,
191            callee,
192            data,
193            Some(msgValue),
194            None,
195            None,
196            *count,
197            ExpectedCallType::Count,
198        )
199    }
200}
201
202impl Cheatcode for expectCall_4Call {
203    fn apply(&self, state: &mut Cheatcodes) -> Result {
204        let Self { callee, msgValue, gas, data } = self;
205        expect_call(
206            state,
207            callee,
208            data,
209            Some(msgValue),
210            Some(*gas),
211            None,
212            1,
213            ExpectedCallType::NonCount,
214        )
215    }
216}
217
218impl Cheatcode for expectCall_5Call {
219    fn apply(&self, state: &mut Cheatcodes) -> Result {
220        let Self { callee, msgValue, gas, data, count } = self;
221        expect_call(
222            state,
223            callee,
224            data,
225            Some(msgValue),
226            Some(*gas),
227            None,
228            *count,
229            ExpectedCallType::Count,
230        )
231    }
232}
233
234impl Cheatcode for expectCallMinGas_0Call {
235    fn apply(&self, state: &mut Cheatcodes) -> Result {
236        let Self { callee, msgValue, minGas, data } = self;
237        expect_call(
238            state,
239            callee,
240            data,
241            Some(msgValue),
242            None,
243            Some(*minGas),
244            1,
245            ExpectedCallType::NonCount,
246        )
247    }
248}
249
250impl Cheatcode for expectCallMinGas_1Call {
251    fn apply(&self, state: &mut Cheatcodes) -> Result {
252        let Self { callee, msgValue, minGas, data, count } = self;
253        expect_call(
254            state,
255            callee,
256            data,
257            Some(msgValue),
258            None,
259            Some(*minGas),
260            *count,
261            ExpectedCallType::Count,
262        )
263    }
264}
265
266impl Cheatcode for expectEmit_0Call {
267    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
268        let Self { checkTopic1, checkTopic2, checkTopic3, checkData } = *self;
269        expect_emit(
270            ccx.state,
271            ccx.ecx.journaled_state.depth(),
272            [true, checkTopic1, checkTopic2, checkTopic3, checkData],
273            None,
274            false,
275            1,
276        )
277    }
278}
279
280impl Cheatcode for expectEmit_1Call {
281    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
282        let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self;
283        expect_emit(
284            ccx.state,
285            ccx.ecx.journaled_state.depth(),
286            [true, checkTopic1, checkTopic2, checkTopic3, checkData],
287            Some(emitter),
288            false,
289            1,
290        )
291    }
292}
293
294impl Cheatcode for expectEmit_2Call {
295    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
296        let Self {} = self;
297        expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, false, 1)
298    }
299}
300
301impl Cheatcode for expectEmit_3Call {
302    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
303        let Self { emitter } = *self;
304        expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], Some(emitter), false, 1)
305    }
306}
307
308impl Cheatcode for expectEmit_4Call {
309    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
310        let Self { checkTopic1, checkTopic2, checkTopic3, checkData, count } = *self;
311        expect_emit(
312            ccx.state,
313            ccx.ecx.journaled_state.depth(),
314            [true, checkTopic1, checkTopic2, checkTopic3, checkData],
315            None,
316            false,
317            count,
318        )
319    }
320}
321
322impl Cheatcode for expectEmit_5Call {
323    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
324        let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter, count } = *self;
325        expect_emit(
326            ccx.state,
327            ccx.ecx.journaled_state.depth(),
328            [true, checkTopic1, checkTopic2, checkTopic3, checkData],
329            Some(emitter),
330            false,
331            count,
332        )
333    }
334}
335
336impl Cheatcode for expectEmit_6Call {
337    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
338        let Self { count } = *self;
339        expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, false, count)
340    }
341}
342
343impl Cheatcode for expectEmit_7Call {
344    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
345        let Self { emitter, count } = *self;
346        expect_emit(
347            ccx.state,
348            ccx.ecx.journaled_state.depth(),
349            [true; 5],
350            Some(emitter),
351            false,
352            count,
353        )
354    }
355}
356
357impl Cheatcode for expectEmitAnonymous_0Call {
358    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
359        let Self { checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData } = *self;
360        expect_emit(
361            ccx.state,
362            ccx.ecx.journaled_state.depth(),
363            [checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData],
364            None,
365            true,
366            1,
367        )
368    }
369}
370
371impl Cheatcode for expectEmitAnonymous_1Call {
372    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
373        let Self { checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self;
374        expect_emit(
375            ccx.state,
376            ccx.ecx.journaled_state.depth(),
377            [checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData],
378            Some(emitter),
379            true,
380            1,
381        )
382    }
383}
384
385impl Cheatcode for expectEmitAnonymous_2Call {
386    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
387        let Self {} = self;
388        expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, true, 1)
389    }
390}
391
392impl Cheatcode for expectEmitAnonymous_3Call {
393    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
394        let Self { emitter } = *self;
395        expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], Some(emitter), true, 1)
396    }
397}
398
399impl Cheatcode for expectCreateCall {
400    fn apply(&self, state: &mut Cheatcodes) -> Result {
401        let Self { bytecode, deployer } = self;
402        expect_create(state, bytecode.clone(), *deployer, CreateScheme::Create)
403    }
404}
405
406impl Cheatcode for expectCreate2Call {
407    fn apply(&self, state: &mut Cheatcodes) -> Result {
408        let Self { bytecode, deployer } = self;
409        expect_create(state, bytecode.clone(), *deployer, CreateScheme::Create2)
410    }
411}
412
413impl Cheatcode for expectRevert_0Call {
414    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
415        let Self {} = self;
416        expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), false, false, None, 1)
417    }
418}
419
420impl Cheatcode for expectRevert_1Call {
421    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
422        let Self { revertData } = self;
423        expect_revert(
424            ccx.state,
425            Some(revertData.as_ref()),
426            ccx.ecx.journaled_state.depth(),
427            false,
428            false,
429            None,
430            1,
431        )
432    }
433}
434
435impl Cheatcode for expectRevert_2Call {
436    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
437        let Self { revertData } = self;
438        expect_revert(
439            ccx.state,
440            Some(revertData),
441            ccx.ecx.journaled_state.depth(),
442            false,
443            false,
444            None,
445            1,
446        )
447    }
448}
449
450impl Cheatcode for expectRevert_3Call {
451    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
452        let Self { reverter } = self;
453        expect_revert(
454            ccx.state,
455            None,
456            ccx.ecx.journaled_state.depth(),
457            false,
458            false,
459            Some(*reverter),
460            1,
461        )
462    }
463}
464
465impl Cheatcode for expectRevert_4Call {
466    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
467        let Self { revertData, reverter } = self;
468        expect_revert(
469            ccx.state,
470            Some(revertData.as_ref()),
471            ccx.ecx.journaled_state.depth(),
472            false,
473            false,
474            Some(*reverter),
475            1,
476        )
477    }
478}
479
480impl Cheatcode for expectRevert_5Call {
481    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
482        let Self { revertData, reverter } = self;
483        expect_revert(
484            ccx.state,
485            Some(revertData),
486            ccx.ecx.journaled_state.depth(),
487            false,
488            false,
489            Some(*reverter),
490            1,
491        )
492    }
493}
494
495impl Cheatcode for expectRevert_6Call {
496    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
497        let Self { count } = self;
498        expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), false, false, None, *count)
499    }
500}
501
502impl Cheatcode for expectRevert_7Call {
503    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
504        let Self { revertData, count } = self;
505        expect_revert(
506            ccx.state,
507            Some(revertData.as_ref()),
508            ccx.ecx.journaled_state.depth(),
509            false,
510            false,
511            None,
512            *count,
513        )
514    }
515}
516
517impl Cheatcode for expectRevert_8Call {
518    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
519        let Self { revertData, count } = self;
520        expect_revert(
521            ccx.state,
522            Some(revertData),
523            ccx.ecx.journaled_state.depth(),
524            false,
525            false,
526            None,
527            *count,
528        )
529    }
530}
531
532impl Cheatcode for expectRevert_9Call {
533    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
534        let Self { reverter, count } = self;
535        expect_revert(
536            ccx.state,
537            None,
538            ccx.ecx.journaled_state.depth(),
539            false,
540            false,
541            Some(*reverter),
542            *count,
543        )
544    }
545}
546
547impl Cheatcode for expectRevert_10Call {
548    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
549        let Self { revertData, reverter, count } = self;
550        expect_revert(
551            ccx.state,
552            Some(revertData.as_ref()),
553            ccx.ecx.journaled_state.depth(),
554            false,
555            false,
556            Some(*reverter),
557            *count,
558        )
559    }
560}
561
562impl Cheatcode for expectRevert_11Call {
563    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
564        let Self { revertData, reverter, count } = self;
565        expect_revert(
566            ccx.state,
567            Some(revertData),
568            ccx.ecx.journaled_state.depth(),
569            false,
570            false,
571            Some(*reverter),
572            *count,
573        )
574    }
575}
576
577impl Cheatcode for expectPartialRevert_0Call {
578    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
579        let Self { revertData } = self;
580        expect_revert(
581            ccx.state,
582            Some(revertData.as_ref()),
583            ccx.ecx.journaled_state.depth(),
584            false,
585            true,
586            None,
587            1,
588        )
589    }
590}
591
592impl Cheatcode for expectPartialRevert_1Call {
593    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
594        let Self { revertData, reverter } = self;
595        expect_revert(
596            ccx.state,
597            Some(revertData.as_ref()),
598            ccx.ecx.journaled_state.depth(),
599            false,
600            true,
601            Some(*reverter),
602            1,
603        )
604    }
605}
606
607impl Cheatcode for _expectCheatcodeRevert_0Call {
608    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
609        expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), true, false, None, 1)
610    }
611}
612
613impl Cheatcode for _expectCheatcodeRevert_1Call {
614    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
615        let Self { revertData } = self;
616        expect_revert(
617            ccx.state,
618            Some(revertData.as_ref()),
619            ccx.ecx.journaled_state.depth(),
620            true,
621            false,
622            None,
623            1,
624        )
625    }
626}
627
628impl Cheatcode for _expectCheatcodeRevert_2Call {
629    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
630        let Self { revertData } = self;
631        expect_revert(
632            ccx.state,
633            Some(revertData),
634            ccx.ecx.journaled_state.depth(),
635            true,
636            false,
637            None,
638            1,
639        )
640    }
641}
642
643impl Cheatcode for expectSafeMemoryCall {
644    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
645        let Self { min, max } = *self;
646        expect_safe_memory(ccx.state, min, max, ccx.ecx.journaled_state.depth().try_into()?)
647    }
648}
649
650impl Cheatcode for stopExpectSafeMemoryCall {
651    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
652        let Self {} = self;
653        ccx.state.allowed_mem_writes.remove(&ccx.ecx.journaled_state.depth().try_into()?);
654        Ok(Default::default())
655    }
656}
657
658impl Cheatcode for expectSafeMemoryCallCall {
659    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
660        let Self { min, max } = *self;
661        expect_safe_memory(ccx.state, min, max, (ccx.ecx.journaled_state.depth() + 1).try_into()?)
662    }
663}
664
665impl RevertParameters for ExpectedRevert {
666    fn reverter(&self) -> Option<Address> {
667        self.reverter
668    }
669
670    fn reason(&self) -> Option<&[u8]> {
671        self.reason.as_deref()
672    }
673
674    fn partial_match(&self) -> bool {
675        self.partial_match
676    }
677}
678
679/// Handles expected calls specified by the `expectCall` cheatcodes.
680///
681/// It can handle calls in two ways:
682/// - If the cheatcode was used with a `count` argument, it will expect the call to be made exactly
683///   `count` times. e.g. `vm.expectCall(address(0xc4f3), abi.encodeWithSelector(0xd34db33f), 4)`
684///   will expect the call to address(0xc4f3) with selector `0xd34db33f` to be made exactly 4 times.
685///   If the amount of calls is less or more than 4, the test will fail. Note that the `count`
686///   argument cannot be overwritten with another `vm.expectCall`. If this is attempted,
687///   `expectCall` will revert.
688/// - If the cheatcode was used without a `count` argument, it will expect the call to be made at
689///   least the amount of times the cheatcode was called. This means that `vm.expectCall` without a
690///   count argument can be called many times, but cannot be called with a `count` argument after it
691///   was called without one. If the latter happens, `expectCall` will revert. e.g
692///   `vm.expectCall(address(0xc4f3), abi.encodeWithSelector(0xd34db33f))` will expect the call to
693///   address(0xc4f3) and selector `0xd34db33f` to be made at least once. If the amount of calls is
694///   0, the test will fail. If the call is made more than once, the test will pass.
695#[expect(clippy::too_many_arguments)] // It is what it is
696fn expect_call(
697    state: &mut Cheatcodes,
698    target: &Address,
699    calldata: &Bytes,
700    value: Option<&U256>,
701    mut gas: Option<u64>,
702    mut min_gas: Option<u64>,
703    count: u64,
704    call_type: ExpectedCallType,
705) -> Result {
706    let expecteds = state.expected_calls.entry(*target).or_default();
707
708    if let Some(val) = value
709        && *val > U256::ZERO
710    {
711        // If the value of the transaction is non-zero, the EVM adds a call stipend of 2300 gas
712        // to ensure that the basic fallback function can be called.
713        let positive_value_cost_stipend = 2300;
714        if let Some(gas) = &mut gas {
715            *gas += positive_value_cost_stipend;
716        }
717        if let Some(min_gas) = &mut min_gas {
718            *min_gas += positive_value_cost_stipend;
719        }
720    }
721
722    match call_type {
723        ExpectedCallType::Count => {
724            // Get the expected calls for this target.
725            // In this case, as we're using counted expectCalls, we should not be able to set them
726            // more than once.
727            ensure!(
728                !expecteds.contains_key(calldata),
729                "counted expected calls can only bet set once"
730            );
731            expecteds.insert(
732                calldata.clone(),
733                (ExpectedCallData { value: value.copied(), gas, min_gas, count, call_type }, 0),
734            );
735        }
736        ExpectedCallType::NonCount => {
737            // Check if the expected calldata exists.
738            // If it does, increment the count by one as we expect to see it one more time.
739            match expecteds.entry(calldata.clone()) {
740                Entry::Occupied(mut entry) => {
741                    let (expected, _) = entry.get_mut();
742                    // Ensure we're not overwriting a counted expectCall.
743                    ensure!(
744                        expected.call_type == ExpectedCallType::NonCount,
745                        "cannot overwrite a counted expectCall with a non-counted expectCall"
746                    );
747                    expected.count += 1;
748                }
749                // If it does not exist, then create it.
750                Entry::Vacant(entry) => {
751                    entry.insert((
752                        ExpectedCallData { value: value.copied(), gas, min_gas, count, call_type },
753                        0,
754                    ));
755                }
756            }
757        }
758    }
759
760    Ok(Default::default())
761}
762
763fn expect_emit(
764    state: &mut Cheatcodes,
765    depth: usize,
766    checks: [bool; 5],
767    address: Option<Address>,
768    anonymous: bool,
769    count: u64,
770) -> Result {
771    let expected_emit = ExpectedEmit {
772        depth,
773        checks,
774        address,
775        found: false,
776        log: None,
777        anonymous,
778        count,
779        mismatch_error: None,
780    };
781    if let Some(found_emit_pos) = state.expected_emits.iter().position(|(emit, _)| emit.found) {
782        // The order of emits already found (back of queue) should not be modified, hence push any
783        // new emit before first found emit.
784        state.expected_emits.insert(found_emit_pos, (expected_emit, Default::default()));
785    } else {
786        // If no expected emits then push new one at the back of queue.
787        state.expected_emits.push_back((expected_emit, Default::default()));
788    }
789
790    Ok(Default::default())
791}
792
793pub(crate) fn handle_expect_emit(
794    state: &mut Cheatcodes,
795    log: &alloy_primitives::Log,
796    interpreter: &mut Interpreter,
797) {
798    // Fill or check the expected emits.
799    // We expect for emit checks to be filled as they're declared (from oldest to newest),
800    // so we fill them and push them to the back of the queue.
801    // If the user has properly filled all the emits, they'll end up in their original order.
802    // If not, the queue will not be in the order the events will be intended to be filled,
803    // and we'll be able to later detect this and bail.
804
805    // First, we can return early if all events have been matched.
806    // This allows a contract to arbitrarily emit more events than expected (additive behavior),
807    // as long as all the previous events were matched in the order they were expected to be.
808    if state.expected_emits.iter().all(|(expected, _)| expected.found) {
809        return;
810    }
811
812    // Check count=0 expectations against this log - fail immediately if violated
813    for (expected_emit, _) in &state.expected_emits {
814        if expected_emit.count == 0
815            && !expected_emit.found
816            && let Some(expected_log) = &expected_emit.log
817            && checks_topics_and_data(expected_emit.checks, expected_log, log)
818            // Check revert address 
819            && (expected_emit.address.is_none() || expected_emit.address == Some(log.address))
820        {
821            // This event was emitted but we expected it NOT to be (count=0)
822            // Fail immediately
823            interpreter.bytecode.set_action(InterpreterAction::new_return(
824                InstructionResult::Revert,
825                Error::encode("log emitted 1 time, expected 0"),
826                interpreter.gas,
827            ));
828            return;
829        }
830    }
831
832    let should_fill_logs = state.expected_emits.iter().any(|(expected, _)| expected.log.is_none());
833    let index_to_fill_or_check = if should_fill_logs {
834        // If there's anything to fill, we start with the last event to match in the queue
835        // (without taking into account events already matched).
836        state
837            .expected_emits
838            .iter()
839            .position(|(emit, _)| emit.found)
840            .unwrap_or(state.expected_emits.len())
841            .saturating_sub(1)
842    } else {
843        // if all expected logs are filled, check any unmatched event
844        // in the declared order, so we start from the front (like a queue).
845        // Skip count=0 expectations as they are handled separately above
846        state.expected_emits.iter().position(|(emit, _)| !emit.found && emit.count > 0).unwrap_or(0)
847    };
848
849    // If there are only count=0 expectations left, we can return early
850    if !should_fill_logs
851        && state.expected_emits.iter().all(|(emit, _)| emit.found || emit.count == 0)
852    {
853        return;
854    }
855
856    let (mut event_to_fill_or_check, mut count_map) = state
857        .expected_emits
858        .remove(index_to_fill_or_check)
859        .expect("we should have an emit to fill or check");
860
861    let Some(expected) = &event_to_fill_or_check.log else {
862        // Unless the caller is trying to match an anonymous event, the first topic must be
863        // filled.
864        if event_to_fill_or_check.anonymous || !log.topics().is_empty() {
865            event_to_fill_or_check.log = Some(log.data.clone());
866            // If we only filled the expected log then we put it back at the same position.
867            state
868                .expected_emits
869                .insert(index_to_fill_or_check, (event_to_fill_or_check, count_map));
870        } else {
871            interpreter.bytecode.set_action(InterpreterAction::new_return(
872                InstructionResult::Revert,
873                Error::encode("use vm.expectEmitAnonymous to match anonymous events"),
874                interpreter.gas,
875            ));
876        }
877        return;
878    };
879
880    // Increment/set `count` for `log.address` and `log.data`
881    match count_map.entry(log.address) {
882        Entry::Occupied(mut entry) => {
883            let log_count_map = entry.get_mut();
884            log_count_map.insert(&log.data);
885        }
886        Entry::Vacant(entry) => {
887            let mut log_count_map = LogCountMap::new(&event_to_fill_or_check);
888            if log_count_map.satisfies_checks(&log.data) {
889                log_count_map.insert(&log.data);
890                entry.insert(log_count_map);
891            }
892        }
893    }
894
895    event_to_fill_or_check.found = || -> bool {
896        if !checks_topics_and_data(event_to_fill_or_check.checks, expected, log) {
897            // Store detailed mismatch information
898
899            // Try to decode the events if we have a signature identifier
900            let (expected_decoded, actual_decoded) = if let Some(signatures_identifier) =
901                state.signatures_identifier()
902                && !event_to_fill_or_check.anonymous
903            {
904                (
905                    decode_event(signatures_identifier, expected),
906                    decode_event(signatures_identifier, log),
907                )
908            } else {
909                (None, None)
910            };
911            event_to_fill_or_check.mismatch_error = Some(get_emit_mismatch_message(
912                event_to_fill_or_check.checks,
913                expected,
914                log,
915                event_to_fill_or_check.anonymous,
916                expected_decoded.as_ref(),
917                actual_decoded.as_ref(),
918            ));
919            return false;
920        }
921
922        // Maybe match source address.
923        if event_to_fill_or_check
924            .address
925            .is_some_and(|addr| addr.to_checksum(None) != log.address.to_checksum(None))
926        {
927            event_to_fill_or_check.mismatch_error = Some(format!(
928                "log emitter mismatch: expected={:#x}, got={:#x}",
929                event_to_fill_or_check.address.unwrap(),
930                log.address
931            ));
932            return false;
933        }
934
935        let expected_count = event_to_fill_or_check.count;
936        match event_to_fill_or_check.address {
937            Some(emitter) => count_map
938                .get(&emitter)
939                .is_some_and(|log_map| log_map.count(&log.data) >= expected_count),
940            None => count_map
941                .values()
942                .find(|log_map| log_map.satisfies_checks(&log.data))
943                .is_some_and(|map| map.count(&log.data) >= expected_count),
944        }
945    }();
946
947    // If we found the event, we can push it to the back of the queue
948    // and begin expecting the next event.
949    if event_to_fill_or_check.found {
950        state.expected_emits.push_back((event_to_fill_or_check, count_map));
951    } else {
952        // We did not match this event, so we need to keep waiting for the right one to
953        // appear.
954        state.expected_emits.push_front((event_to_fill_or_check, count_map));
955    }
956}
957
958/// Handles expected emits specified by the `expectEmit` cheatcodes.
959///
960/// The second element of the tuple counts the number of times the log has been emitted by a
961/// particular address
962pub type ExpectedEmitTracker = VecDeque<(ExpectedEmit, AddressHashMap<LogCountMap>)>;
963
964#[derive(Clone, Debug, Default)]
965pub struct LogCountMap {
966    checks: [bool; 5],
967    expected_log: RawLog,
968    map: HashMap<RawLog, u64>,
969}
970
971impl LogCountMap {
972    /// Instantiates `LogCountMap`.
973    fn new(expected_emit: &ExpectedEmit) -> Self {
974        Self {
975            checks: expected_emit.checks,
976            expected_log: expected_emit.log.clone().expect("log should be filled here"),
977            map: Default::default(),
978        }
979    }
980
981    /// Inserts a log into the map and increments the count.
982    ///
983    /// The log must pass all checks against the expected log for the count to increment.
984    ///
985    /// Returns true if the log was inserted and count was incremented.
986    fn insert(&mut self, log: &RawLog) -> bool {
987        // If its already in the map, increment the count without checking.
988        if self.map.contains_key(log) {
989            self.map.entry(log.clone()).and_modify(|c| *c += 1);
990
991            return true;
992        }
993
994        if !self.satisfies_checks(log) {
995            return false;
996        }
997
998        self.map.entry(log.clone()).and_modify(|c| *c += 1).or_insert(1);
999
1000        true
1001    }
1002
1003    /// Checks the incoming raw log against the expected logs topics and data.
1004    fn satisfies_checks(&self, log: &RawLog) -> bool {
1005        checks_topics_and_data(self.checks, &self.expected_log, log)
1006    }
1007
1008    pub fn count(&self, log: &RawLog) -> u64 {
1009        if !self.satisfies_checks(log) {
1010            return 0;
1011        }
1012
1013        self.count_unchecked()
1014    }
1015
1016    pub fn count_unchecked(&self) -> u64 {
1017        self.map.values().sum()
1018    }
1019}
1020
1021fn expect_create(
1022    state: &mut Cheatcodes,
1023    bytecode: Bytes,
1024    deployer: Address,
1025    create_scheme: CreateScheme,
1026) -> Result {
1027    let expected_create = ExpectedCreate { bytecode, deployer, create_scheme };
1028    state.expected_creates.push(expected_create);
1029
1030    Ok(Default::default())
1031}
1032
1033fn expect_revert(
1034    state: &mut Cheatcodes,
1035    reason: Option<&[u8]>,
1036    depth: usize,
1037    cheatcode: bool,
1038    partial_match: bool,
1039    reverter: Option<Address>,
1040    count: u64,
1041) -> Result {
1042    ensure!(
1043        state.expected_revert.is_none(),
1044        "you must call another function prior to expecting a second revert"
1045    );
1046    state.expected_revert = Some(ExpectedRevert {
1047        reason: reason.map(<[_]>::to_vec),
1048        depth,
1049        kind: if cheatcode {
1050            ExpectedRevertKind::Cheatcode { pending_processing: true }
1051        } else {
1052            ExpectedRevertKind::Default
1053        },
1054        partial_match,
1055        reverter,
1056        reverted_by: None,
1057        max_depth: depth,
1058        count,
1059        actual_count: 0,
1060    });
1061    Ok(Default::default())
1062}
1063
1064fn checks_topics_and_data(checks: [bool; 5], expected: &RawLog, log: &RawLog) -> bool {
1065    if log.topics().len() != expected.topics().len() {
1066        return false;
1067    }
1068
1069    // Check topics.
1070    if !log
1071        .topics()
1072        .iter()
1073        .enumerate()
1074        .filter(|(i, _)| checks[*i])
1075        .all(|(i, topic)| topic == &expected.topics()[i])
1076    {
1077        return false;
1078    }
1079
1080    // Check data
1081    if checks[4] && expected.data.as_ref() != log.data.as_ref() {
1082        return false;
1083    }
1084
1085    true
1086}
1087
1088fn decode_event(
1089    identifier: &foundry_evm_traces::identifier::SignaturesIdentifier,
1090    log: &RawLog,
1091) -> Option<DecodedCallLog> {
1092    let topics = log.topics();
1093    if topics.is_empty() {
1094        return None;
1095    }
1096    let t0 = topics[0]; // event sig
1097    // Try to identify the event
1098    let event = foundry_common::block_on(identifier.identify_event(t0))?;
1099
1100    // Check if event already has indexed information from signatures
1101    let has_indexed_info = event.inputs.iter().any(|p| p.indexed);
1102    // Only use get_indexed_event if the event doesn't have indexing info
1103    let indexed_event = if has_indexed_info { event } else { get_indexed_event(event, log) };
1104
1105    // Try to decode the event
1106    if let Ok(decoded) = indexed_event.decode_log(log) {
1107        let params = reconstruct_params(&indexed_event, &decoded);
1108
1109        let decoded_params = params
1110            .into_iter()
1111            .zip(indexed_event.inputs.iter())
1112            .map(|(param, input)| (input.name.clone(), format_token(&param)))
1113            .collect();
1114
1115        return Some(DecodedCallLog {
1116            name: Some(indexed_event.name),
1117            params: Some(decoded_params),
1118        });
1119    }
1120
1121    None
1122}
1123
1124/// Restore the order of the params of a decoded event
1125fn reconstruct_params(event: &Event, decoded: &alloy_dyn_abi::DecodedEvent) -> Vec<DynSolValue> {
1126    let mut indexed = 0;
1127    let mut unindexed = 0;
1128    let mut inputs = vec![];
1129    for input in &event.inputs {
1130        if input.indexed && indexed < decoded.indexed.len() {
1131            inputs.push(decoded.indexed[indexed].clone());
1132            indexed += 1;
1133        } else if unindexed < decoded.body.len() {
1134            inputs.push(decoded.body[unindexed].clone());
1135            unindexed += 1;
1136        }
1137    }
1138    inputs
1139}
1140
1141/// Gets a detailed mismatch message for emit assertions
1142pub(crate) fn get_emit_mismatch_message(
1143    checks: [bool; 5],
1144    expected: &RawLog,
1145    actual: &RawLog,
1146    is_anonymous: bool,
1147    expected_decoded: Option<&DecodedCallLog>,
1148    actual_decoded: Option<&DecodedCallLog>,
1149) -> String {
1150    // Early return for completely different events or incompatible structures
1151
1152    // 1. Different number of topics
1153    if actual.topics().len() != expected.topics().len() {
1154        return name_mismatched_logs(expected_decoded, actual_decoded);
1155    }
1156
1157    // 2. Different event signatures (for non-anonymous events)
1158    if !is_anonymous
1159        && checks[0]
1160        && (!expected.topics().is_empty() && !actual.topics().is_empty())
1161        && expected.topics()[0] != actual.topics()[0]
1162    {
1163        return name_mismatched_logs(expected_decoded, actual_decoded);
1164    }
1165
1166    let expected_data = expected.data.as_ref();
1167    let actual_data = actual.data.as_ref();
1168
1169    // 3. Check data
1170    if checks[4] && expected_data != actual_data {
1171        // Different lengths or not ABI-encoded
1172        if expected_data.len() != actual_data.len()
1173            || !expected_data.len().is_multiple_of(32)
1174            || expected_data.is_empty()
1175        {
1176            return name_mismatched_logs(expected_decoded, actual_decoded);
1177        }
1178    }
1179
1180    // expected and actual events are the same, so check individual parameters
1181    let mut mismatches = Vec::new();
1182
1183    // Check topics (indexed parameters)
1184    for (i, (expected_topic, actual_topic)) in
1185        expected.topics().iter().zip(actual.topics().iter()).enumerate()
1186    {
1187        // Skip topic[0] for non-anonymous events (already checked above)
1188        if i == 0 && !is_anonymous {
1189            continue;
1190        }
1191
1192        // Only check if the corresponding check flag is set
1193        if i < checks.len() && checks[i] && expected_topic != actual_topic {
1194            let param_idx = if is_anonymous {
1195                i // For anonymous events, topic[0] is param 0
1196            } else {
1197                i - 1 // For regular events, topic[0] is event signature, so topic[1] is param 0
1198            };
1199            mismatches
1200                .push(format!("param {param_idx}: expected={expected_topic}, got={actual_topic}"));
1201        }
1202    }
1203
1204    // Check data (non-indexed parameters)
1205    if checks[4] && expected_data != actual_data {
1206        let num_indexed_params = if is_anonymous {
1207            expected.topics().len()
1208        } else {
1209            expected.topics().len().saturating_sub(1)
1210        };
1211
1212        for (i, (expected_chunk, actual_chunk)) in
1213            expected_data.chunks(32).zip(actual_data.chunks(32)).enumerate()
1214        {
1215            if expected_chunk != actual_chunk {
1216                let param_idx = num_indexed_params + i;
1217                mismatches.push(format!(
1218                    "param {}: expected={}, got={}",
1219                    param_idx,
1220                    hex::encode_prefixed(expected_chunk),
1221                    hex::encode_prefixed(actual_chunk)
1222                ));
1223            }
1224        }
1225    }
1226
1227    if mismatches.is_empty() {
1228        name_mismatched_logs(expected_decoded, actual_decoded)
1229    } else {
1230        // Build the error message with event names if available
1231        let event_prefix = match (expected_decoded, actual_decoded) {
1232            (Some(expected_dec), Some(actual_dec)) if expected_dec.name == actual_dec.name => {
1233                format!(
1234                    "{} param mismatch",
1235                    expected_dec.name.as_ref().unwrap_or(&"log".to_string())
1236                )
1237            }
1238            _ => {
1239                if is_anonymous {
1240                    "anonymous log mismatch".to_string()
1241                } else {
1242                    "log mismatch".to_string()
1243                }
1244            }
1245        };
1246
1247        // Add parameter details if available from decoded events
1248        let detailed_mismatches = if let (Some(expected_dec), Some(actual_dec)) =
1249            (expected_decoded, actual_decoded)
1250            && let (Some(expected_params), Some(actual_params)) =
1251                (&expected_dec.params, &actual_dec.params)
1252        {
1253            mismatches
1254                .into_iter()
1255                .map(|basic_mismatch| {
1256                    // Try to find the parameter name and decoded value
1257                    if let Some(param_idx) = basic_mismatch
1258                        .split(' ')
1259                        .nth(1)
1260                        .and_then(|s| s.trim_end_matches(':').parse::<usize>().ok())
1261                        && param_idx < expected_params.len()
1262                        && param_idx < actual_params.len()
1263                    {
1264                        let (expected_name, expected_value) = &expected_params[param_idx];
1265                        let (_actual_name, actual_value) = &actual_params[param_idx];
1266                        let param_name = if !expected_name.is_empty() {
1267                            expected_name
1268                        } else {
1269                            &format!("param{param_idx}")
1270                        };
1271                        return format!(
1272                            "{param_name}: expected={expected_value}, got={actual_value}",
1273                        );
1274                    }
1275                    basic_mismatch
1276                })
1277                .collect::<Vec<_>>()
1278        } else {
1279            mismatches
1280        };
1281
1282        format!("{} at {}", event_prefix, detailed_mismatches.join(", "))
1283    }
1284}
1285
1286/// Formats the generic mismatch message: "log != expected log" to include event names if available
1287fn name_mismatched_logs(
1288    expected_decoded: Option<&DecodedCallLog>,
1289    actual_decoded: Option<&DecodedCallLog>,
1290) -> String {
1291    let expected_name = expected_decoded.and_then(|d| d.name.as_deref()).unwrap_or("log");
1292    let actual_name = actual_decoded.and_then(|d| d.name.as_deref()).unwrap_or("log");
1293    format!("{actual_name} != expected {expected_name}")
1294}
1295
1296fn expect_safe_memory(state: &mut Cheatcodes, start: u64, end: u64, depth: u64) -> Result {
1297    ensure!(start < end, "memory range start ({start}) is greater than end ({end})");
1298    #[expect(clippy::single_range_in_vec_init)] // Wanted behaviour
1299    let offsets = state.allowed_mem_writes.entry(depth).or_insert_with(|| vec![0..0x60]);
1300    offsets.push(start..end);
1301    Ok(Default::default())
1302}