1use std::fmt::{self, Display};
2
3use crate::{
4 Cheatcode, Cheatcodes, 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 = HashMap<(Bytes, Option<CallScheme>), (ExpectedCallData, u64)>;
36
37#[derive(Clone, Debug)]
38pub struct ExpectedCallData {
39 pub value: Option<U256>,
41 pub gas: Option<u64>,
43 pub min_gas: Option<u64>,
45 pub count: u64,
50 pub call_type: ExpectedCallType,
52}
53
54#[derive(Clone, Debug, PartialEq, Eq)]
56pub enum ExpectedCallType {
57 NonCount,
59 Count,
61}
62
63#[derive(Clone, Debug)]
65pub enum ExpectedRevertKind {
66 Default,
68 Cheatcode { pending_processing: bool },
74}
75
76#[derive(Clone, Debug)]
77pub struct ExpectedRevert {
78 pub reason: Option<Bytes>,
80 pub depth: usize,
82 pub kind: ExpectedRevertKind,
84 pub partial_match: bool,
86 pub reverter: Option<Address>,
88 pub reverted_by: Option<Address>,
90 pub max_depth: usize,
92 pub count: u64,
94 pub actual_count: u64,
96}
97
98#[derive(Clone, Debug)]
99pub struct ExpectedCreate {
100 pub deployer: Address,
102 pub bytecode: Bytes,
104 pub create_scheme: CreateScheme,
106}
107
108#[derive(Clone, Debug)]
109pub enum CreateScheme {
110 Create,
111 Create2,
112}
113
114impl Display for CreateScheme {
115 fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
116 match self {
117 Self::Create => write!(f, "CREATE"),
118 Self::Create2 => write!(f, "CREATE2"),
119 }
120 }
121}
122
123impl From<revm::context_interface::CreateScheme> for CreateScheme {
124 fn from(scheme: revm::context_interface::CreateScheme) -> Self {
125 match scheme {
126 revm::context_interface::CreateScheme::Create => Self::Create,
127 revm::context_interface::CreateScheme::Create2 { .. } => Self::Create2,
128 _ => unimplemented!("Unsupported create scheme"),
129 }
130 }
131}
132
133impl CreateScheme {
134 pub const fn eq(&self, create_scheme: Self) -> bool {
135 matches!(
136 (self, create_scheme),
137 (Self::Create, Self::Create) | (Self::Create2, Self::Create2 { .. })
138 )
139 }
140}
141
142impl Cheatcode for expectCall_0Call {
143 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
144 let Self { callee, data } = self;
145 expect_call(state, callee, data, None, None, None, None, 1, ExpectedCallType::NonCount)
146 }
147}
148
149impl Cheatcode for expectCall_1Call {
150 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
151 let Self { callee, data, count } = self;
152 expect_call(state, callee, data, None, None, None, None, *count, ExpectedCallType::Count)
153 }
154}
155
156impl Cheatcode for expectCall_2Call {
157 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
158 let Self { callee, msgValue, data } = self;
159 expect_call(
160 state,
161 callee,
162 data,
163 Some(msgValue),
164 None,
165 None,
166 None,
167 1,
168 ExpectedCallType::NonCount,
169 )
170 }
171}
172
173impl Cheatcode for expectCall_3Call {
174 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
175 let Self { callee, msgValue, data, count } = self;
176 expect_call(
177 state,
178 callee,
179 data,
180 Some(msgValue),
181 None,
182 None,
183 None,
184 *count,
185 ExpectedCallType::Count,
186 )
187 }
188}
189
190impl Cheatcode for expectCall_4Call {
191 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
192 let Self { callee, msgValue, gas, data } = self;
193 expect_call(
194 state,
195 callee,
196 data,
197 Some(msgValue),
198 Some(*gas),
199 None,
200 None,
201 1,
202 ExpectedCallType::NonCount,
203 )
204 }
205}
206
207impl Cheatcode for expectCall_5Call {
208 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
209 let Self { callee, msgValue, gas, data, count } = self;
210 expect_call(
211 state,
212 callee,
213 data,
214 Some(msgValue),
215 Some(*gas),
216 None,
217 None,
218 *count,
219 ExpectedCallType::Count,
220 )
221 }
222}
223
224impl Cheatcode for expectDelegateCallCall {
225 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
226 let Self { callee, data } = self;
227 expect_call(
228 state,
229 callee,
230 data,
231 None,
232 None,
233 None,
234 Some(CallScheme::DelegateCall),
235 1,
236 ExpectedCallType::NonCount,
237 )
238 }
239}
240
241impl Cheatcode for expectCallMinGas_0Call {
242 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
243 let Self { callee, msgValue, minGas, data } = self;
244 expect_call(
245 state,
246 callee,
247 data,
248 Some(msgValue),
249 None,
250 Some(*minGas),
251 None,
252 1,
253 ExpectedCallType::NonCount,
254 )
255 }
256}
257
258impl Cheatcode for expectCallMinGas_1Call {
259 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
260 let Self { callee, msgValue, minGas, data, count } = self;
261 expect_call(
262 state,
263 callee,
264 data,
265 Some(msgValue),
266 None,
267 Some(*minGas),
268 None,
269 *count,
270 ExpectedCallType::Count,
271 )
272 }
273}
274
275impl Cheatcode for expectEmit_0Call {
276 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
277 let Self { checkTopic1, checkTopic2, checkTopic3, checkData } = *self;
278 expect_emit(
279 ccx.state,
280 ccx.depth(),
281 [true, checkTopic1, checkTopic2, checkTopic3, checkData],
282 None,
283 false,
284 1,
285 )
286 }
287}
288
289impl Cheatcode for expectEmit_1Call {
290 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
291 let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self;
292 expect_emit(
293 ccx.state,
294 ccx.depth(),
295 [true, checkTopic1, checkTopic2, checkTopic3, checkData],
296 Some(emitter),
297 false,
298 1,
299 )
300 }
301}
302
303impl Cheatcode for expectEmit_2Call {
304 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
305 let Self {} = self;
306 expect_emit(ccx.state, ccx.depth(), [true; 5], None, false, 1)
307 }
308}
309
310impl Cheatcode for expectEmit_3Call {
311 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
312 let Self { emitter } = *self;
313 expect_emit(ccx.state, ccx.depth(), [true; 5], Some(emitter), false, 1)
314 }
315}
316
317impl Cheatcode for expectEmit_4Call {
318 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
319 let Self { checkTopic1, checkTopic2, checkTopic3, checkData, count } = *self;
320 expect_emit(
321 ccx.state,
322 ccx.depth(),
323 [true, checkTopic1, checkTopic2, checkTopic3, checkData],
324 None,
325 false,
326 count,
327 )
328 }
329}
330
331impl Cheatcode for expectEmit_5Call {
332 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
333 let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter, count } = *self;
334 expect_emit(
335 ccx.state,
336 ccx.depth(),
337 [true, checkTopic1, checkTopic2, checkTopic3, checkData],
338 Some(emitter),
339 false,
340 count,
341 )
342 }
343}
344
345impl Cheatcode for expectEmit_6Call {
346 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
347 let Self { count } = *self;
348 expect_emit(ccx.state, ccx.depth(), [true; 5], None, false, count)
349 }
350}
351
352impl Cheatcode for expectEmit_7Call {
353 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
354 let Self { emitter, count } = *self;
355 expect_emit(ccx.state, ccx.depth(), [true; 5], Some(emitter), false, count)
356 }
357}
358
359impl Cheatcode for expectEmitAnonymous_0Call {
360 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
361 let Self { checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData } = *self;
362 expect_emit(
363 ccx.state,
364 ccx.depth(),
365 [checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData],
366 None,
367 true,
368 1,
369 )
370 }
371}
372
373impl Cheatcode for expectEmitAnonymous_1Call {
374 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
375 let Self { checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self;
376 expect_emit(
377 ccx.state,
378 ccx.depth(),
379 [checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData],
380 Some(emitter),
381 true,
382 1,
383 )
384 }
385}
386
387impl Cheatcode for expectEmitAnonymous_2Call {
388 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
389 let Self {} = self;
390 expect_emit(ccx.state, ccx.depth(), [true; 5], None, true, 1)
391 }
392}
393
394impl Cheatcode for expectEmitAnonymous_3Call {
395 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
396 let Self { emitter } = *self;
397 expect_emit(ccx.state, ccx.depth(), [true; 5], Some(emitter), true, 1)
398 }
399}
400
401impl Cheatcode for expectCreateCall {
402 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
403 let Self { bytecode, deployer } = self;
404 expect_create(state, bytecode.clone(), *deployer, CreateScheme::Create)
405 }
406}
407
408impl Cheatcode for expectCreate2Call {
409 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
410 let Self { bytecode, deployer } = self;
411 expect_create(state, bytecode.clone(), *deployer, CreateScheme::Create2)
412 }
413}
414
415impl Cheatcode for expectTip20LogoURIUpdatedCall {
416 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
417 let Self { token, updater, newLogoURI } = self;
418 expect_logo_uri_updated(ccx, token, updater, newLogoURI)
419 }
420}
421
422impl Cheatcode for expectKeychainVerifiedCall {
423 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
424 let Self { account, digest, signature } = self;
425 expect_keychain_verified(state, *account, *digest, signature.clone(), false)
426 }
427}
428
429impl Cheatcode for expectKeychainAdminVerifiedCall {
430 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
431 let Self { account, digest, signature } = self;
432 expect_keychain_verified(state, *account, *digest, signature.clone(), true)
433 }
434}
435
436impl Cheatcode for expectLogoURIUpdatedCall {
437 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
438 let Self { token, updater, newLogoURI } = self;
439 expect_logo_uri_updated(ccx, token, updater, newLogoURI)
440 }
441}
442
443fn expect_keychain_verified<FEN: FoundryEvmNetwork>(
444 state: &mut Cheatcodes<FEN>,
445 account: Address,
446 digest: alloy_primitives::B256,
447 signature: Bytes,
448 admin: bool,
449) -> Result {
450 let calldata = if admin {
451 ISignatureVerifier::verifyKeychainAdminCall { account, hash: digest, signature }
452 .abi_encode()
453 } else {
454 ISignatureVerifier::verifyKeychainCall { account, hash: digest, signature }.abi_encode()
455 };
456 expect_call(
457 state,
458 &SIGNATURE_VERIFIER_ADDRESS,
459 &Bytes::from(calldata),
460 None,
461 None,
462 None,
463 None,
464 1,
465 ExpectedCallType::NonCount,
466 )
467}
468
469fn expect_logo_uri_updated<FEN: FoundryEvmNetwork>(
470 ccx: &mut CheatsCtxt<'_, '_, FEN>,
471 token: &Address,
472 updater: &Address,
473 new_logo_uri: &str,
474) -> Result {
475 let expected_emit = ExpectedEmit {
476 depth: ccx.depth(),
477 log: Some(RawLog::new_unchecked(
478 vec![keccak256("LogoURIUpdated(address,string)"), updater.into_word()],
479 new_logo_uri.abi_encode().into(),
480 )),
481 checks: [true, true, false, false, true],
482 address: Some(*token),
483 anonymous: false,
484 found: false,
485 count: 1,
486 mismatch_error: None,
487 };
488 ccx.state.expected_emits.push_back((expected_emit, Default::default()));
489 Ok(Default::default())
490}
491
492impl Cheatcode for expectRevert_0Call {
493 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
494 let Self {} = self;
495 expect_revert(ccx.state, None, ccx.depth(), false, false, None, 1)
496 }
497}
498
499impl Cheatcode for expectRevert_1Call {
500 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
501 let Self { revertData } = self;
502 expect_revert(ccx.state, Some(revertData.as_ref()), ccx.depth(), false, false, None, 1)
503 }
504}
505
506impl Cheatcode for expectRevert_2Call {
507 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
508 let Self { revertData } = self;
509 expect_revert(ccx.state, Some(revertData), ccx.depth(), false, false, None, 1)
510 }
511}
512
513impl Cheatcode for expectRevert_3Call {
514 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
515 let Self { reverter } = self;
516 expect_revert(ccx.state, None, ccx.depth(), false, false, Some(*reverter), 1)
517 }
518}
519
520impl Cheatcode for expectRevert_4Call {
521 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
522 let Self { revertData, reverter } = self;
523 expect_revert(
524 ccx.state,
525 Some(revertData.as_ref()),
526 ccx.depth(),
527 false,
528 false,
529 Some(*reverter),
530 1,
531 )
532 }
533}
534
535impl Cheatcode for expectRevert_5Call {
536 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
537 let Self { revertData, reverter } = self;
538 expect_revert(ccx.state, Some(revertData), ccx.depth(), false, false, Some(*reverter), 1)
539 }
540}
541
542impl Cheatcode for expectRevert_6Call {
543 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
544 let Self { count } = self;
545 expect_revert(ccx.state, None, ccx.depth(), false, false, None, *count)
546 }
547}
548
549impl Cheatcode for expectRevert_7Call {
550 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
551 let Self { revertData, count } = self;
552 expect_revert(ccx.state, Some(revertData.as_ref()), ccx.depth(), false, false, None, *count)
553 }
554}
555
556impl Cheatcode for expectRevert_8Call {
557 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
558 let Self { revertData, count } = self;
559 expect_revert(ccx.state, Some(revertData), ccx.depth(), false, false, None, *count)
560 }
561}
562
563impl Cheatcode for expectRevert_9Call {
564 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
565 let Self { reverter, count } = self;
566 expect_revert(ccx.state, None, ccx.depth(), false, false, Some(*reverter), *count)
567 }
568}
569
570impl Cheatcode for expectRevert_10Call {
571 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
572 let Self { revertData, reverter, count } = self;
573 expect_revert(
574 ccx.state,
575 Some(revertData.as_ref()),
576 ccx.depth(),
577 false,
578 false,
579 Some(*reverter),
580 *count,
581 )
582 }
583}
584
585impl Cheatcode for expectRevert_11Call {
586 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
587 let Self { revertData, reverter, count } = self;
588 expect_revert(
589 ccx.state,
590 Some(revertData),
591 ccx.depth(),
592 false,
593 false,
594 Some(*reverter),
595 *count,
596 )
597 }
598}
599
600impl Cheatcode for expectPartialRevert_0Call {
601 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
602 let Self { revertData } = self;
603 expect_revert(ccx.state, Some(revertData.as_ref()), ccx.depth(), false, true, None, 1)
604 }
605}
606
607impl Cheatcode for expectPartialRevert_1Call {
608 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
609 let Self { revertData, reverter } = self;
610 expect_revert(
611 ccx.state,
612 Some(revertData.as_ref()),
613 ccx.depth(),
614 false,
615 true,
616 Some(*reverter),
617 1,
618 )
619 }
620}
621
622impl Cheatcode for _expectCheatcodeRevert_0Call {
623 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
624 expect_revert(ccx.state, None, ccx.depth(), true, false, None, 1)
625 }
626}
627
628impl Cheatcode for _expectCheatcodeRevert_1Call {
629 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
630 let Self { revertData } = self;
631 expect_revert(ccx.state, Some(revertData.as_ref()), ccx.depth(), true, false, None, 1)
632 }
633}
634
635impl Cheatcode for _expectCheatcodeRevert_2Call {
636 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
637 let Self { revertData } = self;
638 expect_revert(ccx.state, Some(revertData), ccx.depth(), true, false, None, 1)
639 }
640}
641
642impl Cheatcode for expectSafeMemoryCall {
643 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
644 let Self { min, max } = *self;
645 expect_safe_memory(ccx.state, min, max, ccx.depth().try_into()?)
646 }
647}
648
649impl Cheatcode for stopExpectSafeMemoryCall {
650 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
651 let Self {} = self;
652 ccx.state.allowed_mem_writes.remove(&ccx.depth().try_into()?);
653 Ok(Default::default())
654 }
655}
656
657impl Cheatcode for expectSafeMemoryCallCall {
658 fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
659 let Self { min, max } = *self;
660 expect_safe_memory(ccx.state, min, max, (ccx.depth() + 1).try_into()?)
661 }
662}
663
664impl RevertParameters for ExpectedRevert {
665 fn reverter(&self) -> Option<Address> {
666 self.reverter
667 }
668
669 fn reason(&self) -> Option<&[u8]> {
670 self.reason.as_ref().map(|b| &***b)
671 }
672
673 fn partial_match(&self) -> bool {
674 self.partial_match
675 }
676}
677
678impl ExpectedRevert {
679 pub(crate) const fn needs_processing(
686 &self,
687 cheatcode_call: bool,
688 call_failed: bool,
689 depth: usize,
690 internal_expect_revert: bool,
691 ) -> bool {
692 let went_deeper = self.max_depth > self.depth;
693 match self.kind {
694 ExpectedRevertKind::Default => {
695 if cheatcode_call {
697 return false;
698 }
699 if call_failed {
701 return true;
702 }
703 if !internal_expect_revert && went_deeper {
705 return true;
706 }
707 if depth == 0 {
709 return true;
710 }
711 !internal_expect_revert
714 }
715 ExpectedRevertKind::Cheatcode { pending_processing } => {
718 cheatcode_call && !pending_processing
719 }
720 }
721 }
722}
723
724#[expect(clippy::too_many_arguments)] fn expect_call<FEN: FoundryEvmNetwork>(
742 state: &mut Cheatcodes<FEN>,
743 target: &Address,
744 calldata: &Bytes,
745 value: Option<&U256>,
746 mut gas: Option<u64>,
747 mut min_gas: Option<u64>,
748 scheme: Option<CallScheme>,
749 count: u64,
750 call_type: ExpectedCallType,
751) -> Result {
752 let expecteds = state.expected_calls.entry(*target).or_default();
753
754 if let Some(val) = value
755 && *val > U256::ZERO
756 {
757 let positive_value_cost_stipend = 2300;
760 if let Some(gas) = &mut gas {
761 *gas += positive_value_cost_stipend;
762 }
763 if let Some(min_gas) = &mut min_gas {
764 *min_gas += positive_value_cost_stipend;
765 }
766 }
767
768 match call_type {
769 ExpectedCallType::Count => {
770 let key = (calldata.clone(), scheme);
774 ensure!(!expecteds.contains_key(&key), "counted expected calls can only bet set once");
775 expecteds.insert(
776 key,
777 (ExpectedCallData { value: value.copied(), gas, min_gas, count, call_type }, 0),
778 );
779 }
780 ExpectedCallType::NonCount => {
781 match expecteds.entry((calldata.clone(), scheme)) {
784 Entry::Occupied(mut entry) => {
785 let (expected, _) = entry.get_mut();
786 ensure!(
788 expected.call_type == ExpectedCallType::NonCount,
789 "cannot overwrite a counted expectCall with a non-counted expectCall"
790 );
791 expected.count += 1;
792 }
793 Entry::Vacant(entry) => {
795 entry.insert((
796 ExpectedCallData { value: value.copied(), gas, min_gas, count, call_type },
797 0,
798 ));
799 }
800 }
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: CallScheme,
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<CallScheme>) -> u8 {
885 match scheme {
886 None => 0,
887 Some(CallScheme::Call) => 1,
888 Some(CallScheme::CallCode) => 2,
889 Some(CallScheme::DelegateCall) => 3,
890 Some(CallScheme::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<CallScheme>,
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(tracker: &ExpectedCallTracker, calldata: &Bytes, scheme: Option<CallScheme>) -> u64 {
1056 tracker[&TARGET][&(calldata.clone(), scheme)].1
1057 }
1058
1059 fn observe(tracker: &mut ExpectedCallTracker, input: &[u8], value: Option<U256>, gas: u64) {
1060 observe_call(tracker.get_mut(&TARGET).unwrap(), input, value, gas, CallScheme::Call);
1061 }
1062
1063 #[test]
1064 fn observe_call_matches_calldata_prefix() {
1065 let selector = bytes!("771602f7");
1066 let full = bytes!("771602f7aabb");
1067 let mut t = tracker(
1068 selector.clone(),
1069 None,
1070 expected(None, None, None, 1, ExpectedCallType::NonCount),
1071 );
1072 t.entry(TARGET).or_default().insert(
1073 (full.clone(), None),
1074 (expected(None, None, None, 1, ExpectedCallType::NonCount), 0),
1075 );
1076
1077 observe(&mut t, &full, None, 0);
1078 observe(&mut t, &selector, None, 0);
1079 observe(&mut t, &bytes!("12345678"), None, 0);
1080
1081 assert_eq!(seen(&t, &selector, None), 2);
1082 assert_eq!(seen(&t, &full, None), 1);
1083 }
1084
1085 #[test]
1086 fn observe_call_filters_value() {
1087 let calldata = bytes!("c290d691");
1088 let mut t = tracker(
1089 calldata.clone(),
1090 None,
1091 expected(Some(U256::from(1)), None, None, 1, ExpectedCallType::NonCount),
1092 );
1093
1094 observe(&mut t, &calldata, Some(U256::from(2)), 0);
1095 observe(&mut t, &calldata, None, 0);
1096 assert_eq!(seen(&t, &calldata, None), 0);
1097
1098 observe(&mut t, &calldata, Some(U256::from(1)), 0);
1099 assert_eq!(seen(&t, &calldata, None), 1);
1100
1101 let mut zero = tracker(
1103 calldata.clone(),
1104 None,
1105 expected(Some(U256::ZERO), None, None, 1, ExpectedCallType::NonCount),
1106 );
1107 observe(&mut zero, &calldata, None, 0);
1108 assert_eq!(seen(&zero, &calldata, None), 0);
1109 observe(&mut zero, &calldata, Some(U256::ZERO), 0);
1110 assert_eq!(seen(&zero, &calldata, None), 1);
1111
1112 let mut any = tracker(
1114 calldata.clone(),
1115 None,
1116 expected(None, None, None, 1, ExpectedCallType::NonCount),
1117 );
1118 observe(&mut any, &calldata, Some(U256::from(5)), 0);
1119 assert_eq!(seen(&any, &calldata, None), 1);
1120 }
1121
1122 #[test]
1123 fn observe_call_filters_gas_and_min_gas() {
1124 let calldata = bytes!("771602f7");
1125 let mut gas = tracker(
1126 calldata.clone(),
1127 None,
1128 expected(None, Some(25_000), None, 1, ExpectedCallType::NonCount),
1129 );
1130 observe(&mut gas, &calldata, None, 24_999);
1131 observe(&mut gas, &calldata, None, 25_001);
1132 assert_eq!(seen(&gas, &calldata, None), 0);
1133 observe(&mut gas, &calldata, None, 25_000);
1134 assert_eq!(seen(&gas, &calldata, None), 1);
1135
1136 let mut min_gas = tracker(
1137 calldata.clone(),
1138 None,
1139 expected(None, None, Some(50_000), 1, ExpectedCallType::NonCount),
1140 );
1141 observe(&mut min_gas, &calldata, None, 49_999);
1142 assert_eq!(seen(&min_gas, &calldata, None), 0);
1143 observe(&mut min_gas, &calldata, None, 50_000);
1144 observe(&mut min_gas, &calldata, None, 60_000);
1145 assert_eq!(seen(&min_gas, &calldata, None), 2);
1146 }
1147
1148 #[test]
1149 fn observe_call_filters_scheme() {
1150 let calldata = bytes!("771602f7");
1151 let mut t = tracker(
1152 calldata.clone(),
1153 Some(CallScheme::DelegateCall),
1154 expected(None, None, None, 1, ExpectedCallType::NonCount),
1155 );
1156
1157 observe_call(t.get_mut(&TARGET).unwrap(), &calldata, None, 0, CallScheme::Call);
1158 assert_eq!(seen(&t, &calldata, Some(CallScheme::DelegateCall)), 0);
1159
1160 observe_call(t.get_mut(&TARGET).unwrap(), &calldata, None, 0, CallScheme::DelegateCall);
1161 assert_eq!(seen(&t, &calldata, Some(CallScheme::DelegateCall)), 1);
1162 }
1163
1164 #[test]
1165 fn first_unmet_call_compares_counts() {
1166 let calldata = bytes!("771602f7");
1167 let mut count =
1168 tracker(calldata.clone(), None, expected(None, None, None, 2, ExpectedCallType::Count));
1169 let mut non_count =
1170 tracker(calldata, None, expected(None, None, None, 2, ExpectedCallType::NonCount));
1171
1172 for seen in [1, 3] {
1173 count.get_mut(&TARGET).unwrap().values_mut().next().unwrap().1 = seen;
1174 assert!(first_unmet_call(&count, true).is_some(), "count with {seen} calls");
1175 }
1176 count.get_mut(&TARGET).unwrap().values_mut().next().unwrap().1 = 2;
1177 assert_eq!(first_unmet_call(&count, true), None);
1178
1179 non_count.get_mut(&TARGET).unwrap().values_mut().next().unwrap().1 = 1;
1180 assert!(first_unmet_call(&non_count, true).is_some());
1181 for seen in [2, 3] {
1182 non_count.get_mut(&TARGET).unwrap().values_mut().next().unwrap().1 = seen;
1183 assert_eq!(first_unmet_call(&non_count, true), None, "non-count with {seen} calls");
1184 }
1185 }
1186
1187 #[test]
1188 fn first_unmet_call_reports_smallest_unmet_key() {
1189 let low = address!("0000000000000000000000000000000000000001");
1190 let high = address!("0000000000000000000000000000000000000002");
1191 let ordered = [
1192 (low, bytes!("01"), None, ""),
1193 (low, bytes!("01"), Some(CallScheme::Call), ", call type Call"),
1194 (low, bytes!("01"), Some(CallScheme::CallCode), ", call type CallCode"),
1195 (low, bytes!("01"), Some(CallScheme::DelegateCall), ", call type DelegateCall"),
1196 (low, bytes!("01"), Some(CallScheme::StaticCall), ", call type StaticCall"),
1197 (low, bytes!("02"), None, ""),
1198 (high, bytes!("00"), None, ""),
1199 (high, bytes!("01"), None, ""),
1200 ];
1201
1202 for offset in 0..ordered.len() {
1203 let mut t = ExpectedCallTracker::default();
1204 t.entry(Address::ZERO).or_default().insert(
1206 (Bytes::new(), None),
1207 (expected(None, None, None, 1, ExpectedCallType::NonCount), 1),
1208 );
1209 for index in (0..ordered.len()).rev() {
1210 let (address, calldata, scheme, _) = &ordered[(index + offset) % ordered.len()];
1211 t.entry(*address).or_default().insert(
1212 (calldata.clone(), *scheme),
1213 (expected(None, None, None, 1, ExpectedCallType::NonCount), 0),
1214 );
1215 }
1216
1217 for (address, calldata, scheme, suffix) in &ordered {
1218 assert_eq!(
1219 first_unmet_call(&t, true).unwrap(),
1220 format!(
1221 "expected call to {address} with data {}{suffix} to be called 1 time, \
1222 but was called 0 times",
1223 hex::encode_prefixed(calldata),
1224 ),
1225 );
1226 t.get_mut(address).unwrap().remove(&(calldata.clone(), *scheme));
1227 }
1228 assert_eq!(first_unmet_call(&t, true), None);
1229 }
1230 }
1231
1232 #[test]
1233 fn first_unmet_call_message() {
1234 let calldata = bytes!("771602f7");
1235 let mut t = tracker(
1236 calldata.clone(),
1237 Some(CallScheme::DelegateCall),
1238 expected(Some(U256::from(1)), Some(2), Some(3), 2, ExpectedCallType::Count),
1239 );
1240 t.get_mut(&TARGET).unwrap().values_mut().next().unwrap().1 = 1;
1241
1242 assert_eq!(
1243 first_unmet_call(&t, true).unwrap(),
1244 "expected call to 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f with data 0x771602f7, \
1245 value 1, gas 2, minimum gas 3, call type DelegateCall to be called 2 times, \
1246 but was called 1 time"
1247 );
1248 assert_eq!(
1249 first_unmet_call(&t, false).unwrap(),
1250 "expected call to 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f with data 0x771602f7, \
1251 value 1, gas 2, minimum gas 3, call type DelegateCall to be called 2 times, \
1252 but the call reverted instead; ensure you're testing the happy path when using \
1253 `expectCall`"
1254 );
1255
1256 let single =
1257 tracker(calldata, None, expected(None, None, None, 1, ExpectedCallType::NonCount));
1258 assert_eq!(
1259 first_unmet_call(&single, true).unwrap(),
1260 "expected call to 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f with data 0x771602f7 \
1261 to be called 1 time, but was called 0 times"
1262 );
1263 }
1264
1265 #[test]
1266 fn observe_create_converts_the_scheme_only_for_a_matching_deployer() {
1267 let bytecode = bytes!("6080");
1268 let mut expected_creates = vec![ExpectedCreate {
1269 deployer: Address::ZERO,
1270 bytecode: bytecode.clone(),
1271 create_scheme: CreateScheme::Create,
1272 }];
1273
1274 observe_create(&mut expected_creates, TARGET, || unreachable!(), &bytecode);
1275 assert_eq!(expected_creates.len(), 1);
1276
1277 observe_create(&mut expected_creates, Address::ZERO, || CreateScheme::Create, &bytecode);
1278 assert!(expected_creates.is_empty());
1279 }
1280
1281 #[test]
1282 fn internal_expect_revert_waits_for_failure_before_root() {
1283 let expected_revert = ExpectedRevert {
1284 reason: None,
1285 depth: 1,
1286 kind: ExpectedRevertKind::Default,
1287 partial_match: false,
1288 reverter: None,
1289 reverted_by: None,
1290 max_depth: 1,
1291 count: 1,
1292 actual_count: 0,
1293 };
1294
1295 assert!(expected_revert.needs_processing(false, true, 1, true));
1296 assert!(!expected_revert.needs_processing(false, false, 1, true));
1297 }
1298}