1use std::{
2 collections::VecDeque,
3 fmt::{self, Display},
4};
5
6use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Error, Result, Vm::*};
7use alloy_dyn_abi::{DynSolValue, EventExt};
8use alloy_json_abi::Event;
9use alloy_primitives::{
10 Address, Bytes, LogData as RawLog, U256, hex,
11 map::{AddressHashMap, HashMap, hash_map::Entry},
12};
13use foundry_common::{abi::get_indexed_event, fmt::format_token};
14use foundry_evm_traces::DecodedCallLog;
15use revm::{
16 context::JournalTr,
17 interpreter::{
18 InstructionResult, Interpreter, InterpreterAction, interpreter_types::LoopControl,
19 },
20};
21
22use super::revert_handlers::RevertParameters;
23pub type ExpectedCallTracker = HashMap<Address, HashMap<Bytes, (ExpectedCallData, u64)>>;
33
34#[derive(Clone, Debug)]
35pub struct ExpectedCallData {
36 pub value: Option<U256>,
38 pub gas: Option<u64>,
40 pub min_gas: Option<u64>,
42 pub count: u64,
47 pub call_type: ExpectedCallType,
49}
50
51#[derive(Clone, Debug, PartialEq, Eq)]
53pub enum ExpectedCallType {
54 NonCount,
56 Count,
58}
59
60#[derive(Clone, Debug)]
62pub enum ExpectedRevertKind {
63 Default,
65 Cheatcode { pending_processing: bool },
71}
72
73#[derive(Clone, Debug)]
74pub struct ExpectedRevert {
75 pub reason: Option<Vec<u8>>,
77 pub depth: usize,
79 pub kind: ExpectedRevertKind,
81 pub partial_match: bool,
83 pub reverter: Option<Address>,
85 pub reverted_by: Option<Address>,
87 pub max_depth: usize,
89 pub count: u64,
91 pub actual_count: u64,
93}
94
95#[derive(Clone, Debug)]
96pub struct ExpectedEmit {
97 pub depth: usize,
99 pub log: Option<RawLog>,
101 pub checks: [bool; 5],
108 pub address: Option<Address>,
110 pub anonymous: bool,
113 pub found: bool,
115 pub count: u64,
117 pub mismatch_error: Option<String>,
119}
120
121#[derive(Clone, Debug)]
122pub struct ExpectedCreate {
123 pub deployer: Address,
125 pub bytecode: Bytes,
127 pub create_scheme: CreateScheme,
129}
130
131#[derive(Clone, Debug)]
132pub enum CreateScheme {
133 Create,
134 Create2,
135}
136
137impl Display for CreateScheme {
138 fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
139 match self {
140 Self::Create => write!(f, "CREATE"),
141 Self::Create2 => write!(f, "CREATE2"),
142 }
143 }
144}
145
146impl From<revm::context_interface::CreateScheme> for CreateScheme {
147 fn from(scheme: revm::context_interface::CreateScheme) -> Self {
148 match scheme {
149 revm::context_interface::CreateScheme::Create => Self::Create,
150 revm::context_interface::CreateScheme::Create2 { .. } => Self::Create2,
151 _ => unimplemented!("Unsupported create scheme"),
152 }
153 }
154}
155
156impl CreateScheme {
157 pub fn eq(&self, create_scheme: Self) -> bool {
158 matches!(
159 (self, create_scheme),
160 (Self::Create, Self::Create) | (Self::Create2, Self::Create2 { .. })
161 )
162 }
163}
164
165impl Cheatcode for expectCall_0Call {
166 fn apply(&self, state: &mut Cheatcodes) -> Result {
167 let Self { callee, data } = self;
168 expect_call(state, callee, data, None, None, None, 1, ExpectedCallType::NonCount)
169 }
170}
171
172impl Cheatcode for expectCall_1Call {
173 fn apply(&self, state: &mut Cheatcodes) -> Result {
174 let Self { callee, data, count } = self;
175 expect_call(state, callee, data, None, None, None, *count, ExpectedCallType::Count)
176 }
177}
178
179impl Cheatcode for expectCall_2Call {
180 fn apply(&self, state: &mut Cheatcodes) -> Result {
181 let Self { callee, msgValue, data } = self;
182 expect_call(state, callee, data, Some(msgValue), None, None, 1, ExpectedCallType::NonCount)
183 }
184}
185
186impl Cheatcode for expectCall_3Call {
187 fn apply(&self, state: &mut Cheatcodes) -> Result {
188 let Self { callee, msgValue, data, count } = self;
189 expect_call(
190 state,
191 callee,
192 data,
193 Some(msgValue),
194 None,
195 None,
196 *count,
197 ExpectedCallType::Count,
198 )
199 }
200}
201
202impl Cheatcode for expectCall_4Call {
203 fn apply(&self, state: &mut Cheatcodes) -> Result {
204 let Self { callee, msgValue, gas, data } = self;
205 expect_call(
206 state,
207 callee,
208 data,
209 Some(msgValue),
210 Some(*gas),
211 None,
212 1,
213 ExpectedCallType::NonCount,
214 )
215 }
216}
217
218impl Cheatcode for expectCall_5Call {
219 fn apply(&self, state: &mut Cheatcodes) -> Result {
220 let Self { callee, msgValue, gas, data, count } = self;
221 expect_call(
222 state,
223 callee,
224 data,
225 Some(msgValue),
226 Some(*gas),
227 None,
228 *count,
229 ExpectedCallType::Count,
230 )
231 }
232}
233
234impl Cheatcode for expectCallMinGas_0Call {
235 fn apply(&self, state: &mut Cheatcodes) -> Result {
236 let Self { callee, msgValue, minGas, data } = self;
237 expect_call(
238 state,
239 callee,
240 data,
241 Some(msgValue),
242 None,
243 Some(*minGas),
244 1,
245 ExpectedCallType::NonCount,
246 )
247 }
248}
249
250impl Cheatcode for expectCallMinGas_1Call {
251 fn apply(&self, state: &mut Cheatcodes) -> Result {
252 let Self { callee, msgValue, minGas, data, count } = self;
253 expect_call(
254 state,
255 callee,
256 data,
257 Some(msgValue),
258 None,
259 Some(*minGas),
260 *count,
261 ExpectedCallType::Count,
262 )
263 }
264}
265
266impl Cheatcode for expectEmit_0Call {
267 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
268 let Self { checkTopic1, checkTopic2, checkTopic3, checkData } = *self;
269 expect_emit(
270 ccx.state,
271 ccx.ecx.journaled_state.depth(),
272 [true, checkTopic1, checkTopic2, checkTopic3, checkData],
273 None,
274 false,
275 1,
276 )
277 }
278}
279
280impl Cheatcode for expectEmit_1Call {
281 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
282 let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self;
283 expect_emit(
284 ccx.state,
285 ccx.ecx.journaled_state.depth(),
286 [true, checkTopic1, checkTopic2, checkTopic3, checkData],
287 Some(emitter),
288 false,
289 1,
290 )
291 }
292}
293
294impl Cheatcode for expectEmit_2Call {
295 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
296 let Self {} = self;
297 expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, false, 1)
298 }
299}
300
301impl Cheatcode for expectEmit_3Call {
302 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
303 let Self { emitter } = *self;
304 expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], Some(emitter), false, 1)
305 }
306}
307
308impl Cheatcode for expectEmit_4Call {
309 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
310 let Self { checkTopic1, checkTopic2, checkTopic3, checkData, count } = *self;
311 expect_emit(
312 ccx.state,
313 ccx.ecx.journaled_state.depth(),
314 [true, checkTopic1, checkTopic2, checkTopic3, checkData],
315 None,
316 false,
317 count,
318 )
319 }
320}
321
322impl Cheatcode for expectEmit_5Call {
323 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
324 let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter, count } = *self;
325 expect_emit(
326 ccx.state,
327 ccx.ecx.journaled_state.depth(),
328 [true, checkTopic1, checkTopic2, checkTopic3, checkData],
329 Some(emitter),
330 false,
331 count,
332 )
333 }
334}
335
336impl Cheatcode for expectEmit_6Call {
337 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
338 let Self { count } = *self;
339 expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, false, count)
340 }
341}
342
343impl Cheatcode for expectEmit_7Call {
344 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
345 let Self { emitter, count } = *self;
346 expect_emit(
347 ccx.state,
348 ccx.ecx.journaled_state.depth(),
349 [true; 5],
350 Some(emitter),
351 false,
352 count,
353 )
354 }
355}
356
357impl Cheatcode for expectEmitAnonymous_0Call {
358 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
359 let Self { checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData } = *self;
360 expect_emit(
361 ccx.state,
362 ccx.ecx.journaled_state.depth(),
363 [checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData],
364 None,
365 true,
366 1,
367 )
368 }
369}
370
371impl Cheatcode for expectEmitAnonymous_1Call {
372 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
373 let Self { checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self;
374 expect_emit(
375 ccx.state,
376 ccx.ecx.journaled_state.depth(),
377 [checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData],
378 Some(emitter),
379 true,
380 1,
381 )
382 }
383}
384
385impl Cheatcode for expectEmitAnonymous_2Call {
386 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
387 let Self {} = self;
388 expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, true, 1)
389 }
390}
391
392impl Cheatcode for expectEmitAnonymous_3Call {
393 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
394 let Self { emitter } = *self;
395 expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], Some(emitter), true, 1)
396 }
397}
398
399impl Cheatcode for expectCreateCall {
400 fn apply(&self, state: &mut Cheatcodes) -> Result {
401 let Self { bytecode, deployer } = self;
402 expect_create(state, bytecode.clone(), *deployer, CreateScheme::Create)
403 }
404}
405
406impl Cheatcode for expectCreate2Call {
407 fn apply(&self, state: &mut Cheatcodes) -> Result {
408 let Self { bytecode, deployer } = self;
409 expect_create(state, bytecode.clone(), *deployer, CreateScheme::Create2)
410 }
411}
412
413impl Cheatcode for expectRevert_0Call {
414 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
415 let Self {} = self;
416 expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), false, false, None, 1)
417 }
418}
419
420impl Cheatcode for expectRevert_1Call {
421 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
422 let Self { revertData } = self;
423 expect_revert(
424 ccx.state,
425 Some(revertData.as_ref()),
426 ccx.ecx.journaled_state.depth(),
427 false,
428 false,
429 None,
430 1,
431 )
432 }
433}
434
435impl Cheatcode for expectRevert_2Call {
436 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
437 let Self { revertData } = self;
438 expect_revert(
439 ccx.state,
440 Some(revertData),
441 ccx.ecx.journaled_state.depth(),
442 false,
443 false,
444 None,
445 1,
446 )
447 }
448}
449
450impl Cheatcode for expectRevert_3Call {
451 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
452 let Self { reverter } = self;
453 expect_revert(
454 ccx.state,
455 None,
456 ccx.ecx.journaled_state.depth(),
457 false,
458 false,
459 Some(*reverter),
460 1,
461 )
462 }
463}
464
465impl Cheatcode for expectRevert_4Call {
466 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
467 let Self { revertData, reverter } = self;
468 expect_revert(
469 ccx.state,
470 Some(revertData.as_ref()),
471 ccx.ecx.journaled_state.depth(),
472 false,
473 false,
474 Some(*reverter),
475 1,
476 )
477 }
478}
479
480impl Cheatcode for expectRevert_5Call {
481 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
482 let Self { revertData, reverter } = self;
483 expect_revert(
484 ccx.state,
485 Some(revertData),
486 ccx.ecx.journaled_state.depth(),
487 false,
488 false,
489 Some(*reverter),
490 1,
491 )
492 }
493}
494
495impl Cheatcode for expectRevert_6Call {
496 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
497 let Self { count } = self;
498 expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), false, false, None, *count)
499 }
500}
501
502impl Cheatcode for expectRevert_7Call {
503 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
504 let Self { revertData, count } = self;
505 expect_revert(
506 ccx.state,
507 Some(revertData.as_ref()),
508 ccx.ecx.journaled_state.depth(),
509 false,
510 false,
511 None,
512 *count,
513 )
514 }
515}
516
517impl Cheatcode for expectRevert_8Call {
518 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
519 let Self { revertData, count } = self;
520 expect_revert(
521 ccx.state,
522 Some(revertData),
523 ccx.ecx.journaled_state.depth(),
524 false,
525 false,
526 None,
527 *count,
528 )
529 }
530}
531
532impl Cheatcode for expectRevert_9Call {
533 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
534 let Self { reverter, count } = self;
535 expect_revert(
536 ccx.state,
537 None,
538 ccx.ecx.journaled_state.depth(),
539 false,
540 false,
541 Some(*reverter),
542 *count,
543 )
544 }
545}
546
547impl Cheatcode for expectRevert_10Call {
548 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
549 let Self { revertData, reverter, count } = self;
550 expect_revert(
551 ccx.state,
552 Some(revertData.as_ref()),
553 ccx.ecx.journaled_state.depth(),
554 false,
555 false,
556 Some(*reverter),
557 *count,
558 )
559 }
560}
561
562impl Cheatcode for expectRevert_11Call {
563 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
564 let Self { revertData, reverter, count } = self;
565 expect_revert(
566 ccx.state,
567 Some(revertData),
568 ccx.ecx.journaled_state.depth(),
569 false,
570 false,
571 Some(*reverter),
572 *count,
573 )
574 }
575}
576
577impl Cheatcode for expectPartialRevert_0Call {
578 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
579 let Self { revertData } = self;
580 expect_revert(
581 ccx.state,
582 Some(revertData.as_ref()),
583 ccx.ecx.journaled_state.depth(),
584 false,
585 true,
586 None,
587 1,
588 )
589 }
590}
591
592impl Cheatcode for expectPartialRevert_1Call {
593 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
594 let Self { revertData, reverter } = self;
595 expect_revert(
596 ccx.state,
597 Some(revertData.as_ref()),
598 ccx.ecx.journaled_state.depth(),
599 false,
600 true,
601 Some(*reverter),
602 1,
603 )
604 }
605}
606
607impl Cheatcode for _expectCheatcodeRevert_0Call {
608 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
609 expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), true, false, None, 1)
610 }
611}
612
613impl Cheatcode for _expectCheatcodeRevert_1Call {
614 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
615 let Self { revertData } = self;
616 expect_revert(
617 ccx.state,
618 Some(revertData.as_ref()),
619 ccx.ecx.journaled_state.depth(),
620 true,
621 false,
622 None,
623 1,
624 )
625 }
626}
627
628impl Cheatcode for _expectCheatcodeRevert_2Call {
629 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
630 let Self { revertData } = self;
631 expect_revert(
632 ccx.state,
633 Some(revertData),
634 ccx.ecx.journaled_state.depth(),
635 true,
636 false,
637 None,
638 1,
639 )
640 }
641}
642
643impl Cheatcode for expectSafeMemoryCall {
644 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
645 let Self { min, max } = *self;
646 expect_safe_memory(ccx.state, min, max, ccx.ecx.journaled_state.depth().try_into()?)
647 }
648}
649
650impl Cheatcode for stopExpectSafeMemoryCall {
651 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
652 let Self {} = self;
653 ccx.state.allowed_mem_writes.remove(&ccx.ecx.journaled_state.depth().try_into()?);
654 Ok(Default::default())
655 }
656}
657
658impl Cheatcode for expectSafeMemoryCallCall {
659 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
660 let Self { min, max } = *self;
661 expect_safe_memory(ccx.state, min, max, (ccx.ecx.journaled_state.depth() + 1).try_into()?)
662 }
663}
664
665impl RevertParameters for ExpectedRevert {
666 fn reverter(&self) -> Option<Address> {
667 self.reverter
668 }
669
670 fn reason(&self) -> Option<&[u8]> {
671 self.reason.as_deref()
672 }
673
674 fn partial_match(&self) -> bool {
675 self.partial_match
676 }
677}
678
679#[expect(clippy::too_many_arguments)] fn expect_call(
697 state: &mut Cheatcodes,
698 target: &Address,
699 calldata: &Bytes,
700 value: Option<&U256>,
701 mut gas: Option<u64>,
702 mut min_gas: Option<u64>,
703 count: u64,
704 call_type: ExpectedCallType,
705) -> Result {
706 let expecteds = state.expected_calls.entry(*target).or_default();
707
708 if let Some(val) = value
709 && *val > U256::ZERO
710 {
711 let positive_value_cost_stipend = 2300;
714 if let Some(gas) = &mut gas {
715 *gas += positive_value_cost_stipend;
716 }
717 if let Some(min_gas) = &mut min_gas {
718 *min_gas += positive_value_cost_stipend;
719 }
720 }
721
722 match call_type {
723 ExpectedCallType::Count => {
724 ensure!(
728 !expecteds.contains_key(calldata),
729 "counted expected calls can only bet set once"
730 );
731 expecteds.insert(
732 calldata.clone(),
733 (ExpectedCallData { value: value.copied(), gas, min_gas, count, call_type }, 0),
734 );
735 }
736 ExpectedCallType::NonCount => {
737 match expecteds.entry(calldata.clone()) {
740 Entry::Occupied(mut entry) => {
741 let (expected, _) = entry.get_mut();
742 ensure!(
744 expected.call_type == ExpectedCallType::NonCount,
745 "cannot overwrite a counted expectCall with a non-counted expectCall"
746 );
747 expected.count += 1;
748 }
749 Entry::Vacant(entry) => {
751 entry.insert((
752 ExpectedCallData { value: value.copied(), gas, min_gas, count, call_type },
753 0,
754 ));
755 }
756 }
757 }
758 }
759
760 Ok(Default::default())
761}
762
763fn expect_emit(
764 state: &mut Cheatcodes,
765 depth: usize,
766 checks: [bool; 5],
767 address: Option<Address>,
768 anonymous: bool,
769 count: u64,
770) -> Result {
771 let expected_emit = ExpectedEmit {
772 depth,
773 checks,
774 address,
775 found: false,
776 log: None,
777 anonymous,
778 count,
779 mismatch_error: None,
780 };
781 if let Some(found_emit_pos) = state.expected_emits.iter().position(|(emit, _)| emit.found) {
782 state.expected_emits.insert(found_emit_pos, (expected_emit, Default::default()));
785 } else {
786 state.expected_emits.push_back((expected_emit, Default::default()));
788 }
789
790 Ok(Default::default())
791}
792
793pub(crate) fn handle_expect_emit(
794 state: &mut Cheatcodes,
795 log: &alloy_primitives::Log,
796 interpreter: &mut Interpreter,
797) {
798 if state.expected_emits.iter().all(|(expected, _)| expected.found) {
809 return;
810 }
811
812 let should_fill_logs = state.expected_emits.iter().any(|(expected, _)| expected.log.is_none());
813 let index_to_fill_or_check = if should_fill_logs {
814 state
817 .expected_emits
818 .iter()
819 .position(|(emit, _)| emit.found)
820 .unwrap_or(state.expected_emits.len())
821 .saturating_sub(1)
822 } else {
823 0
826 };
827
828 let (mut event_to_fill_or_check, mut count_map) = state
829 .expected_emits
830 .remove(index_to_fill_or_check)
831 .expect("we should have an emit to fill or check");
832
833 let Some(expected) = &event_to_fill_or_check.log else {
834 if event_to_fill_or_check.anonymous || !log.topics().is_empty() {
837 event_to_fill_or_check.log = Some(log.data.clone());
838 state
840 .expected_emits
841 .insert(index_to_fill_or_check, (event_to_fill_or_check, count_map));
842 } else {
843 interpreter.bytecode.set_action(InterpreterAction::new_return(
844 InstructionResult::Revert,
845 Error::encode("use vm.expectEmitAnonymous to match anonymous events"),
846 interpreter.gas,
847 ));
848 }
849 return;
850 };
851
852 match count_map.entry(log.address) {
854 Entry::Occupied(mut entry) => {
855 let log_count_map = entry.get_mut();
858 log_count_map.insert(&log.data);
859 }
860 Entry::Vacant(entry) => {
861 let mut log_count_map = LogCountMap::new(&event_to_fill_or_check);
862
863 if log_count_map.satisfies_checks(&log.data) {
864 log_count_map.insert(&log.data);
865
866 entry.insert(log_count_map);
868 }
869 }
870 }
871
872 event_to_fill_or_check.found = || -> bool {
873 if !checks_topics_and_data(event_to_fill_or_check.checks, expected, log) {
874 let (expected_decoded, actual_decoded) = if let Some(signatures_identifier) =
878 &state.signatures_identifier
879 && !event_to_fill_or_check.anonymous
880 {
881 (
882 decode_event(signatures_identifier, expected),
883 decode_event(signatures_identifier, log),
884 )
885 } else {
886 (None, None)
887 };
888 event_to_fill_or_check.mismatch_error = Some(get_emit_mismatch_message(
889 event_to_fill_or_check.checks,
890 expected,
891 log,
892 event_to_fill_or_check.anonymous,
893 expected_decoded.as_ref(),
894 actual_decoded.as_ref(),
895 ));
896 return false;
897 }
898
899 if event_to_fill_or_check
901 .address
902 .is_some_and(|addr| addr.to_checksum(None) != log.address.to_checksum(None))
903 {
904 event_to_fill_or_check.mismatch_error = Some(format!(
905 "log emitter mismatch: expected={:#x}, got={:#x}",
906 event_to_fill_or_check.address.unwrap(),
907 log.address
908 ));
909 return false;
910 }
911
912 let expected_count = event_to_fill_or_check.count;
913
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 event_to_fill_or_check.found {
928 state.expected_emits.push_back((event_to_fill_or_check, count_map));
929 } else {
930 state.expected_emits.push_front((event_to_fill_or_check, count_map));
933 }
934}
935
936pub type ExpectedEmitTracker = VecDeque<(ExpectedEmit, AddressHashMap<LogCountMap>)>;
941
942#[derive(Clone, Debug, Default)]
943pub struct LogCountMap {
944 checks: [bool; 5],
945 expected_log: RawLog,
946 map: HashMap<RawLog, u64>,
947}
948
949impl LogCountMap {
950 fn new(expected_emit: &ExpectedEmit) -> Self {
952 Self {
953 checks: expected_emit.checks,
954 expected_log: expected_emit.log.clone().expect("log should be filled here"),
955 map: Default::default(),
956 }
957 }
958
959 fn insert(&mut self, log: &RawLog) -> bool {
965 if self.map.contains_key(log) {
967 self.map.entry(log.clone()).and_modify(|c| *c += 1);
968
969 return true;
970 }
971
972 if !self.satisfies_checks(log) {
973 return false;
974 }
975
976 self.map.entry(log.clone()).and_modify(|c| *c += 1).or_insert(1);
977
978 true
979 }
980
981 fn satisfies_checks(&self, log: &RawLog) -> bool {
983 checks_topics_and_data(self.checks, &self.expected_log, log)
984 }
985
986 pub fn count(&self, log: &RawLog) -> u64 {
987 if !self.satisfies_checks(log) {
988 return 0;
989 }
990
991 self.count_unchecked()
992 }
993
994 pub fn count_unchecked(&self) -> u64 {
995 self.map.values().sum()
996 }
997}
998
999fn expect_create(
1000 state: &mut Cheatcodes,
1001 bytecode: Bytes,
1002 deployer: Address,
1003 create_scheme: CreateScheme,
1004) -> Result {
1005 let expected_create = ExpectedCreate { bytecode, deployer, create_scheme };
1006 state.expected_creates.push(expected_create);
1007
1008 Ok(Default::default())
1009}
1010
1011fn expect_revert(
1012 state: &mut Cheatcodes,
1013 reason: Option<&[u8]>,
1014 depth: usize,
1015 cheatcode: bool,
1016 partial_match: bool,
1017 reverter: Option<Address>,
1018 count: u64,
1019) -> Result {
1020 ensure!(
1021 state.expected_revert.is_none(),
1022 "you must call another function prior to expecting a second revert"
1023 );
1024 state.expected_revert = Some(ExpectedRevert {
1025 reason: reason.map(<[_]>::to_vec),
1026 depth,
1027 kind: if cheatcode {
1028 ExpectedRevertKind::Cheatcode { pending_processing: true }
1029 } else {
1030 ExpectedRevertKind::Default
1031 },
1032 partial_match,
1033 reverter,
1034 reverted_by: None,
1035 max_depth: depth,
1036 count,
1037 actual_count: 0,
1038 });
1039 Ok(Default::default())
1040}
1041
1042fn checks_topics_and_data(checks: [bool; 5], expected: &RawLog, log: &RawLog) -> bool {
1043 if log.topics().len() != expected.topics().len() {
1044 return false;
1045 }
1046
1047 if !log
1049 .topics()
1050 .iter()
1051 .enumerate()
1052 .filter(|(i, _)| checks[*i])
1053 .all(|(i, topic)| topic == &expected.topics()[i])
1054 {
1055 return false;
1056 }
1057
1058 if checks[4] && expected.data.as_ref() != log.data.as_ref() {
1060 return false;
1061 }
1062
1063 true
1064}
1065
1066fn decode_event(
1067 identifier: &foundry_evm_traces::identifier::SignaturesIdentifier,
1068 log: &RawLog,
1069) -> Option<DecodedCallLog> {
1070 let topics = log.topics();
1071 if topics.is_empty() {
1072 return None;
1073 }
1074 let t0 = topics[0]; let event = foundry_common::block_on(identifier.identify_event(t0))?;
1077
1078 let has_indexed_info = event.inputs.iter().any(|p| p.indexed);
1080 let indexed_event = if has_indexed_info { event } else { get_indexed_event(event, log) };
1082
1083 if let Ok(decoded) = indexed_event.decode_log(log) {
1085 let params = reconstruct_params(&indexed_event, &decoded);
1086
1087 let decoded_params = params
1088 .into_iter()
1089 .zip(indexed_event.inputs.iter())
1090 .map(|(param, input)| (input.name.clone(), format_token(¶m)))
1091 .collect();
1092
1093 return Some(DecodedCallLog {
1094 name: Some(indexed_event.name),
1095 params: Some(decoded_params),
1096 });
1097 }
1098
1099 None
1100}
1101
1102fn reconstruct_params(event: &Event, decoded: &alloy_dyn_abi::DecodedEvent) -> Vec<DynSolValue> {
1104 let mut indexed = 0;
1105 let mut unindexed = 0;
1106 let mut inputs = vec![];
1107 for input in &event.inputs {
1108 if input.indexed && indexed < decoded.indexed.len() {
1109 inputs.push(decoded.indexed[indexed].clone());
1110 indexed += 1;
1111 } else if unindexed < decoded.body.len() {
1112 inputs.push(decoded.body[unindexed].clone());
1113 unindexed += 1;
1114 }
1115 }
1116 inputs
1117}
1118
1119pub(crate) fn get_emit_mismatch_message(
1121 checks: [bool; 5],
1122 expected: &RawLog,
1123 actual: &RawLog,
1124 is_anonymous: bool,
1125 expected_decoded: Option<&DecodedCallLog>,
1126 actual_decoded: Option<&DecodedCallLog>,
1127) -> String {
1128 if actual.topics().len() != expected.topics().len() {
1132 return name_mismatched_logs(expected_decoded, actual_decoded);
1133 }
1134
1135 if !is_anonymous
1137 && checks[0]
1138 && (!expected.topics().is_empty() && !actual.topics().is_empty())
1139 && expected.topics()[0] != actual.topics()[0]
1140 {
1141 return name_mismatched_logs(expected_decoded, actual_decoded);
1142 }
1143
1144 let expected_data = expected.data.as_ref();
1145 let actual_data = actual.data.as_ref();
1146
1147 if checks[4] && expected_data != actual_data {
1149 if expected_data.len() != actual_data.len()
1151 || !expected_data.len().is_multiple_of(32)
1152 || expected_data.is_empty()
1153 {
1154 return name_mismatched_logs(expected_decoded, actual_decoded);
1155 }
1156 }
1157
1158 let mut mismatches = Vec::new();
1160
1161 for (i, (expected_topic, actual_topic)) in
1163 expected.topics().iter().zip(actual.topics().iter()).enumerate()
1164 {
1165 if i == 0 && !is_anonymous {
1167 continue;
1168 }
1169
1170 if i < checks.len() && checks[i] && expected_topic != actual_topic {
1172 let param_idx = if is_anonymous {
1173 i } else {
1175 i - 1 };
1177 mismatches
1178 .push(format!("param {param_idx}: expected={expected_topic}, got={actual_topic}"));
1179 }
1180 }
1181
1182 if checks[4] && expected_data != actual_data {
1184 let num_indexed_params = if is_anonymous {
1185 expected.topics().len()
1186 } else {
1187 expected.topics().len().saturating_sub(1)
1188 };
1189
1190 for (i, (expected_chunk, actual_chunk)) in
1191 expected_data.chunks(32).zip(actual_data.chunks(32)).enumerate()
1192 {
1193 if expected_chunk != actual_chunk {
1194 let param_idx = num_indexed_params + i;
1195 mismatches.push(format!(
1196 "param {}: expected={}, got={}",
1197 param_idx,
1198 hex::encode_prefixed(expected_chunk),
1199 hex::encode_prefixed(actual_chunk)
1200 ));
1201 }
1202 }
1203 }
1204
1205 if mismatches.is_empty() {
1206 name_mismatched_logs(expected_decoded, actual_decoded)
1207 } else {
1208 let event_prefix = match (expected_decoded, actual_decoded) {
1210 (Some(expected_dec), Some(actual_dec)) if expected_dec.name == actual_dec.name => {
1211 format!(
1212 "{} param mismatch",
1213 expected_dec.name.as_ref().unwrap_or(&"log".to_string())
1214 )
1215 }
1216 _ => {
1217 if is_anonymous {
1218 "anonymous log mismatch".to_string()
1219 } else {
1220 "log mismatch".to_string()
1221 }
1222 }
1223 };
1224
1225 let detailed_mismatches = if let (Some(expected_dec), Some(actual_dec)) =
1227 (expected_decoded, actual_decoded)
1228 && let (Some(expected_params), Some(actual_params)) =
1229 (&expected_dec.params, &actual_dec.params)
1230 {
1231 mismatches
1232 .into_iter()
1233 .map(|basic_mismatch| {
1234 if let Some(param_idx) = basic_mismatch
1236 .split(' ')
1237 .nth(1)
1238 .and_then(|s| s.trim_end_matches(':').parse::<usize>().ok())
1239 && param_idx < expected_params.len()
1240 && param_idx < actual_params.len()
1241 {
1242 let (expected_name, expected_value) = &expected_params[param_idx];
1243 let (_actual_name, actual_value) = &actual_params[param_idx];
1244 let param_name = if !expected_name.is_empty() {
1245 expected_name
1246 } else {
1247 &format!("param{param_idx}")
1248 };
1249 return format!(
1250 "{param_name}: expected={expected_value}, got={actual_value}",
1251 );
1252 }
1253 basic_mismatch
1254 })
1255 .collect::<Vec<_>>()
1256 } else {
1257 mismatches
1258 };
1259
1260 format!("{} at {}", event_prefix, detailed_mismatches.join(", "))
1261 }
1262}
1263
1264fn name_mismatched_logs(
1266 expected_decoded: Option<&DecodedCallLog>,
1267 actual_decoded: Option<&DecodedCallLog>,
1268) -> String {
1269 let expected_name = expected_decoded.and_then(|d| d.name.as_deref()).unwrap_or("log");
1270 let actual_name = actual_decoded.and_then(|d| d.name.as_deref()).unwrap_or("log");
1271 format!("{actual_name} != expected {expected_name}")
1272}
1273
1274fn expect_safe_memory(state: &mut Cheatcodes, start: u64, end: u64, depth: u64) -> Result {
1275 ensure!(start < end, "memory range start ({start}) is greater than end ({end})");
1276 #[expect(clippy::single_range_in_vec_init)] let offsets = state.allowed_mem_writes.entry(depth).or_insert_with(|| vec![0..0x60]);
1278 offsets.push(start..end);
1279 Ok(Default::default())
1280}