1use crate::inspectors::{
10 Cheatcodes, InspectorData, InspectorStack, cheatcodes::BroadcastableTransactions,
11};
12use alloy_dyn_abi::{DynSolValue, FunctionExt, JsonAbiExt};
13use alloy_json_abi::Function;
14use alloy_primitives::{
15 Address, Bytes, Log, TxKind, U256, keccak256,
16 map::{AddressHashMap, HashMap},
17};
18use alloy_sol_types::{SolCall, sol};
19use foundry_evm_core::{
20 EvmEnv,
21 backend::{Backend, BackendError, BackendResult, CowBackend, DatabaseExt, GLOBAL_FAIL_SLOT},
22 constants::{
23 CALLER, CHEATCODE_ADDRESS, CHEATCODE_CONTRACT_HASH, DEFAULT_CREATE2_DEPLOYER,
24 DEFAULT_CREATE2_DEPLOYER_CODE, DEFAULT_CREATE2_DEPLOYER_DEPLOYER,
25 },
26 decode::{RevertDecoder, SkipReason},
27 utils::StateChangeset,
28};
29use foundry_evm_coverage::HitMaps;
30use foundry_evm_traces::{SparsedTraceArena, TraceMode};
31use revm::{
32 bytecode::Bytecode,
33 context::{BlockEnv, TxEnv},
34 context_interface::{
35 result::{ExecutionResult, Output, ResultAndState},
36 transaction::SignedAuthorization,
37 },
38 database::{DatabaseCommit, DatabaseRef},
39 interpreter::{InstructionResult, return_ok},
40 primitives::hardfork::SpecId,
41};
42use std::{
43 borrow::Cow,
44 sync::{
45 Arc,
46 atomic::{AtomicBool, Ordering},
47 },
48 time::{Duration, Instant},
49};
50
51mod builder;
52pub use builder::ExecutorBuilder;
53
54pub mod fuzz;
55pub use fuzz::FuzzedExecutor;
56
57pub mod invariant;
58pub use invariant::InvariantExecutor;
59
60mod corpus;
61mod trace;
62
63pub use trace::TracingExecutor;
64
65const DURATION_BETWEEN_METRICS_REPORT: Duration = Duration::from_secs(5);
66
67sol! {
68 interface ITest {
69 function setUp() external;
70 function failed() external view returns (bool failed);
71
72 #[derive(Default)]
73 function beforeTestSetup(bytes4 testSelector) public view returns (bytes[] memory beforeTestCalldata);
74 }
75}
76
77#[derive(Clone, Debug)]
89pub struct Executor {
90 backend: Arc<Backend>,
99 evm_env: EvmEnv,
101 tx_env: TxEnv,
103 inspector: InspectorStack,
105 gas_limit: u64,
107 legacy_assertions: bool,
109}
110
111impl Executor {
112 #[inline]
114 pub fn new(
115 mut backend: Backend,
116 evm_env: EvmEnv,
117 tx_env: TxEnv,
118 inspector: InspectorStack,
119 gas_limit: u64,
120 legacy_assertions: bool,
121 ) -> Self {
122 backend.insert_account_info(
125 CHEATCODE_ADDRESS,
126 revm::state::AccountInfo {
127 code: Some(Bytecode::new_raw(Bytes::from_static(&[0]))),
128 code_hash: CHEATCODE_CONTRACT_HASH,
131 ..Default::default()
132 },
133 );
134
135 Self {
136 backend: Arc::new(backend),
137 evm_env,
138 tx_env,
139 inspector,
140 gas_limit,
141 legacy_assertions,
142 }
143 }
144
145 fn clone_with_backend(&self, backend: Backend) -> Self {
146 let mut evm_env = self.evm_env.clone();
147 evm_env.cfg_env.spec = self.spec_id();
148 Self {
149 backend: Arc::new(backend),
150 evm_env,
151 tx_env: self.tx_env.clone(),
152 inspector: self.inspector().clone(),
153 gas_limit: self.gas_limit,
154 legacy_assertions: self.legacy_assertions,
155 }
156 }
157
158 pub fn backend(&self) -> &Backend {
160 &self.backend
161 }
162
163 pub fn backend_mut(&mut self) -> &mut Backend {
168 Arc::make_mut(&mut self.backend)
169 }
170
171 pub fn evm_env(&self) -> &EvmEnv {
173 &self.evm_env
174 }
175
176 pub fn evm_env_mut(&mut self) -> &mut EvmEnv {
178 &mut self.evm_env
179 }
180
181 pub fn tx_env(&self) -> &TxEnv {
183 &self.tx_env
184 }
185
186 pub fn tx_env_mut(&mut self) -> &mut TxEnv {
188 &mut self.tx_env
189 }
190
191 pub fn inspector(&self) -> &InspectorStack {
193 &self.inspector
194 }
195
196 pub fn inspector_mut(&mut self) -> &mut InspectorStack {
198 &mut self.inspector
199 }
200
201 pub fn spec_id(&self) -> SpecId {
203 self.evm_env.cfg_env.spec
204 }
205
206 pub fn set_spec_id(&mut self, spec_id: SpecId) {
208 self.evm_env.cfg_env.spec = spec_id;
209 }
210
211 pub fn gas_limit(&self) -> u64 {
216 self.gas_limit
217 }
218
219 pub fn set_gas_limit(&mut self, gas_limit: u64) {
221 self.gas_limit = gas_limit;
222 }
223
224 pub fn legacy_assertions(&self) -> bool {
227 self.legacy_assertions
228 }
229
230 pub fn set_legacy_assertions(&mut self, legacy_assertions: bool) {
233 self.legacy_assertions = legacy_assertions;
234 }
235
236 pub fn deploy_create2_deployer(&mut self) -> eyre::Result<()> {
238 trace!("deploying local create2 deployer");
239 let create2_deployer_account = self
240 .backend()
241 .basic_ref(DEFAULT_CREATE2_DEPLOYER)?
242 .ok_or_else(|| BackendError::MissingAccount(DEFAULT_CREATE2_DEPLOYER))?;
243
244 if create2_deployer_account.code.is_none_or(|code| code.is_empty()) {
246 let creator = DEFAULT_CREATE2_DEPLOYER_DEPLOYER;
247
248 let initial_balance = self.get_balance(creator)?;
250 self.set_balance(creator, U256::MAX)?;
251
252 let res =
253 self.deploy(creator, DEFAULT_CREATE2_DEPLOYER_CODE.into(), U256::ZERO, None)?;
254 trace!(create2=?res.address, "deployed local create2 deployer");
255
256 self.set_balance(creator, initial_balance)?;
257 }
258 Ok(())
259 }
260
261 pub fn set_balance(&mut self, address: Address, amount: U256) -> BackendResult<()> {
263 trace!(?address, ?amount, "setting account balance");
264 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
265 account.balance = amount;
266 self.backend_mut().insert_account_info(address, account);
267 Ok(())
268 }
269
270 pub fn get_balance(&self, address: Address) -> BackendResult<U256> {
272 Ok(self.backend().basic_ref(address)?.map(|acc| acc.balance).unwrap_or_default())
273 }
274
275 pub fn set_nonce(&mut self, address: Address, nonce: u64) -> BackendResult<()> {
277 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
278 account.nonce = nonce;
279 self.backend_mut().insert_account_info(address, account);
280 self.tx_env_mut().nonce = nonce;
281 Ok(())
282 }
283
284 pub fn get_nonce(&self, address: Address) -> BackendResult<u64> {
286 Ok(self.backend().basic_ref(address)?.map(|acc| acc.nonce).unwrap_or_default())
287 }
288
289 pub fn set_code(&mut self, address: Address, code: Bytecode) -> BackendResult<()> {
291 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
292 account.code_hash = keccak256(code.original_byte_slice());
293 account.code = Some(code);
294 self.backend_mut().insert_account_info(address, account);
295 Ok(())
296 }
297
298 pub fn set_storage(
300 &mut self,
301 address: Address,
302 storage: HashMap<U256, U256>,
303 ) -> BackendResult<()> {
304 self.backend_mut().replace_account_storage(address, storage)?;
305 Ok(())
306 }
307
308 pub fn set_storage_slot(
310 &mut self,
311 address: Address,
312 slot: U256,
313 value: U256,
314 ) -> BackendResult<()> {
315 self.backend_mut().insert_account_storage(address, slot, value)?;
316 Ok(())
317 }
318
319 pub fn is_empty_code(&self, address: Address) -> BackendResult<bool> {
321 Ok(self.backend().basic_ref(address)?.map(|acc| acc.is_empty_code_hash()).unwrap_or(true))
322 }
323
324 #[inline]
325 pub fn set_tracing(&mut self, mode: TraceMode) -> &mut Self {
326 self.inspector_mut().tracing(mode);
327 self
328 }
329
330 #[inline]
331 pub fn set_script_execution(&mut self, script_address: Address) {
332 self.inspector_mut().script(script_address);
333 }
334
335 #[inline]
336 pub fn set_trace_printer(&mut self, trace_printer: bool) -> &mut Self {
337 self.inspector_mut().print(trace_printer);
338 self
339 }
340
341 #[inline]
342 pub fn create2_deployer(&self) -> Address {
343 self.inspector().create2_deployer
344 }
345
346 pub fn deploy(
351 &mut self,
352 from: Address,
353 code: Bytes,
354 value: U256,
355 rd: Option<&RevertDecoder>,
356 ) -> Result<DeployResult, EvmError> {
357 let (evm_env, tx_env) = self.build_test_env(from, TxKind::Create, code, value);
358 self.deploy_with_env(evm_env, tx_env, rd)
359 }
360
361 #[instrument(name = "deploy", level = "debug", skip_all)]
368 pub fn deploy_with_env(
369 &mut self,
370 evm_env: EvmEnv,
371 tx_env: TxEnv,
372 rd: Option<&RevertDecoder>,
373 ) -> Result<DeployResult, EvmError> {
374 assert!(
375 matches!(tx_env.kind, TxKind::Create),
376 "Expected create transaction, got {:?}",
377 tx_env.kind
378 );
379 trace!(sender=%tx_env.caller, "deploying contract");
380
381 let mut result = self.transact_with_env(evm_env, tx_env)?;
382 result = result.into_result(rd)?;
383 let Some(Output::Create(_, Some(address))) = result.out else {
384 panic!("Deployment succeeded, but no address was returned: {result:#?}");
385 };
386
387 self.backend_mut().add_persistent_account(address);
390
391 trace!(%address, "deployed contract");
392
393 Ok(DeployResult { raw: result, address })
394 }
395
396 #[instrument(name = "setup", level = "debug", skip_all)]
403 pub fn setup(
404 &mut self,
405 from: Option<Address>,
406 to: Address,
407 rd: Option<&RevertDecoder>,
408 ) -> Result<RawCallResult, EvmError> {
409 trace!(?from, ?to, "setting up contract");
410
411 let from = from.unwrap_or(CALLER);
412 self.backend_mut().set_test_contract(to).set_caller(from);
413 let calldata = Bytes::from_static(&ITest::setUpCall::SELECTOR);
414 let mut res = self.transact_raw(from, to, calldata, U256::ZERO)?;
415 res = res.into_result(rd)?;
416
417 self.evm_env_mut().block_env = res.evm_env.block_env.clone();
419 self.evm_env_mut().cfg_env.chain_id = res.evm_env.cfg_env.chain_id;
421
422 let success =
423 self.is_raw_call_success(to, Cow::Borrowed(&res.state_changeset), &res, false);
424 if !success {
425 return Err(res.into_execution_error("execution error".to_string()).into());
426 }
427
428 Ok(res)
429 }
430
431 pub fn call(
433 &self,
434 from: Address,
435 to: Address,
436 func: &Function,
437 args: &[DynSolValue],
438 value: U256,
439 rd: Option<&RevertDecoder>,
440 ) -> Result<CallResult, EvmError> {
441 let calldata = Bytes::from(func.abi_encode_input(args)?);
442 let result = self.call_raw(from, to, calldata, value)?;
443 result.into_decoded_result(func, rd)
444 }
445
446 pub fn call_sol<C: SolCall>(
448 &self,
449 from: Address,
450 to: Address,
451 args: &C,
452 value: U256,
453 rd: Option<&RevertDecoder>,
454 ) -> Result<CallResult<C::Return>, EvmError> {
455 let calldata = Bytes::from(args.abi_encode());
456 let mut raw = self.call_raw(from, to, calldata, value)?;
457 raw = raw.into_result(rd)?;
458 Ok(CallResult { decoded_result: C::abi_decode_returns(&raw.result)?, raw })
459 }
460
461 pub fn transact(
463 &mut self,
464 from: Address,
465 to: Address,
466 func: &Function,
467 args: &[DynSolValue],
468 value: U256,
469 rd: Option<&RevertDecoder>,
470 ) -> Result<CallResult, EvmError> {
471 let calldata = Bytes::from(func.abi_encode_input(args)?);
472 let result = self.transact_raw(from, to, calldata, value)?;
473 result.into_decoded_result(func, rd)
474 }
475
476 pub fn call_raw(
478 &self,
479 from: Address,
480 to: Address,
481 calldata: Bytes,
482 value: U256,
483 ) -> eyre::Result<RawCallResult> {
484 let (evm_env, tx_env) = self.build_test_env(from, TxKind::Call(to), calldata, value);
485 self.call_with_env(evm_env, tx_env)
486 }
487
488 pub fn call_raw_with_authorization(
491 &mut self,
492 from: Address,
493 to: Address,
494 calldata: Bytes,
495 value: U256,
496 authorization_list: Vec<SignedAuthorization>,
497 ) -> eyre::Result<RawCallResult> {
498 let (evm_env, mut tx_env) = self.build_test_env(from, to.into(), calldata, value);
499 tx_env.set_signed_authorization(authorization_list);
500 tx_env.tx_type = 4;
501 self.call_with_env(evm_env, tx_env)
502 }
503
504 pub fn transact_raw(
506 &mut self,
507 from: Address,
508 to: Address,
509 calldata: Bytes,
510 value: U256,
511 ) -> eyre::Result<RawCallResult> {
512 let (evm_env, tx_env) = self.build_test_env(from, TxKind::Call(to), calldata, value);
513 self.transact_with_env(evm_env, tx_env)
514 }
515
516 pub fn transact_raw_with_authorization(
519 &mut self,
520 from: Address,
521 to: Address,
522 calldata: Bytes,
523 value: U256,
524 authorization_list: Vec<SignedAuthorization>,
525 ) -> eyre::Result<RawCallResult> {
526 let (evm_env, mut tx_env) = self.build_test_env(from, TxKind::Call(to), calldata, value);
527 tx_env.set_signed_authorization(authorization_list);
528 tx_env.tx_type = 4;
529 self.transact_with_env(evm_env, tx_env)
530 }
531
532 #[instrument(name = "call", level = "debug", skip_all)]
536 pub fn call_with_env(
537 &self,
538 mut evm_env: EvmEnv,
539 mut tx_env: TxEnv,
540 ) -> eyre::Result<RawCallResult> {
541 let mut stack = self.inspector().clone();
542 let mut backend = CowBackend::new_borrowed(self.backend());
543 let result = backend.inspect(&mut evm_env, &mut tx_env, &mut stack)?;
544 convert_executed_result(
545 evm_env,
546 tx_env,
547 stack,
548 result,
549 backend.has_state_snapshot_failure(),
550 )
551 }
552
553 #[instrument(name = "transact", level = "debug", skip_all)]
555 pub fn transact_with_env(
556 &mut self,
557 mut evm_env: EvmEnv,
558 mut tx_env: TxEnv,
559 ) -> eyre::Result<RawCallResult> {
560 let mut stack = self.inspector().clone();
561 let backend = self.backend_mut();
562 let result: revm::context::result::ExecResultAndState<ExecutionResult> =
563 backend.inspect(&mut evm_env, &mut tx_env, &mut stack)?;
564 let mut result = convert_executed_result(
565 evm_env,
566 tx_env,
567 stack,
568 result,
569 backend.has_state_snapshot_failure(),
570 )?;
571 self.commit(&mut result);
572 Ok(result)
573 }
574
575 #[instrument(name = "commit", level = "debug", skip_all)]
580 fn commit(&mut self, result: &mut RawCallResult) {
581 self.backend_mut().commit(result.state_changeset.clone());
583
584 self.inspector_mut().cheatcodes = result.cheatcodes.take();
586 if let Some(cheats) = self.inspector_mut().cheatcodes.as_mut() {
587 cheats.broadcastable_transactions.clear();
589 cheats.ignored_traces.ignored.clear();
590
591 if let Some(last_pause_call) = cheats.ignored_traces.last_pause_call.as_mut() {
594 *last_pause_call = (0, 0);
595 }
596 }
597
598 self.inspector_mut().set_env(&result.evm_env, &result.tx_env);
600 }
601
602 pub fn is_raw_call_mut_success(
607 &self,
608 address: Address,
609 call_result: &mut RawCallResult,
610 should_fail: bool,
611 ) -> bool {
612 self.is_raw_call_success(
613 address,
614 Cow::Owned(std::mem::take(&mut call_result.state_changeset)),
615 call_result,
616 should_fail,
617 )
618 }
619
620 pub fn is_raw_call_success(
624 &self,
625 address: Address,
626 state_changeset: Cow<'_, StateChangeset>,
627 call_result: &RawCallResult,
628 should_fail: bool,
629 ) -> bool {
630 if call_result.has_state_snapshot_failure {
631 return should_fail;
633 }
634 self.is_success(address, call_result.reverted, state_changeset, should_fail)
635 }
636
637 pub fn is_success(
659 &self,
660 address: Address,
661 reverted: bool,
662 state_changeset: Cow<'_, StateChangeset>,
663 should_fail: bool,
664 ) -> bool {
665 let success = self.is_success_raw(address, reverted, state_changeset);
666 should_fail ^ success
667 }
668
669 #[instrument(name = "is_success", level = "debug", skip_all)]
670 fn is_success_raw(
671 &self,
672 address: Address,
673 reverted: bool,
674 state_changeset: Cow<'_, StateChangeset>,
675 ) -> bool {
676 if reverted {
678 return false;
679 }
680
681 if self.backend().has_state_snapshot_failure() {
683 return false;
684 }
685
686 if let Some(acc) = state_changeset.get(&CHEATCODE_ADDRESS)
688 && let Some(failed_slot) = acc.storage.get(&GLOBAL_FAIL_SLOT)
689 && !failed_slot.present_value().is_zero()
690 {
691 return false;
692 }
693 if let Ok(failed_slot) = self.backend().storage_ref(CHEATCODE_ADDRESS, GLOBAL_FAIL_SLOT)
694 && !failed_slot.is_zero()
695 {
696 return false;
697 }
698
699 if !self.legacy_assertions {
700 return true;
701 }
702
703 {
705 let mut backend = self.backend().clone_empty();
707
708 for address in [address, CHEATCODE_ADDRESS] {
711 let Ok(acc) = self.backend().basic_ref(address) else { return false };
712 backend.insert_account_info(address, acc.unwrap_or_default());
713 }
714
715 backend.commit(state_changeset.into_owned());
720
721 let executor = self.clone_with_backend(backend);
723 let call = executor.call_sol(CALLER, address, &ITest::failedCall {}, U256::ZERO, None);
724 match call {
725 Ok(CallResult { raw: _, decoded_result: failed }) => {
726 trace!(failed, "DSTest::failed()");
727 !failed
728 }
729 Err(err) => {
730 trace!(%err, "failed to call DSTest::failed()");
731 true
732 }
733 }
734 }
735 }
736
737 fn build_test_env(
742 &self,
743 caller: Address,
744 kind: TxKind,
745 data: Bytes,
746 value: U256,
747 ) -> (EvmEnv, TxEnv) {
748 let evm_env = EvmEnv {
749 cfg_env: {
750 let mut cfg = self.evm_env.cfg_env.clone();
751 cfg.spec = self.spec_id();
752 cfg
753 },
754 block_env: BlockEnv {
758 basefee: 0,
759 gas_limit: self.gas_limit,
760 ..self.evm_env.block_env.clone()
761 },
762 };
763 let tx_env = TxEnv {
764 caller,
765 kind,
766 data,
767 value,
768 gas_price: 0,
770 gas_priority_fee: None,
771 gas_limit: self.gas_limit,
772 chain_id: Some(self.evm_env.cfg_env.chain_id),
773 ..self.tx_env.clone()
774 };
775 (evm_env, tx_env)
776 }
777
778 pub fn call_sol_default<C: SolCall>(&self, to: Address, args: &C) -> C::Return
779 where
780 C::Return: Default,
781 {
782 self.call_sol(CALLER, to, args, U256::ZERO, None)
783 .map(|c| c.decoded_result)
784 .inspect_err(|e| warn!(target: "forge::test", "failed calling {:?}: {e}", C::SIGNATURE))
785 .unwrap_or_default()
786 }
787}
788
789#[derive(Debug, thiserror::Error)]
791#[error("execution reverted: {reason} (gas: {})", raw.gas_used)]
792pub struct ExecutionErr {
793 pub raw: RawCallResult,
795 pub reason: String,
797}
798
799impl std::ops::Deref for ExecutionErr {
800 type Target = RawCallResult;
801
802 #[inline]
803 fn deref(&self) -> &Self::Target {
804 &self.raw
805 }
806}
807
808impl std::ops::DerefMut for ExecutionErr {
809 #[inline]
810 fn deref_mut(&mut self) -> &mut Self::Target {
811 &mut self.raw
812 }
813}
814
815#[derive(Debug, thiserror::Error)]
816pub enum EvmError {
817 #[error(transparent)]
819 Execution(#[from] Box<ExecutionErr>),
820 #[error(transparent)]
822 Abi(#[from] alloy_dyn_abi::Error),
823 #[error("{0}")]
825 Skip(SkipReason),
826 #[error("{0}")]
828 Eyre(
829 #[from]
830 #[source]
831 eyre::Report,
832 ),
833}
834
835impl From<ExecutionErr> for EvmError {
836 fn from(err: ExecutionErr) -> Self {
837 Self::Execution(Box::new(err))
838 }
839}
840
841impl From<alloy_sol_types::Error> for EvmError {
842 fn from(err: alloy_sol_types::Error) -> Self {
843 Self::Abi(err.into())
844 }
845}
846
847#[derive(Debug)]
849pub struct DeployResult {
850 pub raw: RawCallResult,
852 pub address: Address,
854}
855
856impl std::ops::Deref for DeployResult {
857 type Target = RawCallResult;
858
859 #[inline]
860 fn deref(&self) -> &Self::Target {
861 &self.raw
862 }
863}
864
865impl std::ops::DerefMut for DeployResult {
866 #[inline]
867 fn deref_mut(&mut self) -> &mut Self::Target {
868 &mut self.raw
869 }
870}
871
872impl From<DeployResult> for RawCallResult {
873 fn from(d: DeployResult) -> Self {
874 d.raw
875 }
876}
877
878#[derive(Debug)]
880pub struct RawCallResult {
881 pub exit_reason: Option<InstructionResult>,
883 pub reverted: bool,
885 pub has_state_snapshot_failure: bool,
890 pub result: Bytes,
892 pub gas_used: u64,
894 pub gas_refunded: u64,
896 pub stipend: u64,
898 pub logs: Vec<Log>,
900 pub labels: AddressHashMap<String>,
902 pub traces: Option<SparsedTraceArena>,
904 pub line_coverage: Option<HitMaps>,
906 pub edge_coverage: Option<Vec<u8>>,
908 pub transactions: Option<BroadcastableTransactions>,
910 pub state_changeset: StateChangeset,
912 pub evm_env: EvmEnv,
914 pub tx_env: TxEnv,
916 pub cheatcodes: Option<Box<Cheatcodes>>,
918 pub out: Option<Output>,
920 pub chisel_state: Option<(Vec<U256>, Vec<u8>)>,
922 pub reverter: Option<Address>,
923}
924
925impl Default for RawCallResult {
926 fn default() -> Self {
927 Self {
928 exit_reason: None,
929 reverted: false,
930 has_state_snapshot_failure: false,
931 result: Bytes::new(),
932 gas_used: 0,
933 gas_refunded: 0,
934 stipend: 0,
935 logs: Vec::new(),
936 labels: HashMap::default(),
937 traces: None,
938 line_coverage: None,
939 edge_coverage: None,
940 transactions: None,
941 state_changeset: HashMap::default(),
942 evm_env: EvmEnv::default(),
943 tx_env: TxEnv::default(),
944 cheatcodes: Default::default(),
945 out: None,
946 chisel_state: None,
947 reverter: None,
948 }
949 }
950}
951
952impl RawCallResult {
953 pub fn from_evm_result(r: Result<Self, EvmError>) -> eyre::Result<(Self, Option<String>)> {
955 match r {
956 Ok(r) => Ok((r, None)),
957 Err(EvmError::Execution(e)) => Ok((e.raw, Some(e.reason))),
958 Err(e) => Err(e.into()),
959 }
960 }
961
962 pub fn into_evm_error(self, rd: Option<&RevertDecoder>) -> EvmError {
964 if let Some(reason) = SkipReason::decode(&self.result) {
965 return EvmError::Skip(reason);
966 }
967 let reason = rd.unwrap_or_default().decode(&self.result, self.exit_reason);
968 EvmError::Execution(Box::new(self.into_execution_error(reason)))
969 }
970
971 pub fn into_execution_error(self, reason: String) -> ExecutionErr {
973 ExecutionErr { raw: self, reason }
974 }
975
976 pub fn into_result(self, rd: Option<&RevertDecoder>) -> Result<Self, EvmError> {
978 if let Some(reason) = self.exit_reason
979 && reason.is_ok()
980 {
981 Ok(self)
982 } else {
983 Err(self.into_evm_error(rd))
984 }
985 }
986
987 pub fn into_decoded_result(
989 mut self,
990 func: &Function,
991 rd: Option<&RevertDecoder>,
992 ) -> Result<CallResult, EvmError> {
993 self = self.into_result(rd)?;
994 let mut result = func.abi_decode_output(&self.result)?;
995 let decoded_result = if result.len() == 1 {
996 result.pop().unwrap()
997 } else {
998 DynSolValue::Tuple(result)
1000 };
1001 Ok(CallResult { raw: self, decoded_result })
1002 }
1003
1004 pub fn transactions(&self) -> Option<&BroadcastableTransactions> {
1006 self.cheatcodes.as_ref().map(|c| &c.broadcastable_transactions)
1007 }
1008
1009 pub fn merge_edge_coverage(&mut self, history_map: &mut [u8]) -> (bool, bool) {
1012 let mut new_coverage = false;
1013 let mut is_edge = false;
1014 if let Some(x) = &mut self.edge_coverage {
1015 for (curr, hist) in std::iter::zip(x, history_map) {
1018 if *curr > 0 {
1020 let bucket = match *curr {
1022 0 => 0,
1023 1 => 1,
1024 2 => 2,
1025 3 => 4,
1026 4..=7 => 8,
1027 8..=15 => 16,
1028 16..=31 => 32,
1029 32..=127 => 64,
1030 128..=255 => 128,
1031 };
1032
1033 if *hist < bucket {
1035 if *hist == 0 {
1036 is_edge = true;
1038 }
1039 *hist = bucket;
1040 new_coverage = true;
1041 }
1042
1043 *curr = 0;
1045 }
1046 }
1047 }
1048 (new_coverage, is_edge)
1049 }
1050}
1051
1052pub struct CallResult<T = DynSolValue> {
1054 pub raw: RawCallResult,
1056 pub decoded_result: T,
1058}
1059
1060impl std::ops::Deref for CallResult {
1061 type Target = RawCallResult;
1062
1063 #[inline]
1064 fn deref(&self) -> &Self::Target {
1065 &self.raw
1066 }
1067}
1068
1069impl std::ops::DerefMut for CallResult {
1070 #[inline]
1071 fn deref_mut(&mut self) -> &mut Self::Target {
1072 &mut self.raw
1073 }
1074}
1075
1076fn convert_executed_result(
1078 evm_env: EvmEnv,
1079 tx_env: TxEnv,
1080 inspector: InspectorStack,
1081 ResultAndState { result, state: state_changeset }: ResultAndState,
1082 has_state_snapshot_failure: bool,
1083) -> eyre::Result<RawCallResult> {
1084 let (exit_reason, gas_refunded, gas_used, out, exec_logs) = match result {
1085 ExecutionResult::Success { reason, gas_used, gas_refunded, output, logs, .. } => {
1086 (reason.into(), gas_refunded, gas_used, Some(output), logs)
1087 }
1088 ExecutionResult::Revert { gas_used, output } => {
1089 (InstructionResult::Revert, 0_u64, gas_used, Some(Output::Call(output)), vec![])
1091 }
1092 ExecutionResult::Halt { reason, gas_used } => {
1093 (reason.into(), 0_u64, gas_used, None, vec![])
1094 }
1095 };
1096 let gas = revm::interpreter::gas::calculate_initial_tx_gas(
1097 evm_env.cfg_env.spec,
1098 &tx_env.data,
1099 tx_env.kind.is_create(),
1100 tx_env.access_list.len().try_into()?,
1101 0,
1102 0,
1103 );
1104
1105 let result = match &out {
1106 Some(Output::Call(data)) => data.clone(),
1107 _ => Bytes::new(),
1108 };
1109
1110 let InspectorData {
1111 mut logs,
1112 labels,
1113 traces,
1114 line_coverage,
1115 edge_coverage,
1116 cheatcodes,
1117 chisel_state,
1118 reverter,
1119 } = inspector.collect();
1120
1121 if logs.is_empty() {
1122 logs = exec_logs;
1123 }
1124
1125 let transactions = cheatcodes
1126 .as_ref()
1127 .map(|c| c.broadcastable_transactions.clone())
1128 .filter(|txs| !txs.is_empty());
1129
1130 Ok(RawCallResult {
1131 exit_reason: Some(exit_reason),
1132 reverted: !matches!(exit_reason, return_ok!()),
1133 has_state_snapshot_failure,
1134 result,
1135 gas_used,
1136 gas_refunded,
1137 stipend: gas.initial_gas,
1138 logs,
1139 labels,
1140 traces,
1141 line_coverage,
1142 edge_coverage,
1143 transactions,
1144 state_changeset,
1145 evm_env,
1146 tx_env,
1147 cheatcodes,
1148 out,
1149 chisel_state,
1150 reverter,
1151 })
1152}
1153
1154pub struct FuzzTestTimer {
1156 inner: Option<(Instant, Duration)>,
1158}
1159
1160impl FuzzTestTimer {
1161 pub fn new(timeout: Option<u32>) -> Self {
1162 Self { inner: timeout.map(|timeout| (Instant::now(), Duration::from_secs(timeout.into()))) }
1163 }
1164
1165 pub fn is_enabled(&self) -> bool {
1167 self.inner.is_some()
1168 }
1169
1170 pub fn is_timed_out(&self) -> bool {
1172 self.inner.is_some_and(|(start, duration)| start.elapsed() > duration)
1173 }
1174}
1175
1176#[derive(Clone, Debug)]
1179pub struct EarlyExit {
1180 inner: Arc<AtomicBool>,
1182 fail_fast: bool,
1184}
1185
1186impl EarlyExit {
1187 pub fn new(fail_fast: bool) -> Self {
1188 Self { inner: Arc::new(AtomicBool::new(false)), fail_fast }
1189 }
1190
1191 pub fn record_failure(&self) {
1193 if self.fail_fast {
1194 self.inner.store(true, Ordering::Relaxed);
1195 }
1196 }
1197
1198 pub fn record_ctrl_c(&self) {
1200 self.inner.store(true, Ordering::Relaxed);
1201 }
1202
1203 pub fn should_stop(&self) -> bool {
1205 self.inner.load(Ordering::Relaxed)
1206 }
1207}