1use std::{
2 collections::VecDeque,
3 fmt::{self, Display},
4};
5
6use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Error, Result, Vm::*};
7use alloy_primitives::{
8 map::{hash_map::Entry, AddressHashMap, HashMap},
9 Address, Bytes, LogData as RawLog, U256,
10};
11use revm::{
12 context::JournalTr,
13 interpreter::{InstructionResult, Interpreter, InterpreterAction, InterpreterResult},
14};
15
16use super::revert_handlers::RevertParameters;
17pub type ExpectedCallTracker = HashMap<Address, HashMap<Bytes, (ExpectedCallData, u64)>>;
27
28#[derive(Clone, Debug)]
29pub struct ExpectedCallData {
30 pub value: Option<U256>,
32 pub gas: Option<u64>,
34 pub min_gas: Option<u64>,
36 pub count: u64,
41 pub call_type: ExpectedCallType,
43}
44
45#[derive(Clone, Debug, PartialEq, Eq)]
47pub enum ExpectedCallType {
48 NonCount,
50 Count,
52}
53
54#[derive(Clone, Debug)]
56pub enum ExpectedRevertKind {
57 Default,
59 Cheatcode { pending_processing: bool },
65}
66
67#[derive(Clone, Debug)]
68pub struct ExpectedRevert {
69 pub reason: Option<Vec<u8>>,
71 pub depth: usize,
73 pub kind: ExpectedRevertKind,
75 pub partial_match: bool,
77 pub reverter: Option<Address>,
79 pub reverted_by: Option<Address>,
81 pub max_depth: usize,
83 pub count: u64,
85 pub actual_count: u64,
87}
88
89#[derive(Clone, Debug)]
90pub struct ExpectedEmit {
91 pub depth: usize,
93 pub log: Option<RawLog>,
95 pub checks: [bool; 5],
102 pub address: Option<Address>,
104 pub anonymous: bool,
107 pub found: bool,
109 pub count: u64,
111}
112
113#[derive(Clone, Debug)]
114pub struct ExpectedCreate {
115 pub deployer: Address,
117 pub bytecode: Bytes,
119 pub create_scheme: CreateScheme,
121}
122
123#[derive(Clone, Debug)]
124pub enum CreateScheme {
125 Create,
126 Create2,
127}
128
129impl Display for CreateScheme {
130 fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
131 match self {
132 Self::Create => write!(f, "CREATE"),
133 Self::Create2 => write!(f, "CREATE2"),
134 }
135 }
136}
137
138impl From<revm::context_interface::CreateScheme> for CreateScheme {
139 fn from(scheme: revm::context_interface::CreateScheme) -> Self {
140 match scheme {
141 revm::context_interface::CreateScheme::Create => Self::Create,
142 revm::context_interface::CreateScheme::Create2 { .. } => Self::Create2,
143 _ => unimplemented!("Unsupported create scheme"),
144 }
145 }
146}
147
148impl CreateScheme {
149 pub fn eq(&self, create_scheme: Self) -> bool {
150 matches!(
151 (self, create_scheme),
152 (Self::Create, Self::Create) | (Self::Create2, Self::Create2 { .. })
153 )
154 }
155}
156
157impl Cheatcode for expectCall_0Call {
158 fn apply(&self, state: &mut Cheatcodes) -> Result {
159 let Self { callee, data } = self;
160 expect_call(state, callee, data, None, None, None, 1, ExpectedCallType::NonCount)
161 }
162}
163
164impl Cheatcode for expectCall_1Call {
165 fn apply(&self, state: &mut Cheatcodes) -> Result {
166 let Self { callee, data, count } = self;
167 expect_call(state, callee, data, None, None, None, *count, ExpectedCallType::Count)
168 }
169}
170
171impl Cheatcode for expectCall_2Call {
172 fn apply(&self, state: &mut Cheatcodes) -> Result {
173 let Self { callee, msgValue, data } = self;
174 expect_call(state, callee, data, Some(msgValue), None, None, 1, ExpectedCallType::NonCount)
175 }
176}
177
178impl Cheatcode for expectCall_3Call {
179 fn apply(&self, state: &mut Cheatcodes) -> Result {
180 let Self { callee, msgValue, data, count } = self;
181 expect_call(
182 state,
183 callee,
184 data,
185 Some(msgValue),
186 None,
187 None,
188 *count,
189 ExpectedCallType::Count,
190 )
191 }
192}
193
194impl Cheatcode for expectCall_4Call {
195 fn apply(&self, state: &mut Cheatcodes) -> Result {
196 let Self { callee, msgValue, gas, data } = self;
197 expect_call(
198 state,
199 callee,
200 data,
201 Some(msgValue),
202 Some(*gas),
203 None,
204 1,
205 ExpectedCallType::NonCount,
206 )
207 }
208}
209
210impl Cheatcode for expectCall_5Call {
211 fn apply(&self, state: &mut Cheatcodes) -> Result {
212 let Self { callee, msgValue, gas, data, count } = self;
213 expect_call(
214 state,
215 callee,
216 data,
217 Some(msgValue),
218 Some(*gas),
219 None,
220 *count,
221 ExpectedCallType::Count,
222 )
223 }
224}
225
226impl Cheatcode for expectCallMinGas_0Call {
227 fn apply(&self, state: &mut Cheatcodes) -> Result {
228 let Self { callee, msgValue, minGas, data } = self;
229 expect_call(
230 state,
231 callee,
232 data,
233 Some(msgValue),
234 None,
235 Some(*minGas),
236 1,
237 ExpectedCallType::NonCount,
238 )
239 }
240}
241
242impl Cheatcode for expectCallMinGas_1Call {
243 fn apply(&self, state: &mut Cheatcodes) -> Result {
244 let Self { callee, msgValue, minGas, data, count } = self;
245 expect_call(
246 state,
247 callee,
248 data,
249 Some(msgValue),
250 None,
251 Some(*minGas),
252 *count,
253 ExpectedCallType::Count,
254 )
255 }
256}
257
258impl Cheatcode for expectEmit_0Call {
259 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
260 let Self { checkTopic1, checkTopic2, checkTopic3, checkData } = *self;
261 expect_emit(
262 ccx.state,
263 ccx.ecx.journaled_state.depth(),
264 [true, checkTopic1, checkTopic2, checkTopic3, checkData],
265 None,
266 false,
267 1,
268 )
269 }
270}
271
272impl Cheatcode for expectEmit_1Call {
273 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
274 let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self;
275 expect_emit(
276 ccx.state,
277 ccx.ecx.journaled_state.depth(),
278 [true, checkTopic1, checkTopic2, checkTopic3, checkData],
279 Some(emitter),
280 false,
281 1,
282 )
283 }
284}
285
286impl Cheatcode for expectEmit_2Call {
287 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
288 let Self {} = self;
289 expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, false, 1)
290 }
291}
292
293impl Cheatcode for expectEmit_3Call {
294 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
295 let Self { emitter } = *self;
296 expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], Some(emitter), false, 1)
297 }
298}
299
300impl Cheatcode for expectEmit_4Call {
301 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
302 let Self { checkTopic1, checkTopic2, checkTopic3, checkData, count } = *self;
303 expect_emit(
304 ccx.state,
305 ccx.ecx.journaled_state.depth(),
306 [true, checkTopic1, checkTopic2, checkTopic3, checkData],
307 None,
308 false,
309 count,
310 )
311 }
312}
313
314impl Cheatcode for expectEmit_5Call {
315 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
316 let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter, count } = *self;
317 expect_emit(
318 ccx.state,
319 ccx.ecx.journaled_state.depth(),
320 [true, checkTopic1, checkTopic2, checkTopic3, checkData],
321 Some(emitter),
322 false,
323 count,
324 )
325 }
326}
327
328impl Cheatcode for expectEmit_6Call {
329 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
330 let Self { count } = *self;
331 expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, false, count)
332 }
333}
334
335impl Cheatcode for expectEmit_7Call {
336 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
337 let Self { emitter, count } = *self;
338 expect_emit(
339 ccx.state,
340 ccx.ecx.journaled_state.depth(),
341 [true; 5],
342 Some(emitter),
343 false,
344 count,
345 )
346 }
347}
348
349impl Cheatcode for expectEmitAnonymous_0Call {
350 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
351 let Self { checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData } = *self;
352 expect_emit(
353 ccx.state,
354 ccx.ecx.journaled_state.depth(),
355 [checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData],
356 None,
357 true,
358 1,
359 )
360 }
361}
362
363impl Cheatcode for expectEmitAnonymous_1Call {
364 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
365 let Self { checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self;
366 expect_emit(
367 ccx.state,
368 ccx.ecx.journaled_state.depth(),
369 [checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData],
370 Some(emitter),
371 true,
372 1,
373 )
374 }
375}
376
377impl Cheatcode for expectEmitAnonymous_2Call {
378 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
379 let Self {} = self;
380 expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, true, 1)
381 }
382}
383
384impl Cheatcode for expectEmitAnonymous_3Call {
385 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
386 let Self { emitter } = *self;
387 expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], Some(emitter), true, 1)
388 }
389}
390
391impl Cheatcode for expectCreateCall {
392 fn apply(&self, state: &mut Cheatcodes) -> Result {
393 let Self { bytecode, deployer } = self;
394 expect_create(state, bytecode.clone(), *deployer, CreateScheme::Create)
395 }
396}
397
398impl Cheatcode for expectCreate2Call {
399 fn apply(&self, state: &mut Cheatcodes) -> Result {
400 let Self { bytecode, deployer } = self;
401 expect_create(state, bytecode.clone(), *deployer, CreateScheme::Create2)
402 }
403}
404
405impl Cheatcode for expectRevert_0Call {
406 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
407 let Self {} = self;
408 expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), false, false, None, 1)
409 }
410}
411
412impl Cheatcode for expectRevert_1Call {
413 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
414 let Self { revertData } = self;
415 expect_revert(
416 ccx.state,
417 Some(revertData.as_ref()),
418 ccx.ecx.journaled_state.depth(),
419 false,
420 false,
421 None,
422 1,
423 )
424 }
425}
426
427impl Cheatcode for expectRevert_2Call {
428 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
429 let Self { revertData } = self;
430 expect_revert(
431 ccx.state,
432 Some(revertData),
433 ccx.ecx.journaled_state.depth(),
434 false,
435 false,
436 None,
437 1,
438 )
439 }
440}
441
442impl Cheatcode for expectRevert_3Call {
443 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
444 let Self { reverter } = self;
445 expect_revert(
446 ccx.state,
447 None,
448 ccx.ecx.journaled_state.depth(),
449 false,
450 false,
451 Some(*reverter),
452 1,
453 )
454 }
455}
456
457impl Cheatcode for expectRevert_4Call {
458 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
459 let Self { revertData, reverter } = self;
460 expect_revert(
461 ccx.state,
462 Some(revertData.as_ref()),
463 ccx.ecx.journaled_state.depth(),
464 false,
465 false,
466 Some(*reverter),
467 1,
468 )
469 }
470}
471
472impl Cheatcode for expectRevert_5Call {
473 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
474 let Self { revertData, reverter } = self;
475 expect_revert(
476 ccx.state,
477 Some(revertData),
478 ccx.ecx.journaled_state.depth(),
479 false,
480 false,
481 Some(*reverter),
482 1,
483 )
484 }
485}
486
487impl Cheatcode for expectRevert_6Call {
488 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
489 let Self { count } = self;
490 expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), false, false, None, *count)
491 }
492}
493
494impl Cheatcode for expectRevert_7Call {
495 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
496 let Self { revertData, count } = self;
497 expect_revert(
498 ccx.state,
499 Some(revertData.as_ref()),
500 ccx.ecx.journaled_state.depth(),
501 false,
502 false,
503 None,
504 *count,
505 )
506 }
507}
508
509impl Cheatcode for expectRevert_8Call {
510 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
511 let Self { revertData, count } = self;
512 expect_revert(
513 ccx.state,
514 Some(revertData),
515 ccx.ecx.journaled_state.depth(),
516 false,
517 false,
518 None,
519 *count,
520 )
521 }
522}
523
524impl Cheatcode for expectRevert_9Call {
525 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
526 let Self { reverter, count } = self;
527 expect_revert(
528 ccx.state,
529 None,
530 ccx.ecx.journaled_state.depth(),
531 false,
532 false,
533 Some(*reverter),
534 *count,
535 )
536 }
537}
538
539impl Cheatcode for expectRevert_10Call {
540 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
541 let Self { revertData, reverter, count } = self;
542 expect_revert(
543 ccx.state,
544 Some(revertData.as_ref()),
545 ccx.ecx.journaled_state.depth(),
546 false,
547 false,
548 Some(*reverter),
549 *count,
550 )
551 }
552}
553
554impl Cheatcode for expectRevert_11Call {
555 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
556 let Self { revertData, reverter, count } = self;
557 expect_revert(
558 ccx.state,
559 Some(revertData),
560 ccx.ecx.journaled_state.depth(),
561 false,
562 false,
563 Some(*reverter),
564 *count,
565 )
566 }
567}
568
569impl Cheatcode for expectPartialRevert_0Call {
570 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
571 let Self { revertData } = self;
572 expect_revert(
573 ccx.state,
574 Some(revertData.as_ref()),
575 ccx.ecx.journaled_state.depth(),
576 false,
577 true,
578 None,
579 1,
580 )
581 }
582}
583
584impl Cheatcode for expectPartialRevert_1Call {
585 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
586 let Self { revertData, reverter } = self;
587 expect_revert(
588 ccx.state,
589 Some(revertData.as_ref()),
590 ccx.ecx.journaled_state.depth(),
591 false,
592 true,
593 Some(*reverter),
594 1,
595 )
596 }
597}
598
599impl Cheatcode for _expectCheatcodeRevert_0Call {
600 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
601 expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), true, false, None, 1)
602 }
603}
604
605impl Cheatcode for _expectCheatcodeRevert_1Call {
606 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
607 let Self { revertData } = self;
608 expect_revert(
609 ccx.state,
610 Some(revertData.as_ref()),
611 ccx.ecx.journaled_state.depth(),
612 true,
613 false,
614 None,
615 1,
616 )
617 }
618}
619
620impl Cheatcode for _expectCheatcodeRevert_2Call {
621 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
622 let Self { revertData } = self;
623 expect_revert(
624 ccx.state,
625 Some(revertData),
626 ccx.ecx.journaled_state.depth(),
627 true,
628 false,
629 None,
630 1,
631 )
632 }
633}
634
635impl Cheatcode for expectSafeMemoryCall {
636 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
637 let Self { min, max } = *self;
638 expect_safe_memory(ccx.state, min, max, ccx.ecx.journaled_state.depth().try_into()?)
639 }
640}
641
642impl Cheatcode for stopExpectSafeMemoryCall {
643 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
644 let Self {} = self;
645 ccx.state.allowed_mem_writes.remove(&ccx.ecx.journaled_state.depth().try_into()?);
646 Ok(Default::default())
647 }
648}
649
650impl Cheatcode for expectSafeMemoryCallCall {
651 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
652 let Self { min, max } = *self;
653 expect_safe_memory(ccx.state, min, max, (ccx.ecx.journaled_state.depth() + 1).try_into()?)
654 }
655}
656
657impl RevertParameters for ExpectedRevert {
658 fn reverter(&self) -> Option<Address> {
659 self.reverter
660 }
661
662 fn reason(&self) -> Option<&[u8]> {
663 self.reason.as_deref()
664 }
665
666 fn partial_match(&self) -> bool {
667 self.partial_match
668 }
669}
670
671#[expect(clippy::too_many_arguments)] fn expect_call(
689 state: &mut Cheatcodes,
690 target: &Address,
691 calldata: &Bytes,
692 value: Option<&U256>,
693 mut gas: Option<u64>,
694 mut min_gas: Option<u64>,
695 count: u64,
696 call_type: ExpectedCallType,
697) -> Result {
698 let expecteds = state.expected_calls.entry(*target).or_default();
699
700 if let Some(val) = value {
701 if *val > U256::ZERO {
702 let positive_value_cost_stipend = 2300;
705 if let Some(gas) = &mut gas {
706 *gas += positive_value_cost_stipend;
707 }
708 if let Some(min_gas) = &mut min_gas {
709 *min_gas += positive_value_cost_stipend;
710 }
711 }
712 }
713
714 match call_type {
715 ExpectedCallType::Count => {
716 ensure!(
720 !expecteds.contains_key(calldata),
721 "counted expected calls can only bet set once"
722 );
723 expecteds.insert(
724 calldata.clone(),
725 (ExpectedCallData { value: value.copied(), gas, min_gas, count, call_type }, 0),
726 );
727 }
728 ExpectedCallType::NonCount => {
729 match expecteds.entry(calldata.clone()) {
732 Entry::Occupied(mut entry) => {
733 let (expected, _) = entry.get_mut();
734 ensure!(
736 expected.call_type == ExpectedCallType::NonCount,
737 "cannot overwrite a counted expectCall with a non-counted expectCall"
738 );
739 expected.count += 1;
740 }
741 Entry::Vacant(entry) => {
743 entry.insert((
744 ExpectedCallData { value: value.copied(), gas, min_gas, count, call_type },
745 0,
746 ));
747 }
748 }
749 }
750 }
751
752 Ok(Default::default())
753}
754
755fn expect_emit(
756 state: &mut Cheatcodes,
757 depth: usize,
758 checks: [bool; 5],
759 address: Option<Address>,
760 anonymous: bool,
761 count: u64,
762) -> Result {
763 let expected_emit =
764 ExpectedEmit { depth, checks, address, found: false, log: None, anonymous, count };
765 if let Some(found_emit_pos) = state.expected_emits.iter().position(|(emit, _)| emit.found) {
766 state.expected_emits.insert(found_emit_pos, (expected_emit, Default::default()));
769 } else {
770 state.expected_emits.push_back((expected_emit, Default::default()));
772 }
773
774 Ok(Default::default())
775}
776
777pub(crate) fn handle_expect_emit(
778 state: &mut Cheatcodes,
779 log: &alloy_primitives::Log,
780 interpreter: &mut Interpreter,
781) {
782 if state.expected_emits.iter().all(|(expected, _)| expected.found) {
793 return
794 }
795
796 let should_fill_logs = state.expected_emits.iter().any(|(expected, _)| expected.log.is_none());
797 let index_to_fill_or_check = if should_fill_logs {
798 state
801 .expected_emits
802 .iter()
803 .position(|(emit, _)| emit.found)
804 .unwrap_or(state.expected_emits.len())
805 .saturating_sub(1)
806 } else {
807 0
810 };
811
812 let (mut event_to_fill_or_check, mut count_map) = state
813 .expected_emits
814 .remove(index_to_fill_or_check)
815 .expect("we should have an emit to fill or check");
816
817 let Some(expected) = &event_to_fill_or_check.log else {
818 if event_to_fill_or_check.anonymous || !log.topics().is_empty() {
821 event_to_fill_or_check.log = Some(log.data.clone());
822 state
824 .expected_emits
825 .insert(index_to_fill_or_check, (event_to_fill_or_check, count_map));
826 } else {
827 interpreter.control.instruction_result = InstructionResult::Revert;
828 interpreter.control.next_action = InterpreterAction::Return {
829 result: InterpreterResult {
830 output: Error::encode("use vm.expectEmitAnonymous to match anonymous events"),
831 gas: interpreter.control.gas,
832 result: InstructionResult::Revert,
833 },
834 };
835 }
836 return
837 };
838
839 match count_map.entry(log.address) {
841 Entry::Occupied(mut entry) => {
842 let log_count_map = entry.get_mut();
845 log_count_map.insert(&log.data);
846 }
847 Entry::Vacant(entry) => {
848 let mut log_count_map = LogCountMap::new(&event_to_fill_or_check);
849
850 if log_count_map.satisfies_checks(&log.data) {
851 log_count_map.insert(&log.data);
852
853 entry.insert(log_count_map);
855 }
856 }
857 }
858
859 event_to_fill_or_check.found = || -> bool {
860 if !checks_topics_and_data(event_to_fill_or_check.checks, expected, log) {
861 return false
862 }
863
864 if event_to_fill_or_check.address.is_some_and(|addr| addr != log.address) {
866 return false;
867 }
868
869 let expected_count = event_to_fill_or_check.count;
870
871 match event_to_fill_or_check.address {
872 Some(emitter) => count_map
873 .get(&emitter)
874 .is_some_and(|log_map| log_map.count(&log.data) >= expected_count),
875 None => count_map
876 .values()
877 .find(|log_map| log_map.satisfies_checks(&log.data))
878 .is_some_and(|map| map.count(&log.data) >= expected_count),
879 }
880 }();
881
882 if event_to_fill_or_check.found {
885 state.expected_emits.push_back((event_to_fill_or_check, count_map));
886 } else {
887 state.expected_emits.push_front((event_to_fill_or_check, count_map));
890 }
891}
892
893pub type ExpectedEmitTracker = VecDeque<(ExpectedEmit, AddressHashMap<LogCountMap>)>;
898
899#[derive(Clone, Debug, Default)]
900pub struct LogCountMap {
901 checks: [bool; 5],
902 expected_log: RawLog,
903 map: HashMap<RawLog, u64>,
904}
905
906impl LogCountMap {
907 fn new(expected_emit: &ExpectedEmit) -> Self {
909 Self {
910 checks: expected_emit.checks,
911 expected_log: expected_emit.log.clone().expect("log should be filled here"),
912 map: Default::default(),
913 }
914 }
915
916 fn insert(&mut self, log: &RawLog) -> bool {
922 if self.map.contains_key(log) {
924 self.map.entry(log.clone()).and_modify(|c| *c += 1);
925
926 return true
927 }
928
929 if !self.satisfies_checks(log) {
930 return false
931 }
932
933 self.map.entry(log.clone()).and_modify(|c| *c += 1).or_insert(1);
934
935 true
936 }
937
938 fn satisfies_checks(&self, log: &RawLog) -> bool {
940 checks_topics_and_data(self.checks, &self.expected_log, log)
941 }
942
943 pub fn count(&self, log: &RawLog) -> u64 {
944 if !self.satisfies_checks(log) {
945 return 0
946 }
947
948 self.count_unchecked()
949 }
950
951 pub fn count_unchecked(&self) -> u64 {
952 self.map.values().sum()
953 }
954}
955
956fn expect_create(
957 state: &mut Cheatcodes,
958 bytecode: Bytes,
959 deployer: Address,
960 create_scheme: CreateScheme,
961) -> Result {
962 let expected_create = ExpectedCreate { bytecode, deployer, create_scheme };
963 state.expected_creates.push(expected_create);
964
965 Ok(Default::default())
966}
967
968fn expect_revert(
969 state: &mut Cheatcodes,
970 reason: Option<&[u8]>,
971 depth: usize,
972 cheatcode: bool,
973 partial_match: bool,
974 reverter: Option<Address>,
975 count: u64,
976) -> Result {
977 ensure!(
978 state.expected_revert.is_none(),
979 "you must call another function prior to expecting a second revert"
980 );
981 state.expected_revert = Some(ExpectedRevert {
982 reason: reason.map(<[_]>::to_vec),
983 depth,
984 kind: if cheatcode {
985 ExpectedRevertKind::Cheatcode { pending_processing: true }
986 } else {
987 ExpectedRevertKind::Default
988 },
989 partial_match,
990 reverter,
991 reverted_by: None,
992 max_depth: depth,
993 count,
994 actual_count: 0,
995 });
996 Ok(Default::default())
997}
998
999fn checks_topics_and_data(checks: [bool; 5], expected: &RawLog, log: &RawLog) -> bool {
1000 if log.topics().len() != expected.topics().len() {
1001 return false
1002 }
1003
1004 if !log
1006 .topics()
1007 .iter()
1008 .enumerate()
1009 .filter(|(i, _)| checks[*i])
1010 .all(|(i, topic)| topic == &expected.topics()[i])
1011 {
1012 return false
1013 }
1014
1015 if checks[4] && expected.data.as_ref() != log.data.as_ref() {
1017 return false
1018 }
1019
1020 true
1021}
1022
1023fn expect_safe_memory(state: &mut Cheatcodes, start: u64, end: u64, depth: u64) -> Result {
1024 ensure!(start < end, "memory range start ({start}) is greater than end ({end})");
1025 #[expect(clippy::single_range_in_vec_init)] let offsets = state.allowed_mem_writes.entry(depth).or_insert_with(|| vec![0..0x60]);
1027 offsets.push(start..end);
1028 Ok(Default::default())
1029}