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