Skip to main content

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::{ContextTr, 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<Bytes>,
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<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
268        let Self { checkTopic1, checkTopic2, checkTopic3, checkData } = *self;
269        expect_emit(
270            ccx.state,
271            ccx.ecx.journal().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<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
282        let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self;
283        expect_emit(
284            ccx.state,
285            ccx.ecx.journal().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<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
296        let Self {} = self;
297        expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], None, false, 1)
298    }
299}
300
301impl Cheatcode for expectEmit_3Call {
302    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
303        let Self { emitter } = *self;
304        expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], Some(emitter), false, 1)
305    }
306}
307
308impl Cheatcode for expectEmit_4Call {
309    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
310        let Self { checkTopic1, checkTopic2, checkTopic3, checkData, count } = *self;
311        expect_emit(
312            ccx.state,
313            ccx.ecx.journal().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<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
324        let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter, count } = *self;
325        expect_emit(
326            ccx.state,
327            ccx.ecx.journal().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<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
338        let Self { count } = *self;
339        expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], None, false, count)
340    }
341}
342
343impl Cheatcode for expectEmit_7Call {
344    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
345        let Self { emitter, count } = *self;
346        expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], Some(emitter), false, count)
347    }
348}
349
350impl Cheatcode for expectEmitAnonymous_0Call {
351    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
352        let Self { checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData } = *self;
353        expect_emit(
354            ccx.state,
355            ccx.ecx.journal().depth(),
356            [checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData],
357            None,
358            true,
359            1,
360        )
361    }
362}
363
364impl Cheatcode for expectEmitAnonymous_1Call {
365    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
366        let Self { checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self;
367        expect_emit(
368            ccx.state,
369            ccx.ecx.journal().depth(),
370            [checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData],
371            Some(emitter),
372            true,
373            1,
374        )
375    }
376}
377
378impl Cheatcode for expectEmitAnonymous_2Call {
379    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
380        let Self {} = self;
381        expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], None, true, 1)
382    }
383}
384
385impl Cheatcode for expectEmitAnonymous_3Call {
386    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
387        let Self { emitter } = *self;
388        expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], Some(emitter), true, 1)
389    }
390}
391
392impl Cheatcode for expectCreateCall {
393    fn apply(&self, state: &mut Cheatcodes) -> Result {
394        let Self { bytecode, deployer } = self;
395        expect_create(state, bytecode.clone(), *deployer, CreateScheme::Create)
396    }
397}
398
399impl Cheatcode for expectCreate2Call {
400    fn apply(&self, state: &mut Cheatcodes) -> Result {
401        let Self { bytecode, deployer } = self;
402        expect_create(state, bytecode.clone(), *deployer, CreateScheme::Create2)
403    }
404}
405
406impl Cheatcode for expectRevert_0Call {
407    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
408        let Self {} = self;
409        expect_revert(ccx.state, None, ccx.ecx.journal().depth(), false, false, None, 1)
410    }
411}
412
413impl Cheatcode for expectRevert_1Call {
414    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
415        let Self { revertData } = self;
416        expect_revert(
417            ccx.state,
418            Some(revertData.as_ref()),
419            ccx.ecx.journal().depth(),
420            false,
421            false,
422            None,
423            1,
424        )
425    }
426}
427
428impl Cheatcode for expectRevert_2Call {
429    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
430        let Self { revertData } = self;
431        expect_revert(ccx.state, Some(revertData), ccx.ecx.journal().depth(), false, false, None, 1)
432    }
433}
434
435impl Cheatcode for expectRevert_3Call {
436    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
437        let Self { reverter } = self;
438        expect_revert(ccx.state, None, ccx.ecx.journal().depth(), false, false, Some(*reverter), 1)
439    }
440}
441
442impl Cheatcode for expectRevert_4Call {
443    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
444        let Self { revertData, reverter } = self;
445        expect_revert(
446            ccx.state,
447            Some(revertData.as_ref()),
448            ccx.ecx.journal().depth(),
449            false,
450            false,
451            Some(*reverter),
452            1,
453        )
454    }
455}
456
457impl Cheatcode for expectRevert_5Call {
458    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
459        let Self { revertData, reverter } = self;
460        expect_revert(
461            ccx.state,
462            Some(revertData),
463            ccx.ecx.journal().depth(),
464            false,
465            false,
466            Some(*reverter),
467            1,
468        )
469    }
470}
471
472impl Cheatcode for expectRevert_6Call {
473    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
474        let Self { count } = self;
475        expect_revert(ccx.state, None, ccx.ecx.journal().depth(), false, false, None, *count)
476    }
477}
478
479impl Cheatcode for expectRevert_7Call {
480    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
481        let Self { revertData, count } = self;
482        expect_revert(
483            ccx.state,
484            Some(revertData.as_ref()),
485            ccx.ecx.journal().depth(),
486            false,
487            false,
488            None,
489            *count,
490        )
491    }
492}
493
494impl Cheatcode for expectRevert_8Call {
495    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
496        let Self { revertData, count } = self;
497        expect_revert(
498            ccx.state,
499            Some(revertData),
500            ccx.ecx.journal().depth(),
501            false,
502            false,
503            None,
504            *count,
505        )
506    }
507}
508
509impl Cheatcode for expectRevert_9Call {
510    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
511        let Self { reverter, count } = self;
512        expect_revert(
513            ccx.state,
514            None,
515            ccx.ecx.journal().depth(),
516            false,
517            false,
518            Some(*reverter),
519            *count,
520        )
521    }
522}
523
524impl Cheatcode for expectRevert_10Call {
525    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
526        let Self { revertData, reverter, count } = self;
527        expect_revert(
528            ccx.state,
529            Some(revertData.as_ref()),
530            ccx.ecx.journal().depth(),
531            false,
532            false,
533            Some(*reverter),
534            *count,
535        )
536    }
537}
538
539impl Cheatcode for expectRevert_11Call {
540    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
541        let Self { revertData, reverter, count } = self;
542        expect_revert(
543            ccx.state,
544            Some(revertData),
545            ccx.ecx.journal().depth(),
546            false,
547            false,
548            Some(*reverter),
549            *count,
550        )
551    }
552}
553
554impl Cheatcode for expectPartialRevert_0Call {
555    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
556        let Self { revertData } = self;
557        expect_revert(
558            ccx.state,
559            Some(revertData.as_ref()),
560            ccx.ecx.journal().depth(),
561            false,
562            true,
563            None,
564            1,
565        )
566    }
567}
568
569impl Cheatcode for expectPartialRevert_1Call {
570    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
571        let Self { revertData, reverter } = self;
572        expect_revert(
573            ccx.state,
574            Some(revertData.as_ref()),
575            ccx.ecx.journal().depth(),
576            false,
577            true,
578            Some(*reverter),
579            1,
580        )
581    }
582}
583
584impl Cheatcode for _expectCheatcodeRevert_0Call {
585    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
586        expect_revert(ccx.state, None, ccx.ecx.journal().depth(), true, false, None, 1)
587    }
588}
589
590impl Cheatcode for _expectCheatcodeRevert_1Call {
591    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
592        let Self { revertData } = self;
593        expect_revert(
594            ccx.state,
595            Some(revertData.as_ref()),
596            ccx.ecx.journal().depth(),
597            true,
598            false,
599            None,
600            1,
601        )
602    }
603}
604
605impl Cheatcode for _expectCheatcodeRevert_2Call {
606    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
607        let Self { revertData } = self;
608        expect_revert(ccx.state, Some(revertData), ccx.ecx.journal().depth(), true, false, None, 1)
609    }
610}
611
612impl Cheatcode for expectSafeMemoryCall {
613    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
614        let Self { min, max } = *self;
615        expect_safe_memory(ccx.state, min, max, ccx.ecx.journal().depth().try_into()?)
616    }
617}
618
619impl Cheatcode for stopExpectSafeMemoryCall {
620    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
621        let Self {} = self;
622        ccx.state.allowed_mem_writes.remove(&ccx.ecx.journal().depth().try_into()?);
623        Ok(Default::default())
624    }
625}
626
627impl Cheatcode for expectSafeMemoryCallCall {
628    fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
629        let Self { min, max } = *self;
630        expect_safe_memory(ccx.state, min, max, (ccx.ecx.journal().depth() + 1).try_into()?)
631    }
632}
633
634impl RevertParameters for ExpectedRevert {
635    fn reverter(&self) -> Option<Address> {
636        self.reverter
637    }
638
639    fn reason(&self) -> Option<&[u8]> {
640        self.reason.as_ref().map(|b| &***b)
641    }
642
643    fn partial_match(&self) -> bool {
644        self.partial_match
645    }
646}
647
648/// Handles expected calls specified by the `expectCall` cheatcodes.
649///
650/// It can handle calls in two ways:
651/// - If the cheatcode was used with a `count` argument, it will expect the call to be made exactly
652///   `count` times. e.g. `vm.expectCall(address(0xc4f3), abi.encodeWithSelector(0xd34db33f), 4)`
653///   will expect the call to address(0xc4f3) with selector `0xd34db33f` to be made exactly 4 times.
654///   If the amount of calls is less or more than 4, the test will fail. Note that the `count`
655///   argument cannot be overwritten with another `vm.expectCall`. If this is attempted,
656///   `expectCall` will revert.
657/// - If the cheatcode was used without a `count` argument, it will expect the call to be made at
658///   least the amount of times the cheatcode was called. This means that `vm.expectCall` without a
659///   count argument can be called many times, but cannot be called with a `count` argument after it
660///   was called without one. If the latter happens, `expectCall` will revert. e.g
661///   `vm.expectCall(address(0xc4f3), abi.encodeWithSelector(0xd34db33f))` will expect the call to
662///   address(0xc4f3) and selector `0xd34db33f` to be made at least once. If the amount of calls is
663///   0, the test will fail. If the call is made more than once, the test will pass.
664#[expect(clippy::too_many_arguments)] // It is what it is
665fn expect_call(
666    state: &mut Cheatcodes,
667    target: &Address,
668    calldata: &Bytes,
669    value: Option<&U256>,
670    mut gas: Option<u64>,
671    mut min_gas: Option<u64>,
672    count: u64,
673    call_type: ExpectedCallType,
674) -> Result {
675    let expecteds = state.expected_calls.entry(*target).or_default();
676
677    if let Some(val) = value
678        && *val > U256::ZERO
679    {
680        // If the value of the transaction is non-zero, the EVM adds a call stipend of 2300 gas
681        // to ensure that the basic fallback function can be called.
682        let positive_value_cost_stipend = 2300;
683        if let Some(gas) = &mut gas {
684            *gas += positive_value_cost_stipend;
685        }
686        if let Some(min_gas) = &mut min_gas {
687            *min_gas += positive_value_cost_stipend;
688        }
689    }
690
691    match call_type {
692        ExpectedCallType::Count => {
693            // Get the expected calls for this target.
694            // In this case, as we're using counted expectCalls, we should not be able to set them
695            // more than once.
696            ensure!(
697                !expecteds.contains_key(calldata),
698                "counted expected calls can only bet set once"
699            );
700            expecteds.insert(
701                calldata.clone(),
702                (ExpectedCallData { value: value.copied(), gas, min_gas, count, call_type }, 0),
703            );
704        }
705        ExpectedCallType::NonCount => {
706            // Check if the expected calldata exists.
707            // If it does, increment the count by one as we expect to see it one more time.
708            match expecteds.entry(calldata.clone()) {
709                Entry::Occupied(mut entry) => {
710                    let (expected, _) = entry.get_mut();
711                    // Ensure we're not overwriting a counted expectCall.
712                    ensure!(
713                        expected.call_type == ExpectedCallType::NonCount,
714                        "cannot overwrite a counted expectCall with a non-counted expectCall"
715                    );
716                    expected.count += 1;
717                }
718                // If it does not exist, then create it.
719                Entry::Vacant(entry) => {
720                    entry.insert((
721                        ExpectedCallData { value: value.copied(), gas, min_gas, count, call_type },
722                        0,
723                    ));
724                }
725            }
726        }
727    }
728
729    Ok(Default::default())
730}
731
732fn expect_emit(
733    state: &mut Cheatcodes,
734    depth: usize,
735    checks: [bool; 5],
736    address: Option<Address>,
737    anonymous: bool,
738    count: u64,
739) -> Result {
740    let expected_emit = ExpectedEmit {
741        depth,
742        checks,
743        address,
744        found: false,
745        log: None,
746        anonymous,
747        count,
748        mismatch_error: None,
749    };
750    if let Some(found_emit_pos) = state.expected_emits.iter().position(|(emit, _)| emit.found) {
751        // The order of emits already found (back of queue) should not be modified, hence push any
752        // new emit before first found emit.
753        state.expected_emits.insert(found_emit_pos, (expected_emit, Default::default()));
754    } else {
755        // If no expected emits then push new one at the back of queue.
756        state.expected_emits.push_back((expected_emit, Default::default()));
757    }
758
759    Ok(Default::default())
760}
761
762pub(crate) fn handle_expect_emit(
763    state: &mut Cheatcodes,
764    log: &alloy_primitives::Log,
765    mut interpreter: Option<&mut Interpreter>,
766) -> Option<&'static str> {
767    // This function returns an optional string indicating a failure reason.
768    // If the string is `Some`, it indicates that the expectation failed with the provided reason.
769    let mut should_fail = None;
770
771    // Fill or check the expected emits.
772    // We expect for emit checks to be filled as they're declared (from oldest to newest),
773    // so we fill them and push them to the back of the queue.
774    // If the user has properly filled all the emits, they'll end up in their original order.
775    // If not, the queue will not be in the order the events will be intended to be filled,
776    // and we'll be able to later detect this and bail.
777
778    // First, we can return early if all events have been matched.
779    // This allows a contract to arbitrarily emit more events than expected (additive behavior),
780    // as long as all the previous events were matched in the order they were expected to be.
781    if state.expected_emits.iter().all(|(expected, _)| expected.found) {
782        return should_fail;
783    }
784
785    // Check count=0 expectations against this log - fail immediately if violated
786    for (expected_emit, _) in &state.expected_emits {
787        if expected_emit.count == 0
788            && !expected_emit.found
789            && let Some(expected_log) = &expected_emit.log
790            && checks_topics_and_data(expected_emit.checks, expected_log, log)
791            // Check revert address 
792            && (expected_emit.address.is_none() || expected_emit.address == Some(log.address))
793        {
794            if let Some(interpreter) = &mut interpreter {
795                // This event was emitted but we expected it NOT to be (count=0)
796                // Fail immediately
797                interpreter.bytecode.set_action(InterpreterAction::new_return(
798                    InstructionResult::Revert,
799                    Error::encode("log emitted but expected 0 times"),
800                    interpreter.gas,
801                ));
802            } else {
803                should_fail = Some("log emitted but expected 0 times");
804            }
805
806            return should_fail;
807        }
808    }
809
810    let should_fill_logs = state.expected_emits.iter().any(|(expected, _)| expected.log.is_none());
811    let index_to_fill_or_check = if should_fill_logs {
812        // If there's anything to fill, we start with the last event to match in the queue
813        // (without taking into account events already matched).
814        state
815            .expected_emits
816            .iter()
817            .position(|(emit, _)| emit.found)
818            .unwrap_or(state.expected_emits.len())
819            .saturating_sub(1)
820    } else {
821        // if all expected logs are filled, check any unmatched event
822        // in the declared order, so we start from the front (like a queue).
823        // Skip count=0 expectations as they are handled separately above
824        state.expected_emits.iter().position(|(emit, _)| !emit.found && emit.count > 0).unwrap_or(0)
825    };
826
827    // If there are only count=0 expectations left, we can return early
828    if !should_fill_logs
829        && state.expected_emits.iter().all(|(emit, _)| emit.found || emit.count == 0)
830    {
831        return should_fail;
832    }
833
834    let (mut event_to_fill_or_check, mut count_map) = state
835        .expected_emits
836        .remove(index_to_fill_or_check)
837        .expect("we should have an emit to fill or check");
838
839    let Some(expected) = &event_to_fill_or_check.log else {
840        // Unless the caller is trying to match an anonymous event, the first topic must be
841        // filled.
842        if event_to_fill_or_check.anonymous || !log.topics().is_empty() {
843            event_to_fill_or_check.log = Some(log.data.clone());
844            // If we only filled the expected log then we put it back at the same position.
845            state
846                .expected_emits
847                .insert(index_to_fill_or_check, (event_to_fill_or_check, count_map));
848        } else if let Some(interpreter) = &mut interpreter {
849            interpreter.bytecode.set_action(InterpreterAction::new_return(
850                InstructionResult::Revert,
851                Error::encode("use vm.expectEmitAnonymous to match anonymous events"),
852                interpreter.gas,
853            ));
854        } else {
855            should_fail = Some("use vm.expectEmitAnonymous to match anonymous events");
856        }
857
858        return should_fail;
859    };
860
861    // Increment/set `count` for `log.address` and `log.data`
862    match count_map.entry(log.address) {
863        Entry::Occupied(mut entry) => {
864            let log_count_map = entry.get_mut();
865            log_count_map.insert(&log.data);
866        }
867        Entry::Vacant(entry) => {
868            let mut log_count_map = LogCountMap::new(&event_to_fill_or_check);
869            if log_count_map.satisfies_checks(&log.data) {
870                log_count_map.insert(&log.data);
871                entry.insert(log_count_map);
872            }
873        }
874    }
875
876    event_to_fill_or_check.found = || -> bool {
877        if !checks_topics_and_data(event_to_fill_or_check.checks, expected, log) {
878            // Store detailed mismatch information
879
880            // Try to decode the events if we have a signature identifier
881            let (expected_decoded, actual_decoded) = if let Some(signatures_identifier) =
882                state.signatures_identifier()
883                && !event_to_fill_or_check.anonymous
884            {
885                (
886                    decode_event(signatures_identifier, expected),
887                    decode_event(signatures_identifier, log),
888                )
889            } else {
890                (None, None)
891            };
892            event_to_fill_or_check.mismatch_error = Some(get_emit_mismatch_message(
893                event_to_fill_or_check.checks,
894                expected,
895                log,
896                event_to_fill_or_check.anonymous,
897                expected_decoded.as_ref(),
898                actual_decoded.as_ref(),
899            ));
900            return false;
901        }
902
903        // Maybe match source address.
904        if event_to_fill_or_check.address.is_some_and(|addr| addr != log.address) {
905            event_to_fill_or_check.mismatch_error = Some(format!(
906                "log emitter mismatch: expected={:#x}, got={:#x}",
907                event_to_fill_or_check.address.unwrap(),
908                log.address
909            ));
910            return false;
911        }
912
913        let expected_count = event_to_fill_or_check.count;
914        match event_to_fill_or_check.address {
915            Some(emitter) => count_map
916                .get(&emitter)
917                .is_some_and(|log_map| log_map.count(&log.data) >= expected_count),
918            None => count_map
919                .values()
920                .find(|log_map| log_map.satisfies_checks(&log.data))
921                .is_some_and(|map| map.count(&log.data) >= expected_count),
922        }
923    }();
924
925    // If we found the event, we can push it to the back of the queue
926    // and begin expecting the next event.
927    if event_to_fill_or_check.found {
928        state.expected_emits.push_back((event_to_fill_or_check, count_map));
929    } else {
930        // We did not match this event, so we need to keep waiting for the right one to
931        // appear.
932        state.expected_emits.push_front((event_to_fill_or_check, count_map));
933    }
934
935    should_fail
936}
937
938/// Handles expected emits specified by the `expectEmit` cheatcodes.
939///
940/// The second element of the tuple counts the number of times the log has been emitted by a
941/// particular address
942pub type ExpectedEmitTracker = VecDeque<(ExpectedEmit, AddressHashMap<LogCountMap>)>;
943
944#[derive(Clone, Debug, Default)]
945pub struct LogCountMap {
946    checks: [bool; 5],
947    expected_log: RawLog,
948    map: HashMap<RawLog, u64>,
949}
950
951impl LogCountMap {
952    /// Instantiates `LogCountMap`.
953    fn new(expected_emit: &ExpectedEmit) -> Self {
954        Self {
955            checks: expected_emit.checks,
956            expected_log: expected_emit.log.clone().expect("log should be filled here"),
957            map: Default::default(),
958        }
959    }
960
961    /// Inserts a log into the map and increments the count.
962    ///
963    /// The log must pass all checks against the expected log for the count to increment.
964    ///
965    /// Returns true if the log was inserted and count was incremented.
966    fn insert(&mut self, log: &RawLog) -> bool {
967        // If its already in the map, increment the count without checking.
968        if self.map.contains_key(log) {
969            self.map.entry(log.clone()).and_modify(|c| *c += 1);
970
971            return true;
972        }
973
974        if !self.satisfies_checks(log) {
975            return false;
976        }
977
978        self.map.entry(log.clone()).and_modify(|c| *c += 1).or_insert(1);
979
980        true
981    }
982
983    /// Checks the incoming raw log against the expected logs topics and data.
984    fn satisfies_checks(&self, log: &RawLog) -> bool {
985        checks_topics_and_data(self.checks, &self.expected_log, log)
986    }
987
988    pub fn count(&self, log: &RawLog) -> u64 {
989        if !self.satisfies_checks(log) {
990            return 0;
991        }
992
993        self.count_unchecked()
994    }
995
996    pub fn count_unchecked(&self) -> u64 {
997        self.map.values().sum()
998    }
999}
1000
1001fn expect_create(
1002    state: &mut Cheatcodes,
1003    bytecode: Bytes,
1004    deployer: Address,
1005    create_scheme: CreateScheme,
1006) -> Result {
1007    let expected_create = ExpectedCreate { bytecode, deployer, create_scheme };
1008    state.expected_creates.push(expected_create);
1009
1010    Ok(Default::default())
1011}
1012
1013fn expect_revert(
1014    state: &mut Cheatcodes,
1015    reason: Option<&[u8]>,
1016    depth: usize,
1017    cheatcode: bool,
1018    partial_match: bool,
1019    reverter: Option<Address>,
1020    count: u64,
1021) -> Result {
1022    ensure!(
1023        state.expected_revert.is_none(),
1024        "you must call another function prior to expecting a second revert"
1025    );
1026    state.expected_revert = Some(ExpectedRevert {
1027        reason: reason.map(Bytes::copy_from_slice),
1028        depth,
1029        kind: if cheatcode {
1030            ExpectedRevertKind::Cheatcode { pending_processing: true }
1031        } else {
1032            ExpectedRevertKind::Default
1033        },
1034        partial_match,
1035        reverter,
1036        reverted_by: None,
1037        max_depth: depth,
1038        count,
1039        actual_count: 0,
1040    });
1041    Ok(Default::default())
1042}
1043
1044fn checks_topics_and_data(checks: [bool; 5], expected: &RawLog, log: &RawLog) -> bool {
1045    if log.topics().len() != expected.topics().len() {
1046        return false;
1047    }
1048
1049    // Check topics.
1050    if !log
1051        .topics()
1052        .iter()
1053        .enumerate()
1054        .filter(|(i, _)| checks[*i])
1055        .all(|(i, topic)| topic == &expected.topics()[i])
1056    {
1057        return false;
1058    }
1059
1060    // Check data
1061    if checks[4] && expected.data.as_ref() != log.data.as_ref() {
1062        return false;
1063    }
1064
1065    true
1066}
1067
1068fn decode_event(
1069    identifier: &foundry_evm_traces::identifier::SignaturesIdentifier,
1070    log: &RawLog,
1071) -> Option<DecodedCallLog> {
1072    let topics = log.topics();
1073    if topics.is_empty() {
1074        return None;
1075    }
1076    let t0 = topics[0]; // event sig
1077    // Try to identify the event
1078    let event = foundry_common::block_on(identifier.identify_event(t0))?;
1079
1080    // Check if event already has indexed information from signatures
1081    let has_indexed_info = event.inputs.iter().any(|p| p.indexed);
1082    // Only use get_indexed_event if the event doesn't have indexing info
1083    let indexed_event = if has_indexed_info { event } else { get_indexed_event(event, log) };
1084
1085    // Try to decode the event
1086    if let Ok(decoded) = indexed_event.decode_log(log) {
1087        let params = reconstruct_params(&indexed_event, &decoded);
1088
1089        let decoded_params = params
1090            .into_iter()
1091            .zip(indexed_event.inputs.iter())
1092            .map(|(param, input)| (input.name.clone(), format_token(&param)))
1093            .collect();
1094
1095        return Some(DecodedCallLog {
1096            name: Some(indexed_event.name),
1097            params: Some(decoded_params),
1098        });
1099    }
1100
1101    None
1102}
1103
1104/// Restore the order of the params of a decoded event
1105fn reconstruct_params(event: &Event, decoded: &alloy_dyn_abi::DecodedEvent) -> Vec<DynSolValue> {
1106    let mut indexed = 0;
1107    let mut unindexed = 0;
1108    let mut inputs = vec![];
1109    for input in &event.inputs {
1110        if input.indexed && indexed < decoded.indexed.len() {
1111            inputs.push(decoded.indexed[indexed].clone());
1112            indexed += 1;
1113        } else if unindexed < decoded.body.len() {
1114            inputs.push(decoded.body[unindexed].clone());
1115            unindexed += 1;
1116        }
1117    }
1118    inputs
1119}
1120
1121/// Gets a detailed mismatch message for emit assertions
1122pub(crate) fn get_emit_mismatch_message(
1123    checks: [bool; 5],
1124    expected: &RawLog,
1125    actual: &RawLog,
1126    is_anonymous: bool,
1127    expected_decoded: Option<&DecodedCallLog>,
1128    actual_decoded: Option<&DecodedCallLog>,
1129) -> String {
1130    // Early return for completely different events or incompatible structures
1131
1132    // 1. Different number of topics
1133    if actual.topics().len() != expected.topics().len() {
1134        return name_mismatched_logs(expected_decoded, actual_decoded);
1135    }
1136
1137    // 2. Different event signatures (for non-anonymous events)
1138    if !is_anonymous
1139        && checks[0]
1140        && (!expected.topics().is_empty() && !actual.topics().is_empty())
1141        && expected.topics()[0] != actual.topics()[0]
1142    {
1143        return name_mismatched_logs(expected_decoded, actual_decoded);
1144    }
1145
1146    let expected_data = expected.data.as_ref();
1147    let actual_data = actual.data.as_ref();
1148
1149    // 3. Check data
1150    if checks[4] && expected_data != actual_data {
1151        // Different lengths or not ABI-encoded
1152        if expected_data.len() != actual_data.len()
1153            || !expected_data.len().is_multiple_of(32)
1154            || expected_data.is_empty()
1155        {
1156            return name_mismatched_logs(expected_decoded, actual_decoded);
1157        }
1158    }
1159
1160    // expected and actual events are the same, so check individual parameters
1161    let mut mismatches = Vec::new();
1162
1163    // Check topics (indexed parameters)
1164    for (i, (expected_topic, actual_topic)) in
1165        expected.topics().iter().zip(actual.topics().iter()).enumerate()
1166    {
1167        // Skip topic[0] for non-anonymous events (already checked above)
1168        if i == 0 && !is_anonymous {
1169            continue;
1170        }
1171
1172        // Only check if the corresponding check flag is set
1173        if i < checks.len() && checks[i] && expected_topic != actual_topic {
1174            let param_idx = if is_anonymous {
1175                i // For anonymous events, topic[0] is param 0
1176            } else {
1177                i - 1 // For regular events, topic[0] is event signature, so topic[1] is param 0
1178            };
1179            mismatches
1180                .push(format!("param {param_idx}: expected={expected_topic}, got={actual_topic}"));
1181        }
1182    }
1183
1184    // Check data (non-indexed parameters)
1185    if checks[4] && expected_data != actual_data {
1186        let num_indexed_params = if is_anonymous {
1187            expected.topics().len()
1188        } else {
1189            expected.topics().len().saturating_sub(1)
1190        };
1191
1192        for (i, (expected_chunk, actual_chunk)) in
1193            expected_data.chunks(32).zip(actual_data.chunks(32)).enumerate()
1194        {
1195            if expected_chunk != actual_chunk {
1196                let param_idx = num_indexed_params + i;
1197                mismatches.push(format!(
1198                    "param {}: expected={}, got={}",
1199                    param_idx,
1200                    hex::encode_prefixed(expected_chunk),
1201                    hex::encode_prefixed(actual_chunk)
1202                ));
1203            }
1204        }
1205    }
1206
1207    if mismatches.is_empty() {
1208        name_mismatched_logs(expected_decoded, actual_decoded)
1209    } else {
1210        // Build the error message with event names if available
1211        let event_prefix = match (expected_decoded, actual_decoded) {
1212            (Some(expected_dec), Some(actual_dec)) if expected_dec.name == actual_dec.name => {
1213                format!(
1214                    "{} param mismatch",
1215                    expected_dec.name.as_ref().unwrap_or(&"log".to_string())
1216                )
1217            }
1218            _ => {
1219                if is_anonymous {
1220                    "anonymous log mismatch".to_string()
1221                } else {
1222                    "log mismatch".to_string()
1223                }
1224            }
1225        };
1226
1227        // Add parameter details if available from decoded events
1228        let detailed_mismatches = if let (Some(expected_dec), Some(actual_dec)) =
1229            (expected_decoded, actual_decoded)
1230            && let (Some(expected_params), Some(actual_params)) =
1231                (&expected_dec.params, &actual_dec.params)
1232        {
1233            mismatches
1234                .into_iter()
1235                .map(|basic_mismatch| {
1236                    // Try to find the parameter name and decoded value
1237                    if let Some(param_idx) = basic_mismatch
1238                        .split(' ')
1239                        .nth(1)
1240                        .and_then(|s| s.trim_end_matches(':').parse::<usize>().ok())
1241                        && param_idx < expected_params.len()
1242                        && param_idx < actual_params.len()
1243                    {
1244                        let (expected_name, expected_value) = &expected_params[param_idx];
1245                        let (_actual_name, actual_value) = &actual_params[param_idx];
1246                        let param_name = if !expected_name.is_empty() {
1247                            expected_name
1248                        } else {
1249                            &format!("param{param_idx}")
1250                        };
1251                        return format!(
1252                            "{param_name}: expected={expected_value}, got={actual_value}",
1253                        );
1254                    }
1255                    basic_mismatch
1256                })
1257                .collect::<Vec<_>>()
1258        } else {
1259            mismatches
1260        };
1261
1262        format!("{} at {}", event_prefix, detailed_mismatches.join(", "))
1263    }
1264}
1265
1266/// Formats the generic mismatch message: "log != expected log" to include event names if available
1267fn name_mismatched_logs(
1268    expected_decoded: Option<&DecodedCallLog>,
1269    actual_decoded: Option<&DecodedCallLog>,
1270) -> String {
1271    let expected_name = expected_decoded.and_then(|d| d.name.as_deref()).unwrap_or("log");
1272    let actual_name = actual_decoded.and_then(|d| d.name.as_deref()).unwrap_or("log");
1273    format!("{actual_name} != expected {expected_name}")
1274}
1275
1276fn expect_safe_memory(state: &mut Cheatcodes, start: u64, end: u64, depth: u64) -> Result {
1277    ensure!(start < end, "memory range start ({start}) is greater than end ({end})");
1278    #[expect(clippy::single_range_in_vec_init)] // Wanted behaviour
1279    let offsets = state.allowed_mem_writes.entry(depth).or_insert_with(|| vec![0..0x60]);
1280    offsets.push(start..end);
1281    Ok(Default::default())
1282}