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