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::{SolCall, 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 CallScheme, InstructionResult, Interpreter, InterpreterAction,
21 interpreter_types::LoopControl,
22 },
23};
24use tempo_contracts::precompiles::ISignatureVerifier;
25use tempo_precompiles::SIGNATURE_VERIFIER_ADDRESS;
26
27use super::revert_handlers::RevertParameters;
28pub type ExpectedCallTracker =
39 HashMap<Address, HashMap<(Bytes, Option<CallScheme>), (ExpectedCallData, u64)>>;
40
41#[derive(Clone, Debug)]
42pub struct ExpectedCallData {
43 pub value: Option<U256>,
45 pub gas: Option<u64>,
47 pub min_gas: Option<u64>,
49 pub count: u64,
54 pub call_type: ExpectedCallType,
56}
57
58#[derive(Clone, Debug, PartialEq, Eq)]
60pub enum ExpectedCallType {
61 NonCount,
63 Count,
65}
66
67#[derive(Clone, Debug)]
69pub enum ExpectedRevertKind {
70 Default,
72 Cheatcode { pending_processing: bool },
78}
79
80#[derive(Clone, Debug)]
81pub struct ExpectedRevert {
82 pub reason: Option<Bytes>,
84 pub depth: usize,
86 pub kind: ExpectedRevertKind,
88 pub partial_match: bool,
90 pub reverter: Option<Address>,
92 pub reverted_by: Option<Address>,
94 pub max_depth: usize,
96 pub count: u64,
98 pub actual_count: u64,
100}
101
102#[derive(Clone, Debug)]
103pub struct ExpectedEmit {
104 pub depth: usize,
106 pub log: Option<RawLog>,
108 pub checks: [bool; 5],
115 pub address: Option<Address>,
117 pub anonymous: bool,
120 pub found: bool,
122 pub count: u64,
124 pub mismatch_error: Option<EmitMismatch>,
126}
127
128#[derive(Clone, Debug)]
129pub enum EmitMismatch {
130 Log { actual: RawLog },
131 Emitter { expected: Address, actual: Address },
132}
133
134impl EmitMismatch {
135 pub fn to_error_msg<FEN: FoundryEvmNetwork>(
136 &self,
137 state: &Cheatcodes<FEN>,
138 checks: [bool; 5],
139 expected: Option<&RawLog>,
140 anonymous: bool,
141 ) -> String {
142 match self {
143 Self::Log { actual } => {
144 let Some(expected) = expected else {
145 return "log != expected log".to_string();
146 };
147 let (expected_decoded, actual_decoded) = if anonymous {
148 (None, None)
149 } else {
150 state
151 .signatures_identifier()
152 .map(|identifier| {
153 (decode_event(identifier, expected), decode_event(identifier, actual))
154 })
155 .unwrap_or_default()
156 };
157 get_emit_mismatch_message(
158 checks,
159 expected,
160 actual,
161 anonymous,
162 expected_decoded.as_ref(),
163 actual_decoded.as_ref(),
164 )
165 }
166 Self::Emitter { expected, actual } => {
167 format!("log emitter mismatch: expected={expected:#x}, got={actual:#x}")
168 }
169 }
170 }
171}
172
173#[derive(Clone, Debug)]
174pub struct ExpectedCreate {
175 pub deployer: Address,
177 pub bytecode: Bytes,
179 pub create_scheme: CreateScheme,
181}
182
183#[derive(Clone, Debug)]
184pub enum CreateScheme {
185 Create,
186 Create2,
187}
188
189impl Display for CreateScheme {
190 fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
191 match self {
192 Self::Create => write!(f, "CREATE"),
193 Self::Create2 => write!(f, "CREATE2"),
194 }
195 }
196}
197
198impl From<revm::context_interface::CreateScheme> for CreateScheme {
199 fn from(scheme: revm::context_interface::CreateScheme) -> Self {
200 match scheme {
201 revm::context_interface::CreateScheme::Create => Self::Create,
202 revm::context_interface::CreateScheme::Create2 { .. } => Self::Create2,
203 _ => unimplemented!("Unsupported create scheme"),
204 }
205 }
206}
207
208impl CreateScheme {
209 pub const fn eq(&self, create_scheme: Self) -> bool {
210 matches!(
211 (self, create_scheme),
212 (Self::Create, Self::Create) | (Self::Create2, Self::Create2 { .. })
213 )
214 }
215}
216
217impl Cheatcode for expectCall_0Call {
218 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
219 let Self { callee, data } = self;
220 expect_call(state, callee, data, None, None, None, None, 1, ExpectedCallType::NonCount)
221 }
222}
223
224impl Cheatcode for expectCall_1Call {
225 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
226 let Self { callee, data, count } = self;
227 expect_call(state, callee, data, None, None, None, None, *count, ExpectedCallType::Count)
228 }
229}
230
231impl Cheatcode for expectCall_2Call {
232 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
233 let Self { callee, msgValue, data } = self;
234 expect_call(
235 state,
236 callee,
237 data,
238 Some(msgValue),
239 None,
240 None,
241 None,
242 1,
243 ExpectedCallType::NonCount,
244 )
245 }
246}
247
248impl Cheatcode for expectCall_3Call {
249 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
250 let Self { callee, msgValue, data, count } = self;
251 expect_call(
252 state,
253 callee,
254 data,
255 Some(msgValue),
256 None,
257 None,
258 None,
259 *count,
260 ExpectedCallType::Count,
261 )
262 }
263}
264
265impl Cheatcode for expectCall_4Call {
266 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
267 let Self { callee, msgValue, gas, data } = self;
268 expect_call(
269 state,
270 callee,
271 data,
272 Some(msgValue),
273 Some(*gas),
274 None,
275 None,
276 1,
277 ExpectedCallType::NonCount,
278 )
279 }
280}
281
282impl Cheatcode for expectCall_5Call {
283 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
284 let Self { callee, msgValue, gas, data, count } = self;
285 expect_call(
286 state,
287 callee,
288 data,
289 Some(msgValue),
290 Some(*gas),
291 None,
292 None,
293 *count,
294 ExpectedCallType::Count,
295 )
296 }
297}
298
299impl Cheatcode for expectDelegateCallCall {
300 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
301 let Self { callee, data } = self;
302 expect_call(
303 state,
304 callee,
305 data,
306 None,
307 None,
308 None,
309 Some(CallScheme::DelegateCall),
310 1,
311 ExpectedCallType::NonCount,
312 )
313 }
314}
315
316impl Cheatcode for expectCallMinGas_0Call {
317 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
318 let Self { callee, msgValue, minGas, data } = self;
319 expect_call(
320 state,
321 callee,
322 data,
323 Some(msgValue),
324 None,
325 Some(*minGas),
326 None,
327 1,
328 ExpectedCallType::NonCount,
329 )
330 }
331}
332
333impl Cheatcode for expectCallMinGas_1Call {
334 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
335 let Self { callee, msgValue, minGas, data, count } = self;
336 expect_call(
337 state,
338 callee,
339 data,
340 Some(msgValue),
341 None,
342 Some(*minGas),
343 None,
344 *count,
345 ExpectedCallType::Count,
346 )
347 }
348}
349
350impl Cheatcode for expectEmit_0Call {
351 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
352 let Self { checkTopic1, checkTopic2, checkTopic3, checkData } = *self;
353 expect_emit(
354 ccx.state,
355 ccx.ecx.journal().depth(),
356 [true, checkTopic1, checkTopic2, checkTopic3, checkData],
357 None,
358 false,
359 1,
360 )
361 }
362}
363
364impl Cheatcode for expectEmit_1Call {
365 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
366 let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self;
367 expect_emit(
368 ccx.state,
369 ccx.ecx.journal().depth(),
370 [true, checkTopic1, checkTopic2, checkTopic3, checkData],
371 Some(emitter),
372 false,
373 1,
374 )
375 }
376}
377
378impl Cheatcode for expectEmit_2Call {
379 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
380 let Self {} = self;
381 expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], None, false, 1)
382 }
383}
384
385impl Cheatcode for expectEmit_3Call {
386 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
387 let Self { emitter } = *self;
388 expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], Some(emitter), false, 1)
389 }
390}
391
392impl Cheatcode for expectEmit_4Call {
393 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
394 let Self { checkTopic1, checkTopic2, checkTopic3, checkData, count } = *self;
395 expect_emit(
396 ccx.state,
397 ccx.ecx.journal().depth(),
398 [true, checkTopic1, checkTopic2, checkTopic3, checkData],
399 None,
400 false,
401 count,
402 )
403 }
404}
405
406impl Cheatcode for expectEmit_5Call {
407 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
408 let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter, count } = *self;
409 expect_emit(
410 ccx.state,
411 ccx.ecx.journal().depth(),
412 [true, checkTopic1, checkTopic2, checkTopic3, checkData],
413 Some(emitter),
414 false,
415 count,
416 )
417 }
418}
419
420impl Cheatcode for expectEmit_6Call {
421 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
422 let Self { count } = *self;
423 expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], None, false, count)
424 }
425}
426
427impl Cheatcode for expectEmit_7Call {
428 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
429 let Self { emitter, count } = *self;
430 expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], Some(emitter), false, count)
431 }
432}
433
434impl Cheatcode for expectEmitAnonymous_0Call {
435 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
436 let Self { checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData } = *self;
437 expect_emit(
438 ccx.state,
439 ccx.ecx.journal().depth(),
440 [checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData],
441 None,
442 true,
443 1,
444 )
445 }
446}
447
448impl Cheatcode for expectEmitAnonymous_1Call {
449 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
450 let Self { checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self;
451 expect_emit(
452 ccx.state,
453 ccx.ecx.journal().depth(),
454 [checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData],
455 Some(emitter),
456 true,
457 1,
458 )
459 }
460}
461
462impl Cheatcode for expectEmitAnonymous_2Call {
463 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
464 let Self {} = self;
465 expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], None, true, 1)
466 }
467}
468
469impl Cheatcode for expectEmitAnonymous_3Call {
470 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
471 let Self { emitter } = *self;
472 expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], Some(emitter), true, 1)
473 }
474}
475
476impl Cheatcode for expectCreateCall {
477 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
478 let Self { bytecode, deployer } = self;
479 expect_create(state, bytecode.clone(), *deployer, CreateScheme::Create)
480 }
481}
482
483impl Cheatcode for expectCreate2Call {
484 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
485 let Self { bytecode, deployer } = self;
486 expect_create(state, bytecode.clone(), *deployer, CreateScheme::Create2)
487 }
488}
489
490impl Cheatcode for expectTip20LogoURIUpdatedCall {
491 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
492 let Self { token, updater, newLogoURI } = self;
493 expect_logo_uri_updated(ccx, token, updater, newLogoURI)
494 }
495}
496
497impl Cheatcode for expectKeychainVerifiedCall {
498 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
499 let Self { account, digest, signature } = self;
500 expect_keychain_verified(state, *account, *digest, signature.clone(), false)
501 }
502}
503
504impl Cheatcode for expectKeychainAdminVerifiedCall {
505 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
506 let Self { account, digest, signature } = self;
507 expect_keychain_verified(state, *account, *digest, signature.clone(), true)
508 }
509}
510
511impl Cheatcode for expectLogoURIUpdatedCall {
512 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
513 let Self { token, updater, newLogoURI } = self;
514 expect_logo_uri_updated(ccx, token, updater, newLogoURI)
515 }
516}
517
518fn expect_keychain_verified<FEN: FoundryEvmNetwork>(
519 state: &mut Cheatcodes<FEN>,
520 account: Address,
521 digest: alloy_primitives::B256,
522 signature: Bytes,
523 admin: bool,
524) -> Result {
525 let calldata = if admin {
526 ISignatureVerifier::verifyKeychainAdminCall { account, hash: digest, signature }
527 .abi_encode()
528 } else {
529 ISignatureVerifier::verifyKeychainCall { account, hash: digest, signature }.abi_encode()
530 };
531 expect_call(
532 state,
533 &SIGNATURE_VERIFIER_ADDRESS,
534 &Bytes::from(calldata),
535 None,
536 None,
537 None,
538 None,
539 1,
540 ExpectedCallType::NonCount,
541 )
542}
543
544fn expect_logo_uri_updated<FEN: FoundryEvmNetwork>(
545 ccx: &mut CheatsCtxt<'_, '_, FEN>,
546 token: &Address,
547 updater: &Address,
548 new_logo_uri: &str,
549) -> Result {
550 let expected_emit = ExpectedEmit {
551 depth: ccx.ecx.journal().depth(),
552 log: Some(RawLog::new_unchecked(
553 vec![keccak256("LogoURIUpdated(address,string)"), updater.into_word()],
554 new_logo_uri.abi_encode().into(),
555 )),
556 checks: [true, true, false, false, true],
557 address: Some(*token),
558 anonymous: false,
559 found: false,
560 count: 1,
561 mismatch_error: None,
562 };
563 ccx.state.expected_emits.push_back((expected_emit, Default::default()));
564 Ok(Default::default())
565}
566
567impl Cheatcode for expectRevert_0Call {
568 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
569 let Self {} = self;
570 expect_revert(ccx.state, None, ccx.ecx.journal().depth(), false, false, None, 1)
571 }
572}
573
574impl Cheatcode for expectRevert_1Call {
575 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
576 let Self { revertData } = self;
577 expect_revert(
578 ccx.state,
579 Some(revertData.as_ref()),
580 ccx.ecx.journal().depth(),
581 false,
582 false,
583 None,
584 1,
585 )
586 }
587}
588
589impl Cheatcode for expectRevert_2Call {
590 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
591 let Self { revertData } = self;
592 expect_revert(ccx.state, Some(revertData), ccx.ecx.journal().depth(), false, false, None, 1)
593 }
594}
595
596impl Cheatcode for expectRevert_3Call {
597 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
598 let Self { reverter } = self;
599 expect_revert(ccx.state, None, ccx.ecx.journal().depth(), false, false, Some(*reverter), 1)
600 }
601}
602
603impl Cheatcode for expectRevert_4Call {
604 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
605 let Self { revertData, reverter } = self;
606 expect_revert(
607 ccx.state,
608 Some(revertData.as_ref()),
609 ccx.ecx.journal().depth(),
610 false,
611 false,
612 Some(*reverter),
613 1,
614 )
615 }
616}
617
618impl Cheatcode for expectRevert_5Call {
619 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
620 let Self { revertData, reverter } = self;
621 expect_revert(
622 ccx.state,
623 Some(revertData),
624 ccx.ecx.journal().depth(),
625 false,
626 false,
627 Some(*reverter),
628 1,
629 )
630 }
631}
632
633impl Cheatcode for expectRevert_6Call {
634 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
635 let Self { count } = self;
636 expect_revert(ccx.state, None, ccx.ecx.journal().depth(), false, false, None, *count)
637 }
638}
639
640impl Cheatcode for expectRevert_7Call {
641 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
642 let Self { revertData, count } = self;
643 expect_revert(
644 ccx.state,
645 Some(revertData.as_ref()),
646 ccx.ecx.journal().depth(),
647 false,
648 false,
649 None,
650 *count,
651 )
652 }
653}
654
655impl Cheatcode for expectRevert_8Call {
656 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
657 let Self { revertData, count } = self;
658 expect_revert(
659 ccx.state,
660 Some(revertData),
661 ccx.ecx.journal().depth(),
662 false,
663 false,
664 None,
665 *count,
666 )
667 }
668}
669
670impl Cheatcode for expectRevert_9Call {
671 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
672 let Self { reverter, count } = self;
673 expect_revert(
674 ccx.state,
675 None,
676 ccx.ecx.journal().depth(),
677 false,
678 false,
679 Some(*reverter),
680 *count,
681 )
682 }
683}
684
685impl Cheatcode for expectRevert_10Call {
686 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
687 let Self { revertData, reverter, count } = self;
688 expect_revert(
689 ccx.state,
690 Some(revertData.as_ref()),
691 ccx.ecx.journal().depth(),
692 false,
693 false,
694 Some(*reverter),
695 *count,
696 )
697 }
698}
699
700impl Cheatcode for expectRevert_11Call {
701 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
702 let Self { revertData, reverter, count } = self;
703 expect_revert(
704 ccx.state,
705 Some(revertData),
706 ccx.ecx.journal().depth(),
707 false,
708 false,
709 Some(*reverter),
710 *count,
711 )
712 }
713}
714
715impl Cheatcode for expectPartialRevert_0Call {
716 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
717 let Self { revertData } = self;
718 expect_revert(
719 ccx.state,
720 Some(revertData.as_ref()),
721 ccx.ecx.journal().depth(),
722 false,
723 true,
724 None,
725 1,
726 )
727 }
728}
729
730impl Cheatcode for expectPartialRevert_1Call {
731 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
732 let Self { revertData, reverter } = self;
733 expect_revert(
734 ccx.state,
735 Some(revertData.as_ref()),
736 ccx.ecx.journal().depth(),
737 false,
738 true,
739 Some(*reverter),
740 1,
741 )
742 }
743}
744
745impl Cheatcode for _expectCheatcodeRevert_0Call {
746 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
747 expect_revert(ccx.state, None, ccx.ecx.journal().depth(), true, false, None, 1)
748 }
749}
750
751impl Cheatcode for _expectCheatcodeRevert_1Call {
752 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
753 let Self { revertData } = self;
754 expect_revert(
755 ccx.state,
756 Some(revertData.as_ref()),
757 ccx.ecx.journal().depth(),
758 true,
759 false,
760 None,
761 1,
762 )
763 }
764}
765
766impl Cheatcode for _expectCheatcodeRevert_2Call {
767 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
768 let Self { revertData } = self;
769 expect_revert(ccx.state, Some(revertData), ccx.ecx.journal().depth(), true, false, None, 1)
770 }
771}
772
773impl Cheatcode for expectSafeMemoryCall {
774 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
775 let Self { min, max } = *self;
776 expect_safe_memory(ccx.state, min, max, ccx.ecx.journal().depth().try_into()?)
777 }
778}
779
780impl Cheatcode for stopExpectSafeMemoryCall {
781 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
782 let Self {} = self;
783 ccx.state.allowed_mem_writes.remove(&ccx.ecx.journal().depth().try_into()?);
784 Ok(Default::default())
785 }
786}
787
788impl Cheatcode for expectSafeMemoryCallCall {
789 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
790 let Self { min, max } = *self;
791 expect_safe_memory(ccx.state, min, max, (ccx.ecx.journal().depth() + 1).try_into()?)
792 }
793}
794
795impl RevertParameters for ExpectedRevert {
796 fn reverter(&self) -> Option<Address> {
797 self.reverter
798 }
799
800 fn reason(&self) -> Option<&[u8]> {
801 self.reason.as_ref().map(|b| &***b)
802 }
803
804 fn partial_match(&self) -> bool {
805 self.partial_match
806 }
807}
808
809#[expect(clippy::too_many_arguments)] fn expect_call<FEN: FoundryEvmNetwork>(
827 state: &mut Cheatcodes<FEN>,
828 target: &Address,
829 calldata: &Bytes,
830 value: Option<&U256>,
831 mut gas: Option<u64>,
832 mut min_gas: Option<u64>,
833 scheme: Option<CallScheme>,
834 count: u64,
835 call_type: ExpectedCallType,
836) -> Result {
837 let expecteds = state.expected_calls.entry(*target).or_default();
838
839 if let Some(val) = value
840 && *val > U256::ZERO
841 {
842 let positive_value_cost_stipend = 2300;
845 if let Some(gas) = &mut gas {
846 *gas += positive_value_cost_stipend;
847 }
848 if let Some(min_gas) = &mut min_gas {
849 *min_gas += positive_value_cost_stipend;
850 }
851 }
852
853 match call_type {
854 ExpectedCallType::Count => {
855 let key = (calldata.clone(), scheme);
859 ensure!(!expecteds.contains_key(&key), "counted expected calls can only bet set once");
860 expecteds.insert(
861 key,
862 (ExpectedCallData { value: value.copied(), gas, min_gas, count, call_type }, 0),
863 );
864 }
865 ExpectedCallType::NonCount => {
866 match expecteds.entry((calldata.clone(), scheme)) {
869 Entry::Occupied(mut entry) => {
870 let (expected, _) = entry.get_mut();
871 ensure!(
873 expected.call_type == ExpectedCallType::NonCount,
874 "cannot overwrite a counted expectCall with a non-counted expectCall"
875 );
876 expected.count += 1;
877 }
878 Entry::Vacant(entry) => {
880 entry.insert((
881 ExpectedCallData { value: value.copied(), gas, min_gas, count, call_type },
882 0,
883 ));
884 }
885 }
886 }
887 }
888
889 Ok(Default::default())
890}
891
892fn expect_emit<FEN: FoundryEvmNetwork>(
893 state: &mut Cheatcodes<FEN>,
894 depth: usize,
895 checks: [bool; 5],
896 address: Option<Address>,
897 anonymous: bool,
898 count: u64,
899) -> Result {
900 let expected_emit = ExpectedEmit {
901 depth,
902 checks,
903 address,
904 found: false,
905 log: None,
906 anonymous,
907 count,
908 mismatch_error: None,
909 };
910 if let Some(found_emit_pos) = state.expected_emits.iter().position(|(emit, _)| emit.found) {
911 state.expected_emits.insert(found_emit_pos, (expected_emit, Default::default()));
914 } else {
915 state.expected_emits.push_back((expected_emit, Default::default()));
917 }
918
919 Ok(Default::default())
920}
921
922pub(crate) fn handle_expect_emit<FEN: FoundryEvmNetwork>(
923 state: &mut Cheatcodes<FEN>,
924 log: &alloy_primitives::Log,
925 mut interpreter: Option<&mut Interpreter>,
926) -> Option<&'static str> {
927 let mut failure_reason = None;
930
931 if state.expected_emits.iter().all(|(expected, _)| expected.found) {
942 return failure_reason;
943 }
944
945 for (expected_emit, _) in &state.expected_emits {
947 if expected_emit.count == 0
948 && !expected_emit.found
949 && let Some(expected_log) = &expected_emit.log
950 && checks_topics_and_data(expected_emit.checks, expected_log, log)
951 && (expected_emit.address.is_none_or(|address| address == log.address))
953 {
954 if let Some(interpreter) = &mut interpreter {
955 interpreter.bytecode.set_action(InterpreterAction::new_return(
958 InstructionResult::Revert,
959 Error::encode("log emitted but expected 0 times"),
960 interpreter.gas,
961 ));
962 } else {
963 failure_reason = Some("log emitted but expected 0 times");
964 }
965
966 return failure_reason;
967 }
968 }
969
970 let should_fill_logs = state.expected_emits.iter().any(|(expected, _)| expected.log.is_none());
971 let index_to_fill_or_check = if should_fill_logs {
972 state
975 .expected_emits
976 .iter()
977 .position(|(emit, _)| emit.found)
978 .unwrap_or(state.expected_emits.len())
979 .saturating_sub(1)
980 } else {
981 state.expected_emits.iter().position(|(emit, _)| !emit.found && emit.count > 0).unwrap_or(0)
985 };
986
987 if !should_fill_logs
989 && state.expected_emits.iter().all(|(emit, _)| emit.found || emit.count == 0)
990 {
991 return failure_reason;
992 }
993
994 let (mut event_to_fill_or_check, mut count_map) = state
995 .expected_emits
996 .remove(index_to_fill_or_check)
997 .expect("we should have an emit to fill or check");
998
999 let Some(expected) = &event_to_fill_or_check.log else {
1000 if event_to_fill_or_check.anonymous || !log.topics().is_empty() {
1003 event_to_fill_or_check.log = Some(log.data.clone());
1004 state
1006 .expected_emits
1007 .insert(index_to_fill_or_check, (event_to_fill_or_check, count_map));
1008 } else if let Some(interpreter) = &mut interpreter {
1009 interpreter.bytecode.set_action(InterpreterAction::new_return(
1010 InstructionResult::Revert,
1011 Error::encode("use vm.expectEmitAnonymous to match anonymous events"),
1012 interpreter.gas,
1013 ));
1014 } else {
1015 failure_reason = Some("use vm.expectEmitAnonymous to match anonymous events");
1016 }
1017
1018 return failure_reason;
1019 };
1020
1021 match count_map.entry(log.address) {
1023 Entry::Occupied(mut entry) => {
1024 let log_count_map = entry.get_mut();
1025 log_count_map.insert(&log.data);
1026 }
1027 Entry::Vacant(entry) => {
1028 let mut log_count_map = LogCountMap::new(&event_to_fill_or_check);
1029 if log_count_map.satisfies_checks(&log.data) {
1030 log_count_map.insert(&log.data);
1031 entry.insert(log_count_map);
1032 }
1033 }
1034 }
1035
1036 event_to_fill_or_check.found = || -> bool {
1037 if !checks_topics_and_data(event_to_fill_or_check.checks, expected, log) {
1038 event_to_fill_or_check.mismatch_error =
1039 Some(EmitMismatch::Log { actual: log.data.clone() });
1040 return false;
1041 }
1042
1043 if let Some(expected) = event_to_fill_or_check.address
1045 && expected != log.address
1046 {
1047 event_to_fill_or_check.mismatch_error =
1048 Some(EmitMismatch::Emitter { expected, actual: log.address });
1049 return false;
1050 }
1051
1052 let expected_count = event_to_fill_or_check.count;
1053 match event_to_fill_or_check.address {
1054 Some(emitter) => count_map
1055 .get(&emitter)
1056 .is_some_and(|log_map| log_map.count(&log.data) >= expected_count),
1057 None => count_map
1058 .values()
1059 .find(|log_map| log_map.satisfies_checks(&log.data))
1060 .is_some_and(|map| map.count(&log.data) >= expected_count),
1061 }
1062 }();
1063
1064 if event_to_fill_or_check.found {
1067 state.expected_emits.push_back((event_to_fill_or_check, count_map));
1068 } else {
1069 state.expected_emits.push_front((event_to_fill_or_check, count_map));
1072 }
1073
1074 failure_reason
1075}
1076
1077pub type ExpectedEmitTracker = VecDeque<(ExpectedEmit, AddressHashMap<LogCountMap>)>;
1082
1083#[derive(Clone, Debug, Default)]
1084pub struct LogCountMap {
1085 checks: [bool; 5],
1086 expected_log: RawLog,
1087 map: HashMap<RawLog, u64>,
1088}
1089
1090impl LogCountMap {
1091 fn new(expected_emit: &ExpectedEmit) -> Self {
1093 Self {
1094 checks: expected_emit.checks,
1095 expected_log: expected_emit.log.clone().expect("log should be filled here"),
1096 map: Default::default(),
1097 }
1098 }
1099
1100 fn insert(&mut self, log: &RawLog) -> bool {
1106 if self.map.contains_key(log) {
1108 self.map.entry(log.clone()).and_modify(|c| *c += 1);
1109
1110 return true;
1111 }
1112
1113 if !self.satisfies_checks(log) {
1114 return false;
1115 }
1116
1117 self.map.entry(log.clone()).and_modify(|c| *c += 1).or_insert(1);
1118
1119 true
1120 }
1121
1122 fn satisfies_checks(&self, log: &RawLog) -> bool {
1124 checks_topics_and_data(self.checks, &self.expected_log, log)
1125 }
1126
1127 pub fn count(&self, log: &RawLog) -> u64 {
1128 if !self.satisfies_checks(log) {
1129 return 0;
1130 }
1131
1132 self.count_unchecked()
1133 }
1134
1135 pub fn count_unchecked(&self) -> u64 {
1136 self.map.values().sum()
1137 }
1138}
1139
1140fn expect_create<FEN: FoundryEvmNetwork>(
1141 state: &mut Cheatcodes<FEN>,
1142 bytecode: Bytes,
1143 deployer: Address,
1144 create_scheme: CreateScheme,
1145) -> Result {
1146 let expected_create = ExpectedCreate { bytecode, deployer, create_scheme };
1147 state.expected_creates.push(expected_create);
1148
1149 Ok(Default::default())
1150}
1151
1152fn expect_revert<FEN: FoundryEvmNetwork>(
1153 state: &mut Cheatcodes<FEN>,
1154 reason: Option<&[u8]>,
1155 depth: usize,
1156 cheatcode: bool,
1157 partial_match: bool,
1158 reverter: Option<Address>,
1159 count: u64,
1160) -> Result {
1161 ensure!(
1162 state.expected_revert.is_none(),
1163 "you must call another function prior to expecting a second revert"
1164 );
1165 state.expected_revert = Some(ExpectedRevert {
1166 reason: reason.map(Bytes::copy_from_slice),
1167 depth,
1168 kind: if cheatcode {
1169 ExpectedRevertKind::Cheatcode { pending_processing: true }
1170 } else {
1171 ExpectedRevertKind::Default
1172 },
1173 partial_match,
1174 reverter,
1175 reverted_by: None,
1176 max_depth: depth,
1177 count,
1178 actual_count: 0,
1179 });
1180 Ok(Default::default())
1181}
1182
1183fn checks_topics_and_data(checks: [bool; 5], expected: &RawLog, log: &RawLog) -> bool {
1184 if log.topics().len() != expected.topics().len() {
1185 return false;
1186 }
1187
1188 if !log
1190 .topics()
1191 .iter()
1192 .enumerate()
1193 .filter(|(i, _)| checks[*i])
1194 .all(|(i, topic)| topic == &expected.topics()[i])
1195 {
1196 return false;
1197 }
1198
1199 if checks[4] && expected.data.as_ref() != log.data.as_ref() {
1201 return false;
1202 }
1203
1204 true
1205}
1206
1207fn decode_event(
1208 identifier: &foundry_evm_traces::identifier::SignaturesIdentifier,
1209 log: &RawLog,
1210) -> Option<DecodedCallLog> {
1211 let topics = log.topics();
1212 if topics.is_empty() {
1213 return None;
1214 }
1215 let t0 = topics[0]; let event = foundry_common::block_on(
1218 identifier.identify_event_with_indexed_count(t0, topics.len().saturating_sub(1)),
1219 )?;
1220
1221 let has_indexed_info = event.inputs.iter().any(|p| p.indexed);
1223 let indexed_event = if has_indexed_info { event } else { get_indexed_event(event, log) };
1225
1226 if let Ok(decoded) = indexed_event.decode_log(log) {
1228 let params = reconstruct_params(&indexed_event, &decoded);
1229
1230 let decoded_params = params
1231 .into_iter()
1232 .zip(indexed_event.inputs.iter())
1233 .map(|(param, input)| (input.name.clone(), format_token(¶m)))
1234 .collect();
1235
1236 return Some(DecodedCallLog {
1237 name: Some(indexed_event.name),
1238 params: Some(decoded_params),
1239 });
1240 }
1241
1242 None
1243}
1244
1245fn reconstruct_params(event: &Event, decoded: &alloy_dyn_abi::DecodedEvent) -> Vec<DynSolValue> {
1247 let mut indexed = 0;
1248 let mut unindexed = 0;
1249 let mut inputs = vec![];
1250 for input in &event.inputs {
1251 if input.indexed && indexed < decoded.indexed.len() {
1252 inputs.push(decoded.indexed[indexed].clone());
1253 indexed += 1;
1254 } else if unindexed < decoded.body.len() {
1255 inputs.push(decoded.body[unindexed].clone());
1256 unindexed += 1;
1257 }
1258 }
1259 inputs
1260}
1261
1262pub(crate) fn get_emit_mismatch_message(
1264 checks: [bool; 5],
1265 expected: &RawLog,
1266 actual: &RawLog,
1267 is_anonymous: bool,
1268 expected_decoded: Option<&DecodedCallLog>,
1269 actual_decoded: Option<&DecodedCallLog>,
1270) -> String {
1271 if actual.topics().len() != expected.topics().len() {
1275 let expected_name = expected_decoded.and_then(|d| d.name.as_deref()).unwrap_or("log");
1276 let actual_name = actual_decoded.and_then(|d| d.name.as_deref()).unwrap_or("log");
1277 let expected_topics = checked_topic_count(expected, is_anonymous);
1278 let actual_topics = checked_topic_count(actual, is_anonymous);
1279
1280 if expected_name == actual_name {
1281 return format!(
1282 "{actual_name} indexed topic count mismatch: expected {expected_topics}, got {actual_topics}"
1283 );
1284 }
1285
1286 return name_mismatched_logs(expected_decoded, actual_decoded);
1287 }
1288
1289 if !is_anonymous
1291 && checks[0]
1292 && (!expected.topics().is_empty() && !actual.topics().is_empty())
1293 && expected.topics()[0] != actual.topics()[0]
1294 {
1295 return name_mismatched_logs(expected_decoded, actual_decoded);
1296 }
1297
1298 let expected_data = expected.data.as_ref();
1299 let actual_data = actual.data.as_ref();
1300
1301 if checks[4] && expected_data != actual_data {
1303 if expected_data.len() != actual_data.len()
1305 || !expected_data.len().is_multiple_of(32)
1306 || expected_data.is_empty()
1307 {
1308 return name_mismatched_logs(expected_decoded, actual_decoded);
1309 }
1310 }
1311
1312 let mut mismatches = Vec::new();
1314
1315 for (i, (expected_topic, actual_topic)) in
1317 expected.topics().iter().zip(actual.topics().iter()).enumerate()
1318 {
1319 if i == 0 && !is_anonymous {
1321 continue;
1322 }
1323
1324 if i < checks.len() && checks[i] && expected_topic != actual_topic {
1326 let param_idx = if is_anonymous {
1327 i } else {
1329 i - 1 };
1331 mismatches
1332 .push(format!("param {param_idx}: expected={expected_topic}, got={actual_topic}"));
1333 }
1334 }
1335
1336 if checks[4] && expected_data != actual_data {
1338 let num_indexed_params = if is_anonymous {
1339 expected.topics().len()
1340 } else {
1341 expected.topics().len().saturating_sub(1)
1342 };
1343
1344 for (i, (expected_chunk, actual_chunk)) in
1345 expected_data.chunks(32).zip(actual_data.chunks(32)).enumerate()
1346 {
1347 if expected_chunk != actual_chunk {
1348 let param_idx = num_indexed_params + i;
1349 mismatches.push(format!(
1350 "param {}: expected={}, got={}",
1351 param_idx,
1352 hex::encode_prefixed(expected_chunk),
1353 hex::encode_prefixed(actual_chunk)
1354 ));
1355 }
1356 }
1357 }
1358
1359 if mismatches.is_empty() {
1360 name_mismatched_logs(expected_decoded, actual_decoded)
1361 } else {
1362 let event_prefix = match (expected_decoded, actual_decoded) {
1364 (Some(expected_dec), Some(actual_dec)) if expected_dec.name == actual_dec.name => {
1365 format!(
1366 "{} param mismatch",
1367 expected_dec.name.as_ref().unwrap_or(&"log".to_string())
1368 )
1369 }
1370 _ => {
1371 if is_anonymous {
1372 "anonymous log mismatch".to_string()
1373 } else {
1374 "log mismatch".to_string()
1375 }
1376 }
1377 };
1378
1379 let detailed_mismatches = if let (Some(expected_dec), Some(actual_dec)) =
1381 (expected_decoded, actual_decoded)
1382 && let (Some(expected_params), Some(actual_params)) =
1383 (&expected_dec.params, &actual_dec.params)
1384 {
1385 mismatches
1386 .into_iter()
1387 .map(|basic_mismatch| {
1388 if let Some(param_idx) = basic_mismatch
1390 .split(' ')
1391 .nth(1)
1392 .and_then(|s| s.trim_end_matches(':').parse::<usize>().ok())
1393 && param_idx < expected_params.len()
1394 && param_idx < actual_params.len()
1395 {
1396 let (expected_name, expected_value) = &expected_params[param_idx];
1397 let (_actual_name, actual_value) = &actual_params[param_idx];
1398 let param_name = if expected_name.is_empty() {
1399 &format!("param{param_idx}")
1400 } else {
1401 expected_name
1402 };
1403 return format!(
1404 "{param_name}: expected={expected_value}, got={actual_value}",
1405 );
1406 }
1407 basic_mismatch
1408 })
1409 .collect::<Vec<_>>()
1410 } else {
1411 mismatches
1412 };
1413
1414 format!("{} at {}", event_prefix, detailed_mismatches.join(", "))
1415 }
1416}
1417
1418fn name_mismatched_logs(
1420 expected_decoded: Option<&DecodedCallLog>,
1421 actual_decoded: Option<&DecodedCallLog>,
1422) -> String {
1423 let expected_name = expected_decoded.and_then(|d| d.name.as_deref()).unwrap_or("log");
1424 let actual_name = actual_decoded.and_then(|d| d.name.as_deref()).unwrap_or("log");
1425 format!("{actual_name} != expected {expected_name}")
1426}
1427
1428fn checked_topic_count(log: &RawLog, is_anonymous: bool) -> usize {
1429 if is_anonymous { log.topics().len() } else { log.topics().len().saturating_sub(1) }
1430}
1431
1432fn expect_safe_memory<FEN: FoundryEvmNetwork>(
1433 state: &mut Cheatcodes<FEN>,
1434 start: u64,
1435 end: u64,
1436 depth: u64,
1437) -> Result {
1438 ensure!(start < end, "memory range start ({start}) is greater than end ({end})");
1439 #[expect(clippy::single_range_in_vec_init)] let offsets = state.allowed_mem_writes.entry(depth).or_insert_with(|| vec![0..0x60]);
1441 offsets.push(start..end);
1442 Ok(Default::default())
1443}