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;
24pub type ExpectedCallTracker = HashMap<Address, HashMap<Bytes, (ExpectedCallData, u64)>>;
34
35#[derive(Clone, Debug)]
36pub struct ExpectedCallData {
37 pub value: Option<U256>,
39 pub gas: Option<u64>,
41 pub min_gas: Option<u64>,
43 pub count: u64,
48 pub call_type: ExpectedCallType,
50}
51
52#[derive(Clone, Debug, PartialEq, Eq)]
54pub enum ExpectedCallType {
55 NonCount,
57 Count,
59}
60
61#[derive(Clone, Debug)]
63pub enum ExpectedRevertKind {
64 Default,
66 Cheatcode { pending_processing: bool },
72}
73
74#[derive(Clone, Debug)]
75pub struct ExpectedRevert {
76 pub reason: Option<Bytes>,
78 pub depth: usize,
80 pub kind: ExpectedRevertKind,
82 pub partial_match: bool,
84 pub reverter: Option<Address>,
86 pub reverted_by: Option<Address>,
88 pub max_depth: usize,
90 pub count: u64,
92 pub actual_count: u64,
94}
95
96#[derive(Clone, Debug)]
97pub struct ExpectedEmit {
98 pub depth: usize,
100 pub log: Option<RawLog>,
102 pub checks: [bool; 5],
109 pub address: Option<Address>,
111 pub anonymous: bool,
114 pub found: bool,
116 pub count: u64,
118 pub mismatch_error: Option<String>,
120}
121
122#[derive(Clone, Debug)]
123pub struct ExpectedCreate {
124 pub deployer: Address,
126 pub bytecode: Bytes,
128 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#[expect(clippy::too_many_arguments)] fn 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 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 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 match expecteds.entry(calldata.clone()) {
710 Entry::Occupied(mut entry) => {
711 let (expected, _) = entry.get_mut();
712 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 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 state.expected_emits.insert(found_emit_pos, (expected_emit, Default::default()));
755 } else {
756 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 let mut failure_reason = None;
771
772 if state.expected_emits.iter().all(|(expected, _)| expected.found) {
783 return failure_reason;
784 }
785
786 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 && (expected_emit.address.is_none() || expected_emit.address == Some(log.address))
794 {
795 if let Some(interpreter) = &mut interpreter {
796 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 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 state.expected_emits.iter().position(|(emit, _)| !emit.found && emit.count > 0).unwrap_or(0)
826 };
827
828 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 if event_to_fill_or_check.anonymous || !log.topics().is_empty() {
844 event_to_fill_or_check.log = Some(log.data.clone());
845 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 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 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 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 event_to_fill_or_check.found {
929 state.expected_emits.push_back((event_to_fill_or_check, count_map));
930 } else {
931 state.expected_emits.push_front((event_to_fill_or_check, count_map));
934 }
935
936 failure_reason
937}
938
939pub 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 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 fn insert(&mut self, log: &RawLog) -> bool {
968 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 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 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 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]; let event = foundry_common::block_on(identifier.identify_event(t0))?;
1080
1081 let has_indexed_info = event.inputs.iter().any(|p| p.indexed);
1083 let indexed_event = if has_indexed_info { event } else { get_indexed_event(event, log) };
1085
1086 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(¶m)))
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
1105fn 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
1122pub(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 if actual.topics().len() != expected.topics().len() {
1135 return name_mismatched_logs(expected_decoded, actual_decoded);
1136 }
1137
1138 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 if checks[4] && expected_data != actual_data {
1152 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 let mut mismatches = Vec::new();
1163
1164 for (i, (expected_topic, actual_topic)) in
1166 expected.topics().iter().zip(actual.topics().iter()).enumerate()
1167 {
1168 if i == 0 && !is_anonymous {
1170 continue;
1171 }
1172
1173 if i < checks.len() && checks[i] && expected_topic != actual_topic {
1175 let param_idx = if is_anonymous {
1176 i } else {
1178 i - 1 };
1180 mismatches
1181 .push(format!("param {param_idx}: expected={expected_topic}, got={actual_topic}"));
1182 }
1183 }
1184
1185 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 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 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 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
1267fn 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)] let offsets = state.allowed_mem_writes.entry(depth).or_insert_with(|| vec![0..0x60]);
1286 offsets.push(start..end);
1287 Ok(Default::default())
1288}