1use std::{
2 collections::VecDeque,
3 fmt::{self, Display},
4};
5
6use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Error, Result, Vm::*};
7use alloy_dyn_abi::{DynSolValue, EventExt};
8use alloy_json_abi::Event;
9use alloy_primitives::{
10 Address, Bytes, LogData as RawLog, U256, hex,
11 map::{AddressHashMap, HashMap, hash_map::Entry},
12};
13use foundry_common::{abi::get_indexed_event, fmt::format_token};
14use foundry_evm_traces::DecodedCallLog;
15use revm::{
16 context::{ContextTr, JournalTr},
17 interpreter::{
18 InstructionResult, Interpreter, InterpreterAction, interpreter_types::LoopControl,
19 },
20};
21
22use super::revert_handlers::RevertParameters;
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<Bytes>,
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<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
268 let Self { checkTopic1, checkTopic2, checkTopic3, checkData } = *self;
269 expect_emit(
270 ccx.state,
271 ccx.ecx.journal().depth(),
272 [true, checkTopic1, checkTopic2, checkTopic3, checkData],
273 None,
274 false,
275 1,
276 )
277 }
278}
279
280impl Cheatcode for expectEmit_1Call {
281 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
282 let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self;
283 expect_emit(
284 ccx.state,
285 ccx.ecx.journal().depth(),
286 [true, checkTopic1, checkTopic2, checkTopic3, checkData],
287 Some(emitter),
288 false,
289 1,
290 )
291 }
292}
293
294impl Cheatcode for expectEmit_2Call {
295 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
296 let Self {} = self;
297 expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], None, false, 1)
298 }
299}
300
301impl Cheatcode for expectEmit_3Call {
302 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
303 let Self { emitter } = *self;
304 expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], Some(emitter), false, 1)
305 }
306}
307
308impl Cheatcode for expectEmit_4Call {
309 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
310 let Self { checkTopic1, checkTopic2, checkTopic3, checkData, count } = *self;
311 expect_emit(
312 ccx.state,
313 ccx.ecx.journal().depth(),
314 [true, checkTopic1, checkTopic2, checkTopic3, checkData],
315 None,
316 false,
317 count,
318 )
319 }
320}
321
322impl Cheatcode for expectEmit_5Call {
323 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
324 let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter, count } = *self;
325 expect_emit(
326 ccx.state,
327 ccx.ecx.journal().depth(),
328 [true, checkTopic1, checkTopic2, checkTopic3, checkData],
329 Some(emitter),
330 false,
331 count,
332 )
333 }
334}
335
336impl Cheatcode for expectEmit_6Call {
337 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
338 let Self { count } = *self;
339 expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], None, false, count)
340 }
341}
342
343impl Cheatcode for expectEmit_7Call {
344 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
345 let Self { emitter, count } = *self;
346 expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], Some(emitter), false, count)
347 }
348}
349
350impl Cheatcode for expectEmitAnonymous_0Call {
351 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
352 let Self { checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData } = *self;
353 expect_emit(
354 ccx.state,
355 ccx.ecx.journal().depth(),
356 [checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData],
357 None,
358 true,
359 1,
360 )
361 }
362}
363
364impl Cheatcode for expectEmitAnonymous_1Call {
365 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
366 let Self { checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self;
367 expect_emit(
368 ccx.state,
369 ccx.ecx.journal().depth(),
370 [checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData],
371 Some(emitter),
372 true,
373 1,
374 )
375 }
376}
377
378impl Cheatcode for expectEmitAnonymous_2Call {
379 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
380 let Self {} = self;
381 expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], None, true, 1)
382 }
383}
384
385impl Cheatcode for expectEmitAnonymous_3Call {
386 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
387 let Self { emitter } = *self;
388 expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], Some(emitter), true, 1)
389 }
390}
391
392impl Cheatcode for expectCreateCall {
393 fn apply(&self, state: &mut Cheatcodes) -> Result {
394 let Self { bytecode, deployer } = self;
395 expect_create(state, bytecode.clone(), *deployer, CreateScheme::Create)
396 }
397}
398
399impl Cheatcode for expectCreate2Call {
400 fn apply(&self, state: &mut Cheatcodes) -> Result {
401 let Self { bytecode, deployer } = self;
402 expect_create(state, bytecode.clone(), *deployer, CreateScheme::Create2)
403 }
404}
405
406impl Cheatcode for expectRevert_0Call {
407 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
408 let Self {} = self;
409 expect_revert(ccx.state, None, ccx.ecx.journal().depth(), false, false, None, 1)
410 }
411}
412
413impl Cheatcode for expectRevert_1Call {
414 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
415 let Self { revertData } = self;
416 expect_revert(
417 ccx.state,
418 Some(revertData.as_ref()),
419 ccx.ecx.journal().depth(),
420 false,
421 false,
422 None,
423 1,
424 )
425 }
426}
427
428impl Cheatcode for expectRevert_2Call {
429 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
430 let Self { revertData } = self;
431 expect_revert(ccx.state, Some(revertData), ccx.ecx.journal().depth(), false, false, None, 1)
432 }
433}
434
435impl Cheatcode for expectRevert_3Call {
436 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
437 let Self { reverter } = self;
438 expect_revert(ccx.state, None, ccx.ecx.journal().depth(), false, false, Some(*reverter), 1)
439 }
440}
441
442impl Cheatcode for expectRevert_4Call {
443 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
444 let Self { revertData, reverter } = self;
445 expect_revert(
446 ccx.state,
447 Some(revertData.as_ref()),
448 ccx.ecx.journal().depth(),
449 false,
450 false,
451 Some(*reverter),
452 1,
453 )
454 }
455}
456
457impl Cheatcode for expectRevert_5Call {
458 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
459 let Self { revertData, reverter } = self;
460 expect_revert(
461 ccx.state,
462 Some(revertData),
463 ccx.ecx.journal().depth(),
464 false,
465 false,
466 Some(*reverter),
467 1,
468 )
469 }
470}
471
472impl Cheatcode for expectRevert_6Call {
473 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
474 let Self { count } = self;
475 expect_revert(ccx.state, None, ccx.ecx.journal().depth(), false, false, None, *count)
476 }
477}
478
479impl Cheatcode for expectRevert_7Call {
480 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
481 let Self { revertData, count } = self;
482 expect_revert(
483 ccx.state,
484 Some(revertData.as_ref()),
485 ccx.ecx.journal().depth(),
486 false,
487 false,
488 None,
489 *count,
490 )
491 }
492}
493
494impl Cheatcode for expectRevert_8Call {
495 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
496 let Self { revertData, count } = self;
497 expect_revert(
498 ccx.state,
499 Some(revertData),
500 ccx.ecx.journal().depth(),
501 false,
502 false,
503 None,
504 *count,
505 )
506 }
507}
508
509impl Cheatcode for expectRevert_9Call {
510 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
511 let Self { reverter, count } = self;
512 expect_revert(
513 ccx.state,
514 None,
515 ccx.ecx.journal().depth(),
516 false,
517 false,
518 Some(*reverter),
519 *count,
520 )
521 }
522}
523
524impl Cheatcode for expectRevert_10Call {
525 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
526 let Self { revertData, reverter, count } = self;
527 expect_revert(
528 ccx.state,
529 Some(revertData.as_ref()),
530 ccx.ecx.journal().depth(),
531 false,
532 false,
533 Some(*reverter),
534 *count,
535 )
536 }
537}
538
539impl Cheatcode for expectRevert_11Call {
540 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
541 let Self { revertData, reverter, count } = self;
542 expect_revert(
543 ccx.state,
544 Some(revertData),
545 ccx.ecx.journal().depth(),
546 false,
547 false,
548 Some(*reverter),
549 *count,
550 )
551 }
552}
553
554impl Cheatcode for expectPartialRevert_0Call {
555 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
556 let Self { revertData } = self;
557 expect_revert(
558 ccx.state,
559 Some(revertData.as_ref()),
560 ccx.ecx.journal().depth(),
561 false,
562 true,
563 None,
564 1,
565 )
566 }
567}
568
569impl Cheatcode for expectPartialRevert_1Call {
570 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
571 let Self { revertData, reverter } = self;
572 expect_revert(
573 ccx.state,
574 Some(revertData.as_ref()),
575 ccx.ecx.journal().depth(),
576 false,
577 true,
578 Some(*reverter),
579 1,
580 )
581 }
582}
583
584impl Cheatcode for _expectCheatcodeRevert_0Call {
585 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
586 expect_revert(ccx.state, None, ccx.ecx.journal().depth(), true, false, None, 1)
587 }
588}
589
590impl Cheatcode for _expectCheatcodeRevert_1Call {
591 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
592 let Self { revertData } = self;
593 expect_revert(
594 ccx.state,
595 Some(revertData.as_ref()),
596 ccx.ecx.journal().depth(),
597 true,
598 false,
599 None,
600 1,
601 )
602 }
603}
604
605impl Cheatcode for _expectCheatcodeRevert_2Call {
606 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
607 let Self { revertData } = self;
608 expect_revert(ccx.state, Some(revertData), ccx.ecx.journal().depth(), true, false, None, 1)
609 }
610}
611
612impl Cheatcode for expectSafeMemoryCall {
613 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
614 let Self { min, max } = *self;
615 expect_safe_memory(ccx.state, min, max, ccx.ecx.journal().depth().try_into()?)
616 }
617}
618
619impl Cheatcode for stopExpectSafeMemoryCall {
620 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
621 let Self {} = self;
622 ccx.state.allowed_mem_writes.remove(&ccx.ecx.journal().depth().try_into()?);
623 Ok(Default::default())
624 }
625}
626
627impl Cheatcode for expectSafeMemoryCallCall {
628 fn apply_stateful<CTX: ContextTr>(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result {
629 let Self { min, max } = *self;
630 expect_safe_memory(ccx.state, min, max, (ccx.ecx.journal().depth() + 1).try_into()?)
631 }
632}
633
634impl RevertParameters for ExpectedRevert {
635 fn reverter(&self) -> Option<Address> {
636 self.reverter
637 }
638
639 fn reason(&self) -> Option<&[u8]> {
640 self.reason.as_ref().map(|b| &***b)
641 }
642
643 fn partial_match(&self) -> bool {
644 self.partial_match
645 }
646}
647
648#[expect(clippy::too_many_arguments)] fn expect_call(
666 state: &mut Cheatcodes,
667 target: &Address,
668 calldata: &Bytes,
669 value: Option<&U256>,
670 mut gas: Option<u64>,
671 mut min_gas: Option<u64>,
672 count: u64,
673 call_type: ExpectedCallType,
674) -> Result {
675 let expecteds = state.expected_calls.entry(*target).or_default();
676
677 if let Some(val) = value
678 && *val > U256::ZERO
679 {
680 let positive_value_cost_stipend = 2300;
683 if let Some(gas) = &mut gas {
684 *gas += positive_value_cost_stipend;
685 }
686 if let Some(min_gas) = &mut min_gas {
687 *min_gas += positive_value_cost_stipend;
688 }
689 }
690
691 match call_type {
692 ExpectedCallType::Count => {
693 ensure!(
697 !expecteds.contains_key(calldata),
698 "counted expected calls can only bet set once"
699 );
700 expecteds.insert(
701 calldata.clone(),
702 (ExpectedCallData { value: value.copied(), gas, min_gas, count, call_type }, 0),
703 );
704 }
705 ExpectedCallType::NonCount => {
706 match expecteds.entry(calldata.clone()) {
709 Entry::Occupied(mut entry) => {
710 let (expected, _) = entry.get_mut();
711 ensure!(
713 expected.call_type == ExpectedCallType::NonCount,
714 "cannot overwrite a counted expectCall with a non-counted expectCall"
715 );
716 expected.count += 1;
717 }
718 Entry::Vacant(entry) => {
720 entry.insert((
721 ExpectedCallData { value: value.copied(), gas, min_gas, count, call_type },
722 0,
723 ));
724 }
725 }
726 }
727 }
728
729 Ok(Default::default())
730}
731
732fn expect_emit(
733 state: &mut Cheatcodes,
734 depth: usize,
735 checks: [bool; 5],
736 address: Option<Address>,
737 anonymous: bool,
738 count: u64,
739) -> Result {
740 let expected_emit = ExpectedEmit {
741 depth,
742 checks,
743 address,
744 found: false,
745 log: None,
746 anonymous,
747 count,
748 mismatch_error: None,
749 };
750 if let Some(found_emit_pos) = state.expected_emits.iter().position(|(emit, _)| emit.found) {
751 state.expected_emits.insert(found_emit_pos, (expected_emit, Default::default()));
754 } else {
755 state.expected_emits.push_back((expected_emit, Default::default()));
757 }
758
759 Ok(Default::default())
760}
761
762pub(crate) fn handle_expect_emit(
763 state: &mut Cheatcodes,
764 log: &alloy_primitives::Log,
765 mut interpreter: Option<&mut Interpreter>,
766) -> Option<&'static str> {
767 let mut should_fail = None;
770
771 if state.expected_emits.iter().all(|(expected, _)| expected.found) {
782 return should_fail;
783 }
784
785 for (expected_emit, _) in &state.expected_emits {
787 if expected_emit.count == 0
788 && !expected_emit.found
789 && let Some(expected_log) = &expected_emit.log
790 && checks_topics_and_data(expected_emit.checks, expected_log, log)
791 && (expected_emit.address.is_none() || expected_emit.address == Some(log.address))
793 {
794 if let Some(interpreter) = &mut interpreter {
795 interpreter.bytecode.set_action(InterpreterAction::new_return(
798 InstructionResult::Revert,
799 Error::encode("log emitted but expected 0 times"),
800 interpreter.gas,
801 ));
802 } else {
803 should_fail = Some("log emitted but expected 0 times");
804 }
805
806 return should_fail;
807 }
808 }
809
810 let should_fill_logs = state.expected_emits.iter().any(|(expected, _)| expected.log.is_none());
811 let index_to_fill_or_check = if should_fill_logs {
812 state
815 .expected_emits
816 .iter()
817 .position(|(emit, _)| emit.found)
818 .unwrap_or(state.expected_emits.len())
819 .saturating_sub(1)
820 } else {
821 state.expected_emits.iter().position(|(emit, _)| !emit.found && emit.count > 0).unwrap_or(0)
825 };
826
827 if !should_fill_logs
829 && state.expected_emits.iter().all(|(emit, _)| emit.found || emit.count == 0)
830 {
831 return should_fail;
832 }
833
834 let (mut event_to_fill_or_check, mut count_map) = state
835 .expected_emits
836 .remove(index_to_fill_or_check)
837 .expect("we should have an emit to fill or check");
838
839 let Some(expected) = &event_to_fill_or_check.log else {
840 if event_to_fill_or_check.anonymous || !log.topics().is_empty() {
843 event_to_fill_or_check.log = Some(log.data.clone());
844 state
846 .expected_emits
847 .insert(index_to_fill_or_check, (event_to_fill_or_check, count_map));
848 } else if let Some(interpreter) = &mut interpreter {
849 interpreter.bytecode.set_action(InterpreterAction::new_return(
850 InstructionResult::Revert,
851 Error::encode("use vm.expectEmitAnonymous to match anonymous events"),
852 interpreter.gas,
853 ));
854 } else {
855 should_fail = Some("use vm.expectEmitAnonymous to match anonymous events");
856 }
857
858 return should_fail;
859 };
860
861 match count_map.entry(log.address) {
863 Entry::Occupied(mut entry) => {
864 let log_count_map = entry.get_mut();
865 log_count_map.insert(&log.data);
866 }
867 Entry::Vacant(entry) => {
868 let mut log_count_map = LogCountMap::new(&event_to_fill_or_check);
869 if log_count_map.satisfies_checks(&log.data) {
870 log_count_map.insert(&log.data);
871 entry.insert(log_count_map);
872 }
873 }
874 }
875
876 event_to_fill_or_check.found = || -> bool {
877 if !checks_topics_and_data(event_to_fill_or_check.checks, expected, log) {
878 let (expected_decoded, actual_decoded) = if let Some(signatures_identifier) =
882 state.signatures_identifier()
883 && !event_to_fill_or_check.anonymous
884 {
885 (
886 decode_event(signatures_identifier, expected),
887 decode_event(signatures_identifier, log),
888 )
889 } else {
890 (None, None)
891 };
892 event_to_fill_or_check.mismatch_error = Some(get_emit_mismatch_message(
893 event_to_fill_or_check.checks,
894 expected,
895 log,
896 event_to_fill_or_check.anonymous,
897 expected_decoded.as_ref(),
898 actual_decoded.as_ref(),
899 ));
900 return false;
901 }
902
903 if event_to_fill_or_check.address.is_some_and(|addr| addr != log.address) {
905 event_to_fill_or_check.mismatch_error = Some(format!(
906 "log emitter mismatch: expected={:#x}, got={:#x}",
907 event_to_fill_or_check.address.unwrap(),
908 log.address
909 ));
910 return false;
911 }
912
913 let expected_count = event_to_fill_or_check.count;
914 match event_to_fill_or_check.address {
915 Some(emitter) => count_map
916 .get(&emitter)
917 .is_some_and(|log_map| log_map.count(&log.data) >= expected_count),
918 None => count_map
919 .values()
920 .find(|log_map| log_map.satisfies_checks(&log.data))
921 .is_some_and(|map| map.count(&log.data) >= expected_count),
922 }
923 }();
924
925 if 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 should_fail
936}
937
938pub type ExpectedEmitTracker = VecDeque<(ExpectedEmit, AddressHashMap<LogCountMap>)>;
943
944#[derive(Clone, Debug, Default)]
945pub struct LogCountMap {
946 checks: [bool; 5],
947 expected_log: RawLog,
948 map: HashMap<RawLog, u64>,
949}
950
951impl LogCountMap {
952 fn new(expected_emit: &ExpectedEmit) -> Self {
954 Self {
955 checks: expected_emit.checks,
956 expected_log: expected_emit.log.clone().expect("log should be filled here"),
957 map: Default::default(),
958 }
959 }
960
961 fn insert(&mut self, log: &RawLog) -> bool {
967 if self.map.contains_key(log) {
969 self.map.entry(log.clone()).and_modify(|c| *c += 1);
970
971 return true;
972 }
973
974 if !self.satisfies_checks(log) {
975 return false;
976 }
977
978 self.map.entry(log.clone()).and_modify(|c| *c += 1).or_insert(1);
979
980 true
981 }
982
983 fn satisfies_checks(&self, log: &RawLog) -> bool {
985 checks_topics_and_data(self.checks, &self.expected_log, log)
986 }
987
988 pub fn count(&self, log: &RawLog) -> u64 {
989 if !self.satisfies_checks(log) {
990 return 0;
991 }
992
993 self.count_unchecked()
994 }
995
996 pub fn count_unchecked(&self) -> u64 {
997 self.map.values().sum()
998 }
999}
1000
1001fn expect_create(
1002 state: &mut Cheatcodes,
1003 bytecode: Bytes,
1004 deployer: Address,
1005 create_scheme: CreateScheme,
1006) -> Result {
1007 let expected_create = ExpectedCreate { bytecode, deployer, create_scheme };
1008 state.expected_creates.push(expected_create);
1009
1010 Ok(Default::default())
1011}
1012
1013fn expect_revert(
1014 state: &mut Cheatcodes,
1015 reason: Option<&[u8]>,
1016 depth: usize,
1017 cheatcode: bool,
1018 partial_match: bool,
1019 reverter: Option<Address>,
1020 count: u64,
1021) -> Result {
1022 ensure!(
1023 state.expected_revert.is_none(),
1024 "you must call another function prior to expecting a second revert"
1025 );
1026 state.expected_revert = Some(ExpectedRevert {
1027 reason: reason.map(Bytes::copy_from_slice),
1028 depth,
1029 kind: if cheatcode {
1030 ExpectedRevertKind::Cheatcode { pending_processing: true }
1031 } else {
1032 ExpectedRevertKind::Default
1033 },
1034 partial_match,
1035 reverter,
1036 reverted_by: None,
1037 max_depth: depth,
1038 count,
1039 actual_count: 0,
1040 });
1041 Ok(Default::default())
1042}
1043
1044fn checks_topics_and_data(checks: [bool; 5], expected: &RawLog, log: &RawLog) -> bool {
1045 if log.topics().len() != expected.topics().len() {
1046 return false;
1047 }
1048
1049 if !log
1051 .topics()
1052 .iter()
1053 .enumerate()
1054 .filter(|(i, _)| checks[*i])
1055 .all(|(i, topic)| topic == &expected.topics()[i])
1056 {
1057 return false;
1058 }
1059
1060 if checks[4] && expected.data.as_ref() != log.data.as_ref() {
1062 return false;
1063 }
1064
1065 true
1066}
1067
1068fn decode_event(
1069 identifier: &foundry_evm_traces::identifier::SignaturesIdentifier,
1070 log: &RawLog,
1071) -> Option<DecodedCallLog> {
1072 let topics = log.topics();
1073 if topics.is_empty() {
1074 return None;
1075 }
1076 let t0 = topics[0]; let event = foundry_common::block_on(identifier.identify_event(t0))?;
1079
1080 let has_indexed_info = event.inputs.iter().any(|p| p.indexed);
1082 let indexed_event = if has_indexed_info { event } else { get_indexed_event(event, log) };
1084
1085 if let Ok(decoded) = indexed_event.decode_log(log) {
1087 let params = reconstruct_params(&indexed_event, &decoded);
1088
1089 let decoded_params = params
1090 .into_iter()
1091 .zip(indexed_event.inputs.iter())
1092 .map(|(param, input)| (input.name.clone(), format_token(¶m)))
1093 .collect();
1094
1095 return Some(DecodedCallLog {
1096 name: Some(indexed_event.name),
1097 params: Some(decoded_params),
1098 });
1099 }
1100
1101 None
1102}
1103
1104fn reconstruct_params(event: &Event, decoded: &alloy_dyn_abi::DecodedEvent) -> Vec<DynSolValue> {
1106 let mut indexed = 0;
1107 let mut unindexed = 0;
1108 let mut inputs = vec![];
1109 for input in &event.inputs {
1110 if input.indexed && indexed < decoded.indexed.len() {
1111 inputs.push(decoded.indexed[indexed].clone());
1112 indexed += 1;
1113 } else if unindexed < decoded.body.len() {
1114 inputs.push(decoded.body[unindexed].clone());
1115 unindexed += 1;
1116 }
1117 }
1118 inputs
1119}
1120
1121pub(crate) fn get_emit_mismatch_message(
1123 checks: [bool; 5],
1124 expected: &RawLog,
1125 actual: &RawLog,
1126 is_anonymous: bool,
1127 expected_decoded: Option<&DecodedCallLog>,
1128 actual_decoded: Option<&DecodedCallLog>,
1129) -> String {
1130 if actual.topics().len() != expected.topics().len() {
1134 return name_mismatched_logs(expected_decoded, actual_decoded);
1135 }
1136
1137 if !is_anonymous
1139 && checks[0]
1140 && (!expected.topics().is_empty() && !actual.topics().is_empty())
1141 && expected.topics()[0] != actual.topics()[0]
1142 {
1143 return name_mismatched_logs(expected_decoded, actual_decoded);
1144 }
1145
1146 let expected_data = expected.data.as_ref();
1147 let actual_data = actual.data.as_ref();
1148
1149 if checks[4] && expected_data != actual_data {
1151 if expected_data.len() != actual_data.len()
1153 || !expected_data.len().is_multiple_of(32)
1154 || expected_data.is_empty()
1155 {
1156 return name_mismatched_logs(expected_decoded, actual_decoded);
1157 }
1158 }
1159
1160 let mut mismatches = Vec::new();
1162
1163 for (i, (expected_topic, actual_topic)) in
1165 expected.topics().iter().zip(actual.topics().iter()).enumerate()
1166 {
1167 if i == 0 && !is_anonymous {
1169 continue;
1170 }
1171
1172 if i < checks.len() && checks[i] && expected_topic != actual_topic {
1174 let param_idx = if is_anonymous {
1175 i } else {
1177 i - 1 };
1179 mismatches
1180 .push(format!("param {param_idx}: expected={expected_topic}, got={actual_topic}"));
1181 }
1182 }
1183
1184 if checks[4] && expected_data != actual_data {
1186 let num_indexed_params = if is_anonymous {
1187 expected.topics().len()
1188 } else {
1189 expected.topics().len().saturating_sub(1)
1190 };
1191
1192 for (i, (expected_chunk, actual_chunk)) in
1193 expected_data.chunks(32).zip(actual_data.chunks(32)).enumerate()
1194 {
1195 if expected_chunk != actual_chunk {
1196 let param_idx = num_indexed_params + i;
1197 mismatches.push(format!(
1198 "param {}: expected={}, got={}",
1199 param_idx,
1200 hex::encode_prefixed(expected_chunk),
1201 hex::encode_prefixed(actual_chunk)
1202 ));
1203 }
1204 }
1205 }
1206
1207 if mismatches.is_empty() {
1208 name_mismatched_logs(expected_decoded, actual_decoded)
1209 } else {
1210 let event_prefix = match (expected_decoded, actual_decoded) {
1212 (Some(expected_dec), Some(actual_dec)) if expected_dec.name == actual_dec.name => {
1213 format!(
1214 "{} param mismatch",
1215 expected_dec.name.as_ref().unwrap_or(&"log".to_string())
1216 )
1217 }
1218 _ => {
1219 if is_anonymous {
1220 "anonymous log mismatch".to_string()
1221 } else {
1222 "log mismatch".to_string()
1223 }
1224 }
1225 };
1226
1227 let detailed_mismatches = if let (Some(expected_dec), Some(actual_dec)) =
1229 (expected_decoded, actual_decoded)
1230 && let (Some(expected_params), Some(actual_params)) =
1231 (&expected_dec.params, &actual_dec.params)
1232 {
1233 mismatches
1234 .into_iter()
1235 .map(|basic_mismatch| {
1236 if let Some(param_idx) = basic_mismatch
1238 .split(' ')
1239 .nth(1)
1240 .and_then(|s| s.trim_end_matches(':').parse::<usize>().ok())
1241 && param_idx < expected_params.len()
1242 && param_idx < actual_params.len()
1243 {
1244 let (expected_name, expected_value) = &expected_params[param_idx];
1245 let (_actual_name, actual_value) = &actual_params[param_idx];
1246 let param_name = if !expected_name.is_empty() {
1247 expected_name
1248 } else {
1249 &format!("param{param_idx}")
1250 };
1251 return format!(
1252 "{param_name}: expected={expected_value}, got={actual_value}",
1253 );
1254 }
1255 basic_mismatch
1256 })
1257 .collect::<Vec<_>>()
1258 } else {
1259 mismatches
1260 };
1261
1262 format!("{} at {}", event_prefix, detailed_mismatches.join(", "))
1263 }
1264}
1265
1266fn name_mismatched_logs(
1268 expected_decoded: Option<&DecodedCallLog>,
1269 actual_decoded: Option<&DecodedCallLog>,
1270) -> String {
1271 let expected_name = expected_decoded.and_then(|d| d.name.as_deref()).unwrap_or("log");
1272 let actual_name = actual_decoded.and_then(|d| d.name.as_deref()).unwrap_or("log");
1273 format!("{actual_name} != expected {expected_name}")
1274}
1275
1276fn expect_safe_memory(state: &mut Cheatcodes, start: u64, end: u64, depth: u64) -> Result {
1277 ensure!(start < end, "memory range start ({start}) is greater than end ({end})");
1278 #[expect(clippy::single_range_in_vec_init)] let offsets = state.allowed_mem_writes.entry(depth).or_insert_with(|| vec![0..0x60]);
1280 offsets.push(start..end);
1281 Ok(Default::default())
1282}