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