1use std::fmt::{self, Display};
2
3use crate::{
4 Cheatcode, Cheatcodes, CheatcodesCore, CheatsCtxt, Error, Result,
5 Vm::*,
6 expected_emit::{ExpectedEmit, observe_log},
7};
8use alloy_primitives::{
9 Address, Bytes, Log, LogData as RawLog, U256, hex, keccak256,
10 map::{HashMap, hash_map::Entry},
11};
12use alloy_sol_types::{SolCall, SolValue};
13use foundry_evm_core::evm::FoundryEvmNetwork;
14use itertools::Itertools;
15use revm::interpreter::{
16 CallScheme, InstructionResult, Interpreter, InterpreterAction, interpreter_types::LoopControl,
17};
18use tempo_contracts::precompiles::ISignatureVerifier;
19use tempo_precompiles::SIGNATURE_VERIFIER_ADDRESS;
20
21use super::revert_handlers::RevertParameters;
22pub type ExpectedCallTracker = HashMap<Address, ExpectedCallsForTarget>;
33
34pub type ExpectedCallsForTarget =
36 HashMap<(Bytes, Option<ExpectedCallScheme>), (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
55impl Default for ExpectedCallData {
56 fn default() -> Self {
57 Self {
58 value: None,
59 gas: None,
60 min_gas: None,
61 count: 1,
62 call_type: ExpectedCallType::NonCount,
63 }
64 }
65}
66
67#[derive(Clone, Debug, PartialEq, Eq)]
69pub enum ExpectedCallType {
70 NonCount,
72 Count,
74}
75
76#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
78pub enum ExpectedCallScheme {
79 Call,
81 CallCode,
83 DelegateCall,
85 StaticCall,
87}
88
89impl From<CallScheme> for ExpectedCallScheme {
90 fn from(scheme: CallScheme) -> Self {
91 match scheme {
92 CallScheme::Call => Self::Call,
93 CallScheme::CallCode => Self::CallCode,
94 CallScheme::DelegateCall => Self::DelegateCall,
95 CallScheme::StaticCall => Self::StaticCall,
96 }
97 }
98}
99
100#[derive(Clone, Debug)]
102pub enum ExpectedRevertKind {
103 Default,
105 Cheatcode { pending_processing: bool },
111}
112
113#[derive(Clone, Debug)]
114pub struct ExpectedRevert {
115 pub reason: Option<Bytes>,
117 pub depth: usize,
119 pub kind: ExpectedRevertKind,
121 pub partial_match: bool,
123 pub reverter: Option<Address>,
125 pub reverted_by: Option<Address>,
127 pub max_depth: usize,
129 pub count: u64,
131 pub actual_count: u64,
133}
134
135#[derive(Clone, Debug)]
136pub struct ExpectedCreate {
137 pub deployer: Address,
139 pub bytecode: Bytes,
141 pub create_scheme: CreateScheme,
143}
144
145#[derive(Clone, Debug)]
146pub enum CreateScheme {
147 Create,
148 Create2,
149}
150
151impl Display for CreateScheme {
152 fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
153 match self {
154 Self::Create => write!(f, "CREATE"),
155 Self::Create2 => write!(f, "CREATE2"),
156 }
157 }
158}
159
160impl From<revm::context_interface::CreateScheme> for CreateScheme {
161 fn from(scheme: revm::context_interface::CreateScheme) -> Self {
162 match scheme {
163 revm::context_interface::CreateScheme::Create => Self::Create,
164 revm::context_interface::CreateScheme::Create2 { .. } => Self::Create2,
165 _ => unimplemented!("Unsupported create scheme"),
166 }
167 }
168}
169
170impl CreateScheme {
171 pub const fn eq(&self, create_scheme: Self) -> bool {
172 matches!(
173 (self, create_scheme),
174 (Self::Create, Self::Create) | (Self::Create2, Self::Create2 { .. })
175 )
176 }
177}
178
179impl Cheatcode for expectCall_0Call {
180 fn apply_context(&self, state: &mut CheatcodesCore) -> Result {
181 let Self { callee, data } = self;
182 expect_call(state, callee, data, None, ExpectedCallData::default())
183 }
184}
185
186impl Cheatcode for expectCall_1Call {
187 fn apply_context(&self, state: &mut CheatcodesCore) -> Result {
188 let Self { callee, data, count } = self;
189 expect_call(
190 state,
191 callee,
192 data,
193 None,
194 ExpectedCallData {
195 count: *count,
196 call_type: ExpectedCallType::Count,
197 ..Default::default()
198 },
199 )
200 }
201}
202
203impl Cheatcode for expectCall_2Call {
204 fn apply_context(&self, state: &mut CheatcodesCore) -> Result {
205 let Self { callee, msgValue, data } = self;
206 expect_call(
207 state,
208 callee,
209 data,
210 None,
211 ExpectedCallData { value: Some(*msgValue), ..Default::default() },
212 )
213 }
214}
215
216impl Cheatcode for expectCall_3Call {
217 fn apply_context(&self, state: &mut CheatcodesCore) -> Result {
218 let Self { callee, msgValue, data, count } = self;
219 expect_call(
220 state,
221 callee,
222 data,
223 None,
224 ExpectedCallData {
225 value: Some(*msgValue),
226 count: *count,
227 call_type: ExpectedCallType::Count,
228 ..Default::default()
229 },
230 )
231 }
232}
233
234impl Cheatcode for expectCall_4Call {
235 fn apply_context(&self, state: &mut CheatcodesCore) -> Result {
236 let Self { callee, msgValue, gas, data } = self;
237 expect_call(
238 state,
239 callee,
240 data,
241 None,
242 ExpectedCallData { value: Some(*msgValue), gas: Some(*gas), ..Default::default() },
243 )
244 }
245}
246
247impl Cheatcode for expectCall_5Call {
248 fn apply_context(&self, state: &mut CheatcodesCore) -> Result {
249 let Self { callee, msgValue, gas, data, count } = self;
250 expect_call(
251 state,
252 callee,
253 data,
254 None,
255 ExpectedCallData {
256 value: Some(*msgValue),
257 gas: Some(*gas),
258 count: *count,
259 call_type: ExpectedCallType::Count,
260 ..Default::default()
261 },
262 )
263 }
264}
265
266impl Cheatcode for expectDelegateCallCall {
267 fn apply_context(&self, state: &mut CheatcodesCore) -> Result {
268 let Self { callee, data } = self;
269 expect_call(
270 state,
271 callee,
272 data,
273 Some(ExpectedCallScheme::DelegateCall),
274 ExpectedCallData::default(),
275 )
276 }
277}
278
279impl Cheatcode for expectCallMinGas_0Call {
280 fn apply_context(&self, state: &mut CheatcodesCore) -> Result {
281 let Self { callee, msgValue, minGas, data } = self;
282 expect_call(
283 state,
284 callee,
285 data,
286 None,
287 ExpectedCallData {
288 value: Some(*msgValue),
289 min_gas: Some(*minGas),
290 ..Default::default()
291 },
292 )
293 }
294}
295
296impl Cheatcode for expectCallMinGas_1Call {
297 fn apply_context(&self, state: &mut CheatcodesCore) -> Result {
298 let Self { callee, msgValue, minGas, data, count } = self;
299 expect_call(
300 state,
301 callee,
302 data,
303 None,
304 ExpectedCallData {
305 value: Some(*msgValue),
306 min_gas: Some(*minGas),
307 count: *count,
308 call_type: ExpectedCallType::Count,
309 ..Default::default()
310 },
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.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.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.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.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.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.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.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.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.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.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.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.depth(), [true; 5], Some(emitter), true, 1)
438 }
439}
440
441impl Cheatcode for expectCreateCall {
442 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
443 let Self { bytecode, deployer } = self;
444 expect_create(ccx.state, bytecode.clone(), *deployer, CreateScheme::Create)
445 }
446}
447
448impl Cheatcode for expectCreate2Call {
449 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
450 let Self { bytecode, deployer } = self;
451 expect_create(ccx.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_context(&self, state: &mut CheatcodesCore) -> 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_context(&self, state: &mut CheatcodesCore) -> 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(
484 state: &mut CheatcodesCore,
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 ExpectedCallData::default(),
502 )
503}
504
505fn expect_logo_uri_updated<FEN: FoundryEvmNetwork>(
506 ccx: &mut CheatsCtxt<'_, '_, FEN>,
507 token: &Address,
508 updater: &Address,
509 new_logo_uri: &str,
510) -> Result {
511 let expected_emit = ExpectedEmit {
512 depth: ccx.depth(),
513 log: Some(RawLog::new_unchecked(
514 vec![keccak256("LogoURIUpdated(address,string)"), updater.into_word()],
515 new_logo_uri.abi_encode().into(),
516 )),
517 checks: [true, true, false, false, true],
518 address: Some(*token),
519 anonymous: false,
520 found: false,
521 count: 1,
522 mismatch_error: None,
523 };
524 ccx.state.expected_emits.push_back((expected_emit, Default::default()));
525 Ok(Default::default())
526}
527
528impl Cheatcode for expectRevert_0Call {
529 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
530 let Self {} = self;
531 expect_revert(ccx.state, None, ccx.depth(), false, false, None, 1)
532 }
533}
534
535impl Cheatcode for expectRevert_1Call {
536 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
537 let Self { revertData } = self;
538 expect_revert(ccx.state, Some(revertData.as_ref()), ccx.depth(), false, false, None, 1)
539 }
540}
541
542impl Cheatcode for expectRevert_2Call {
543 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
544 let Self { revertData } = self;
545 expect_revert(ccx.state, Some(revertData), ccx.depth(), false, false, None, 1)
546 }
547}
548
549impl Cheatcode for expectRevert_3Call {
550 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
551 let Self { reverter } = self;
552 expect_revert(ccx.state, None, ccx.depth(), false, false, Some(*reverter), 1)
553 }
554}
555
556impl Cheatcode for expectRevert_4Call {
557 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
558 let Self { revertData, reverter } = self;
559 expect_revert(
560 ccx.state,
561 Some(revertData.as_ref()),
562 ccx.depth(),
563 false,
564 false,
565 Some(*reverter),
566 1,
567 )
568 }
569}
570
571impl Cheatcode for expectRevert_5Call {
572 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
573 let Self { revertData, reverter } = self;
574 expect_revert(ccx.state, Some(revertData), ccx.depth(), false, false, Some(*reverter), 1)
575 }
576}
577
578impl Cheatcode for expectRevert_6Call {
579 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
580 let Self { count } = self;
581 expect_revert(ccx.state, None, ccx.depth(), false, false, None, *count)
582 }
583}
584
585impl Cheatcode for expectRevert_7Call {
586 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
587 let Self { revertData, count } = self;
588 expect_revert(ccx.state, Some(revertData.as_ref()), ccx.depth(), false, false, None, *count)
589 }
590}
591
592impl Cheatcode for expectRevert_8Call {
593 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
594 let Self { revertData, count } = self;
595 expect_revert(ccx.state, Some(revertData), ccx.depth(), false, false, None, *count)
596 }
597}
598
599impl Cheatcode for expectRevert_9Call {
600 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
601 let Self { reverter, count } = self;
602 expect_revert(ccx.state, None, ccx.depth(), false, false, Some(*reverter), *count)
603 }
604}
605
606impl Cheatcode for expectRevert_10Call {
607 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
608 let Self { revertData, reverter, count } = self;
609 expect_revert(
610 ccx.state,
611 Some(revertData.as_ref()),
612 ccx.depth(),
613 false,
614 false,
615 Some(*reverter),
616 *count,
617 )
618 }
619}
620
621impl Cheatcode for expectRevert_11Call {
622 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
623 let Self { revertData, reverter, count } = self;
624 expect_revert(
625 ccx.state,
626 Some(revertData),
627 ccx.depth(),
628 false,
629 false,
630 Some(*reverter),
631 *count,
632 )
633 }
634}
635
636impl Cheatcode for expectPartialRevert_0Call {
637 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
638 let Self { revertData } = self;
639 expect_revert(ccx.state, Some(revertData.as_ref()), ccx.depth(), false, true, None, 1)
640 }
641}
642
643impl Cheatcode for expectPartialRevert_1Call {
644 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
645 let Self { revertData, reverter } = self;
646 expect_revert(
647 ccx.state,
648 Some(revertData.as_ref()),
649 ccx.depth(),
650 false,
651 true,
652 Some(*reverter),
653 1,
654 )
655 }
656}
657
658impl Cheatcode for _expectCheatcodeRevert_0Call {
659 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
660 expect_revert(ccx.state, None, ccx.depth(), true, false, None, 1)
661 }
662}
663
664impl Cheatcode for _expectCheatcodeRevert_1Call {
665 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
666 let Self { revertData } = self;
667 expect_revert(ccx.state, Some(revertData.as_ref()), ccx.depth(), true, false, None, 1)
668 }
669}
670
671impl Cheatcode for _expectCheatcodeRevert_2Call {
672 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
673 let Self { revertData } = self;
674 expect_revert(ccx.state, Some(revertData), ccx.depth(), true, false, None, 1)
675 }
676}
677
678impl Cheatcode for expectSafeMemoryCall {
679 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
680 let Self { min, max } = *self;
681 expect_safe_memory(ccx.state, min, max, ccx.depth().try_into()?)
682 }
683}
684
685impl Cheatcode for stopExpectSafeMemoryCall {
686 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
687 let Self {} = self;
688 ccx.state.allowed_mem_writes.remove(&ccx.depth().try_into()?);
689 Ok(Default::default())
690 }
691}
692
693impl Cheatcode for expectSafeMemoryCallCall {
694 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
695 let Self { min, max } = *self;
696 expect_safe_memory(ccx.state, min, max, (ccx.depth() + 1).try_into()?)
697 }
698}
699
700impl RevertParameters for ExpectedRevert {
701 fn reverter(&self) -> Option<Address> {
702 self.reverter
703 }
704
705 fn reason(&self) -> Option<&[u8]> {
706 self.reason.as_ref().map(|b| &***b)
707 }
708
709 fn partial_match(&self) -> bool {
710 self.partial_match
711 }
712}
713
714impl ExpectedRevert {
715 pub(crate) const fn needs_processing(
722 &self,
723 cheatcode_call: bool,
724 call_failed: bool,
725 depth: usize,
726 internal_expect_revert: bool,
727 ) -> bool {
728 let went_deeper = self.max_depth > self.depth;
729 match self.kind {
730 ExpectedRevertKind::Default => {
731 if cheatcode_call {
733 return false;
734 }
735 if call_failed {
737 return true;
738 }
739 if !internal_expect_revert && went_deeper {
741 return true;
742 }
743 if depth == 0 {
745 return true;
746 }
747 !internal_expect_revert
750 }
751 ExpectedRevertKind::Cheatcode { pending_processing } => {
754 cheatcode_call && !pending_processing
755 }
756 }
757 }
758}
759
760fn expect_call(
764 state: &mut CheatcodesCore,
765 target: &Address,
766 calldata: &Bytes,
767 scheme: Option<ExpectedCallScheme>,
768 mut expected: ExpectedCallData,
769) -> Result {
770 let expecteds = state.expected_calls.entry(*target).or_default();
771
772 if let Some(value) = expected.value
773 && value > U256::ZERO
774 {
775 let positive_value_cost_stipend = 2300;
778 if let Some(gas) = &mut expected.gas {
779 *gas += positive_value_cost_stipend;
780 }
781 if let Some(min_gas) = &mut expected.min_gas {
782 *min_gas += positive_value_cost_stipend;
783 }
784 }
785
786 match expecteds.entry((calldata.clone(), scheme)) {
787 Entry::Occupied(mut entry) => {
788 ensure!(
789 expected.call_type == ExpectedCallType::NonCount,
790 "counted expected calls can only bet set once"
791 );
792 let (existing, _) = entry.get_mut();
793 ensure!(
794 existing.call_type == ExpectedCallType::NonCount,
795 "cannot overwrite a counted expectCall with a non-counted expectCall"
796 );
797 existing.count += 1;
798 }
799 Entry::Vacant(entry) => {
800 entry.insert((expected, 0));
801 }
802 }
803
804 Ok(Default::default())
805}
806
807pub(crate) fn observe_call(
809 expected_calls_for_target: &mut ExpectedCallsForTarget,
810 input: &[u8],
811 value: Option<U256>,
812 gas_limit: u64,
813 scheme: ExpectedCallScheme,
814) {
815 for ((calldata, expected_scheme), (expected, actual_count)) in expected_calls_for_target {
817 if calldata.len() <= input.len() &&
820 input.get(..calldata.len()) == Some(calldata.as_ref()) &&
822 expected.value.is_none_or(|expected_value| Some(expected_value) == value) &&
824 expected.gas.is_none_or(|gas| gas == gas_limit) &&
826 expected.min_gas.is_none_or(|min_gas| min_gas <= gas_limit) &&
828 expected_scheme.is_none_or(|expected_scheme| expected_scheme == scheme)
830 {
831 *actual_count += 1;
832 }
833 }
834}
835
836pub(crate) fn first_unmet_call(tracker: &ExpectedCallTracker, succeeded: bool) -> Option<String> {
841 let (address, calldata, scheme, expected, actual_count) = tracker
842 .iter()
843 .flat_map(|(address, calldatas)| {
844 calldatas.iter().map(move |((calldata, scheme), (expected, actual_count))| {
845 (address, calldata, scheme, expected, actual_count)
846 })
847 })
848 .filter(|(_, _, _, expected, actual_count)| match expected.call_type {
849 ExpectedCallType::Count => expected.count != **actual_count,
851 ExpectedCallType::NonCount => expected.count > **actual_count,
853 })
854 .min_by_key(|(address, calldata, scheme, ..)| {
855 (*address, *calldata, call_scheme_rank(**scheme))
856 })?;
857
858 let ExpectedCallData { gas, min_gas, value, count, .. } = expected;
859 let expected_values = [
860 Some(format!("data {}", hex::encode_prefixed(calldata))),
861 value.as_ref().map(|v| format!("value {v}")),
862 gas.map(|g| format!("gas {g}")),
863 min_gas.map(|g| format!("minimum gas {g}")),
864 scheme.map(|scheme| format!("call type {scheme:?}")),
865 ]
866 .into_iter()
867 .flatten()
868 .join(", ");
869 let but = if succeeded {
870 let s = if *actual_count == 1 { "" } else { "s" };
871 format!("was called {actual_count} time{s}")
872 } else {
873 "the call reverted instead; \
874 ensure you're testing the happy path when using `expectCall`"
875 .to_string()
876 };
877 let s = if *count == 1 { "" } else { "s" };
878 Some(format!(
879 "expected call to {address} with {expected_values} \
880 to be called {count} time{s}, but {but}"
881 ))
882}
883
884const fn call_scheme_rank(scheme: Option<ExpectedCallScheme>) -> u8 {
885 match scheme {
886 None => 0,
887 Some(ExpectedCallScheme::Call) => 1,
888 Some(ExpectedCallScheme::CallCode) => 2,
889 Some(ExpectedCallScheme::DelegateCall) => 3,
890 Some(ExpectedCallScheme::StaticCall) => 4,
891 }
892}
893
894fn expect_emit<FEN: FoundryEvmNetwork>(
895 state: &mut Cheatcodes<FEN>,
896 depth: usize,
897 checks: [bool; 5],
898 address: Option<Address>,
899 anonymous: bool,
900 count: u64,
901) -> Result {
902 let expected_emit = ExpectedEmit {
903 depth,
904 checks,
905 address,
906 found: false,
907 log: None,
908 anonymous,
909 count,
910 mismatch_error: None,
911 };
912 if let Some(found_emit_pos) = state.expected_emits.iter().position(|(emit, _)| emit.found) {
913 state.expected_emits.insert(found_emit_pos, (expected_emit, Default::default()));
916 } else {
917 state.expected_emits.push_back((expected_emit, Default::default()));
919 }
920
921 Ok(Default::default())
922}
923
924pub(crate) fn handle_expect_emit<FEN: FoundryEvmNetwork>(
929 state: &mut Cheatcodes<FEN>,
930 log: &Log,
931 interpreter: Option<&mut Interpreter>,
932) -> Option<&'static str> {
933 let reason = observe_log(&mut state.expected_emits, log)?;
934 let Some(interpreter) = interpreter else { return Some(reason) };
935 interpreter.bytecode.set_action(InterpreterAction::new_return(
936 InstructionResult::Revert,
937 Error::encode(reason),
938 interpreter.gas,
939 ));
940 None
941}
942
943fn expect_create<FEN: FoundryEvmNetwork>(
944 state: &mut Cheatcodes<FEN>,
945 bytecode: Bytes,
946 deployer: Address,
947 create_scheme: CreateScheme,
948) -> Result {
949 let expected_create = ExpectedCreate { bytecode, deployer, create_scheme };
950 state.expected_creates.push(expected_create);
951
952 Ok(Default::default())
953}
954
955fn expect_revert<FEN: FoundryEvmNetwork>(
956 state: &mut Cheatcodes<FEN>,
957 reason: Option<&[u8]>,
958 depth: usize,
959 cheatcode: bool,
960 partial_match: bool,
961 reverter: Option<Address>,
962 count: u64,
963) -> Result {
964 ensure!(
965 state.expected_revert.is_none(),
966 "you must call another function prior to expecting a second revert"
967 );
968 state.expected_revert = Some(ExpectedRevert {
969 reason: reason.map(Bytes::copy_from_slice),
970 depth,
971 kind: if cheatcode {
972 ExpectedRevertKind::Cheatcode { pending_processing: true }
973 } else {
974 ExpectedRevertKind::Default
975 },
976 partial_match,
977 reverter,
978 reverted_by: None,
979 max_depth: depth,
980 count,
981 actual_count: 0,
982 });
983 Ok(Default::default())
984}
985
986fn expect_safe_memory<FEN: FoundryEvmNetwork>(
987 state: &mut Cheatcodes<FEN>,
988 start: u64,
989 end: u64,
990 depth: u64,
991) -> Result {
992 ensure!(start < end, "memory range start ({start}) is greater than end ({end})");
993 #[expect(clippy::single_range_in_vec_init)] let offsets = state.allowed_mem_writes.entry(depth).or_insert_with(|| vec![0..0x60]);
995 offsets.push(start..end);
996 Ok(Default::default())
997}
998
999pub(crate) fn observe_create(
1003 expected_creates: &mut Vec<ExpectedCreate>,
1004 deployer: Address,
1005 create_scheme: impl Fn() -> CreateScheme,
1006 bytecode: &Bytes,
1007) {
1008 if let Some((index, _)) = expected_creates.iter().find_position(|expected_create| {
1009 expected_create.deployer == deployer
1010 && expected_create.create_scheme.eq(create_scheme())
1011 && expected_create.bytecode == *bytecode
1012 }) {
1013 expected_creates.swap_remove(index);
1014 }
1015}
1016
1017pub(crate) fn first_unmet_create(expected_creates: &[ExpectedCreate]) -> Option<String> {
1019 let expected_create = expected_creates.first()?;
1020 Some(format!(
1021 "expected {} call by address {} for bytecode {} but not found",
1022 expected_create.create_scheme,
1023 hex::encode_prefixed(expected_create.deployer),
1024 hex::encode_prefixed(&expected_create.bytecode),
1025 ))
1026}
1027
1028#[cfg(test)]
1029mod tests {
1030 use super::*;
1031 use alloy_primitives::{address, bytes};
1032
1033 const TARGET: Address = address!("0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f");
1034
1035 fn expected(
1036 value: Option<U256>,
1037 gas: Option<u64>,
1038 min_gas: Option<u64>,
1039 count: u64,
1040 call_type: ExpectedCallType,
1041 ) -> ExpectedCallData {
1042 ExpectedCallData { value, gas, min_gas, count, call_type }
1043 }
1044
1045 fn tracker(
1046 calldata: Bytes,
1047 scheme: Option<ExpectedCallScheme>,
1048 data: ExpectedCallData,
1049 ) -> ExpectedCallTracker {
1050 let mut tracker = ExpectedCallTracker::default();
1051 tracker.entry(TARGET).or_default().insert((calldata, scheme), (data, 0));
1052 tracker
1053 }
1054
1055 fn seen(
1056 tracker: &ExpectedCallTracker,
1057 calldata: &Bytes,
1058 scheme: Option<ExpectedCallScheme>,
1059 ) -> u64 {
1060 tracker[&TARGET][&(calldata.clone(), scheme)].1
1061 }
1062
1063 fn observe(tracker: &mut ExpectedCallTracker, input: &[u8], value: Option<U256>, gas: u64) {
1064 observe_call(
1065 tracker.get_mut(&TARGET).unwrap(),
1066 input,
1067 value,
1068 gas,
1069 ExpectedCallScheme::Call,
1070 );
1071 }
1072
1073 #[test]
1074 fn observe_call_matches_calldata_prefix() {
1075 let selector = bytes!("771602f7");
1076 let full = bytes!("771602f7aabb");
1077 let mut t = tracker(
1078 selector.clone(),
1079 None,
1080 expected(None, None, None, 1, ExpectedCallType::NonCount),
1081 );
1082 t.entry(TARGET).or_default().insert(
1083 (full.clone(), None),
1084 (expected(None, None, None, 1, ExpectedCallType::NonCount), 0),
1085 );
1086
1087 observe(&mut t, &full, None, 0);
1088 observe(&mut t, &selector, None, 0);
1089 observe(&mut t, &bytes!("12345678"), None, 0);
1090
1091 assert_eq!(seen(&t, &selector, None), 2);
1092 assert_eq!(seen(&t, &full, None), 1);
1093 }
1094
1095 #[test]
1096 fn observe_call_filters_value() {
1097 let calldata = bytes!("c290d691");
1098 let mut t = tracker(
1099 calldata.clone(),
1100 None,
1101 expected(Some(U256::from(1)), None, None, 1, ExpectedCallType::NonCount),
1102 );
1103
1104 observe(&mut t, &calldata, Some(U256::from(2)), 0);
1105 observe(&mut t, &calldata, None, 0);
1106 assert_eq!(seen(&t, &calldata, None), 0);
1107
1108 observe(&mut t, &calldata, Some(U256::from(1)), 0);
1109 assert_eq!(seen(&t, &calldata, None), 1);
1110
1111 let mut zero = tracker(
1113 calldata.clone(),
1114 None,
1115 expected(Some(U256::ZERO), None, None, 1, ExpectedCallType::NonCount),
1116 );
1117 observe(&mut zero, &calldata, None, 0);
1118 assert_eq!(seen(&zero, &calldata, None), 0);
1119 observe(&mut zero, &calldata, Some(U256::ZERO), 0);
1120 assert_eq!(seen(&zero, &calldata, None), 1);
1121
1122 let mut any = tracker(
1124 calldata.clone(),
1125 None,
1126 expected(None, None, None, 1, ExpectedCallType::NonCount),
1127 );
1128 observe(&mut any, &calldata, Some(U256::from(5)), 0);
1129 assert_eq!(seen(&any, &calldata, None), 1);
1130 }
1131
1132 #[test]
1133 fn observe_call_filters_gas_and_min_gas() {
1134 let calldata = bytes!("771602f7");
1135 let mut gas = tracker(
1136 calldata.clone(),
1137 None,
1138 expected(None, Some(25_000), None, 1, ExpectedCallType::NonCount),
1139 );
1140 observe(&mut gas, &calldata, None, 24_999);
1141 observe(&mut gas, &calldata, None, 25_001);
1142 assert_eq!(seen(&gas, &calldata, None), 0);
1143 observe(&mut gas, &calldata, None, 25_000);
1144 assert_eq!(seen(&gas, &calldata, None), 1);
1145
1146 let mut min_gas = tracker(
1147 calldata.clone(),
1148 None,
1149 expected(None, None, Some(50_000), 1, ExpectedCallType::NonCount),
1150 );
1151 observe(&mut min_gas, &calldata, None, 49_999);
1152 assert_eq!(seen(&min_gas, &calldata, None), 0);
1153 observe(&mut min_gas, &calldata, None, 50_000);
1154 observe(&mut min_gas, &calldata, None, 60_000);
1155 assert_eq!(seen(&min_gas, &calldata, None), 2);
1156 }
1157
1158 #[test]
1159 fn observe_call_filters_scheme() {
1160 let calldata = bytes!("771602f7");
1161 let mut t = tracker(
1162 calldata.clone(),
1163 Some(ExpectedCallScheme::DelegateCall),
1164 expected(None, None, None, 1, ExpectedCallType::NonCount),
1165 );
1166
1167 observe_call(t.get_mut(&TARGET).unwrap(), &calldata, None, 0, ExpectedCallScheme::Call);
1168 assert_eq!(seen(&t, &calldata, Some(ExpectedCallScheme::DelegateCall)), 0);
1169
1170 observe_call(
1171 t.get_mut(&TARGET).unwrap(),
1172 &calldata,
1173 None,
1174 0,
1175 ExpectedCallScheme::DelegateCall,
1176 );
1177 assert_eq!(seen(&t, &calldata, Some(ExpectedCallScheme::DelegateCall)), 1);
1178 }
1179
1180 #[test]
1181 fn first_unmet_call_compares_counts() {
1182 let calldata = bytes!("771602f7");
1183 let mut count =
1184 tracker(calldata.clone(), None, expected(None, None, None, 2, ExpectedCallType::Count));
1185 let mut non_count =
1186 tracker(calldata, None, expected(None, None, None, 2, ExpectedCallType::NonCount));
1187
1188 for seen in [1, 3] {
1189 count.get_mut(&TARGET).unwrap().values_mut().next().unwrap().1 = seen;
1190 assert!(first_unmet_call(&count, true).is_some(), "count with {seen} calls");
1191 }
1192 count.get_mut(&TARGET).unwrap().values_mut().next().unwrap().1 = 2;
1193 assert_eq!(first_unmet_call(&count, true), None);
1194
1195 non_count.get_mut(&TARGET).unwrap().values_mut().next().unwrap().1 = 1;
1196 assert!(first_unmet_call(&non_count, true).is_some());
1197 for seen in [2, 3] {
1198 non_count.get_mut(&TARGET).unwrap().values_mut().next().unwrap().1 = seen;
1199 assert_eq!(first_unmet_call(&non_count, true), None, "non-count with {seen} calls");
1200 }
1201 }
1202
1203 #[test]
1204 fn first_unmet_call_reports_smallest_unmet_key() {
1205 let low = address!("0000000000000000000000000000000000000001");
1206 let high = address!("0000000000000000000000000000000000000002");
1207 let ordered = [
1208 (low, bytes!("01"), None, ""),
1209 (low, bytes!("01"), Some(ExpectedCallScheme::Call), ", call type Call"),
1210 (low, bytes!("01"), Some(ExpectedCallScheme::CallCode), ", call type CallCode"),
1211 (low, bytes!("01"), Some(ExpectedCallScheme::DelegateCall), ", call type DelegateCall"),
1212 (low, bytes!("01"), Some(ExpectedCallScheme::StaticCall), ", call type StaticCall"),
1213 (low, bytes!("02"), None, ""),
1214 (high, bytes!("00"), None, ""),
1215 (high, bytes!("01"), None, ""),
1216 ];
1217
1218 for offset in 0..ordered.len() {
1219 let mut t = ExpectedCallTracker::default();
1220 t.entry(Address::ZERO).or_default().insert(
1222 (Bytes::new(), None),
1223 (expected(None, None, None, 1, ExpectedCallType::NonCount), 1),
1224 );
1225 for index in (0..ordered.len()).rev() {
1226 let (address, calldata, scheme, _) = &ordered[(index + offset) % ordered.len()];
1227 t.entry(*address).or_default().insert(
1228 (calldata.clone(), *scheme),
1229 (expected(None, None, None, 1, ExpectedCallType::NonCount), 0),
1230 );
1231 }
1232
1233 for (address, calldata, scheme, suffix) in &ordered {
1234 assert_eq!(
1235 first_unmet_call(&t, true).unwrap(),
1236 format!(
1237 "expected call to {address} with data {}{suffix} to be called 1 time, \
1238 but was called 0 times",
1239 hex::encode_prefixed(calldata),
1240 ),
1241 );
1242 t.get_mut(address).unwrap().remove(&(calldata.clone(), *scheme));
1243 }
1244 assert_eq!(first_unmet_call(&t, true), None);
1245 }
1246 }
1247
1248 #[test]
1249 fn first_unmet_call_message() {
1250 let calldata = bytes!("771602f7");
1251 let mut t = tracker(
1252 calldata.clone(),
1253 Some(ExpectedCallScheme::DelegateCall),
1254 expected(Some(U256::from(1)), Some(2), Some(3), 2, ExpectedCallType::Count),
1255 );
1256 t.get_mut(&TARGET).unwrap().values_mut().next().unwrap().1 = 1;
1257
1258 assert_eq!(
1259 first_unmet_call(&t, true).unwrap(),
1260 "expected call to 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f with data 0x771602f7, \
1261 value 1, gas 2, minimum gas 3, call type DelegateCall to be called 2 times, \
1262 but was called 1 time"
1263 );
1264 assert_eq!(
1265 first_unmet_call(&t, false).unwrap(),
1266 "expected call to 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f with data 0x771602f7, \
1267 value 1, gas 2, minimum gas 3, call type DelegateCall to be called 2 times, \
1268 but the call reverted instead; ensure you're testing the happy path when using \
1269 `expectCall`"
1270 );
1271
1272 let single =
1273 tracker(calldata, None, expected(None, None, None, 1, ExpectedCallType::NonCount));
1274 assert_eq!(
1275 first_unmet_call(&single, true).unwrap(),
1276 "expected call to 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f with data 0x771602f7 \
1277 to be called 1 time, but was called 0 times"
1278 );
1279 }
1280
1281 #[test]
1282 fn observe_create_converts_the_scheme_only_for_a_matching_deployer() {
1283 let bytecode = bytes!("6080");
1284 let mut expected_creates = vec![ExpectedCreate {
1285 deployer: Address::ZERO,
1286 bytecode: bytecode.clone(),
1287 create_scheme: CreateScheme::Create,
1288 }];
1289
1290 observe_create(&mut expected_creates, TARGET, || unreachable!(), &bytecode);
1291 assert_eq!(expected_creates.len(), 1);
1292
1293 observe_create(&mut expected_creates, Address::ZERO, || CreateScheme::Create, &bytecode);
1294 assert!(expected_creates.is_empty());
1295 }
1296
1297 #[test]
1298 fn internal_expect_revert_waits_for_failure_before_root() {
1299 let expected_revert = ExpectedRevert {
1300 reason: None,
1301 depth: 1,
1302 kind: ExpectedRevertKind::Default,
1303 partial_match: false,
1304 reverter: None,
1305 reverted_by: None,
1306 max_depth: 1,
1307 count: 1,
1308 actual_count: 0,
1309 };
1310
1311 assert!(expected_revert.needs_processing(false, true, 1, true));
1312 assert!(!expected_revert.needs_processing(false, false, 1, true));
1313 }
1314}