1use crate::{
10 Env,
11 inspectors::{
12 Cheatcodes, InspectorData, InspectorStack, cheatcodes::BroadcastableTransactions,
13 },
14};
15use alloy_dyn_abi::{DynSolValue, FunctionExt, JsonAbiExt};
16use alloy_json_abi::Function;
17use alloy_primitives::{
18 Address, Bytes, Log, TxKind, U256, keccak256,
19 map::{AddressHashMap, HashMap},
20};
21use alloy_sol_types::{SolCall, sol};
22use foundry_evm_core::{
23 EvmEnv,
24 backend::{Backend, BackendError, BackendResult, CowBackend, DatabaseExt, GLOBAL_FAIL_SLOT},
25 constants::{
26 CALLER, CHEATCODE_ADDRESS, CHEATCODE_CONTRACT_HASH, DEFAULT_CREATE2_DEPLOYER,
27 DEFAULT_CREATE2_DEPLOYER_CODE, DEFAULT_CREATE2_DEPLOYER_DEPLOYER,
28 },
29 decode::{RevertDecoder, SkipReason},
30 utils::StateChangeset,
31};
32use foundry_evm_coverage::HitMaps;
33use foundry_evm_traces::{SparsedTraceArena, TraceMode};
34use revm::{
35 bytecode::Bytecode,
36 context::{BlockEnv, TxEnv},
37 context_interface::{
38 result::{ExecutionResult, Output, ResultAndState},
39 transaction::SignedAuthorization,
40 },
41 database::{DatabaseCommit, DatabaseRef},
42 interpreter::{InstructionResult, return_ok},
43 primitives::hardfork::SpecId,
44};
45use std::{
46 borrow::Cow,
47 sync::{
48 Arc,
49 atomic::{AtomicBool, Ordering},
50 },
51 time::{Duration, Instant},
52};
53
54mod builder;
55pub use builder::ExecutorBuilder;
56
57pub mod fuzz;
58pub use fuzz::FuzzedExecutor;
59
60pub mod invariant;
61pub use invariant::InvariantExecutor;
62
63mod corpus;
64mod trace;
65
66pub use trace::TracingExecutor;
67
68const DURATION_BETWEEN_METRICS_REPORT: Duration = Duration::from_secs(5);
69
70sol! {
71 interface ITest {
72 function setUp() external;
73 function failed() external view returns (bool failed);
74
75 #[derive(Default)]
76 function beforeTestSetup(bytes4 testSelector) public view returns (bytes[] memory beforeTestCalldata);
77 }
78}
79
80#[derive(Clone, Debug)]
92pub struct Executor {
93 backend: Backend,
99 env: Env,
101 inspector: InspectorStack,
103 gas_limit: u64,
105 legacy_assertions: bool,
107}
108
109impl Executor {
110 #[inline]
112 pub fn builder() -> ExecutorBuilder {
113 ExecutorBuilder::new()
114 }
115
116 #[inline]
118 pub fn new(
119 mut backend: Backend,
120 env: Env,
121 inspector: InspectorStack,
122 gas_limit: u64,
123 legacy_assertions: bool,
124 ) -> Self {
125 backend.insert_account_info(
128 CHEATCODE_ADDRESS,
129 revm::state::AccountInfo {
130 code: Some(Bytecode::new_raw(Bytes::from_static(&[0]))),
131 code_hash: CHEATCODE_CONTRACT_HASH,
134 ..Default::default()
135 },
136 );
137
138 Self { backend, env, inspector, gas_limit, legacy_assertions }
139 }
140
141 fn clone_with_backend(&self, backend: Backend) -> Self {
142 let env = Env::new_with_spec_id(
143 self.env.evm_env.cfg_env.clone(),
144 self.env.evm_env.block_env.clone(),
145 self.env.tx.clone(),
146 self.spec_id(),
147 );
148 Self::new(backend, env, self.inspector().clone(), self.gas_limit, self.legacy_assertions)
149 }
150
151 pub fn backend(&self) -> &Backend {
153 &self.backend
154 }
155
156 pub fn backend_mut(&mut self) -> &mut Backend {
158 &mut self.backend
159 }
160
161 pub fn env(&self) -> &Env {
163 &self.env
164 }
165
166 pub fn env_mut(&mut self) -> &mut Env {
168 &mut self.env
169 }
170
171 pub fn inspector(&self) -> &InspectorStack {
173 &self.inspector
174 }
175
176 pub fn inspector_mut(&mut self) -> &mut InspectorStack {
178 &mut self.inspector
179 }
180
181 pub fn spec_id(&self) -> SpecId {
183 self.env.evm_env.cfg_env.spec
184 }
185
186 pub fn set_spec_id(&mut self, spec_id: SpecId) {
188 self.env.evm_env.cfg_env.spec = spec_id;
189 }
190
191 pub fn gas_limit(&self) -> u64 {
196 self.gas_limit
197 }
198
199 pub fn set_gas_limit(&mut self, gas_limit: u64) {
201 self.gas_limit = gas_limit;
202 }
203
204 pub fn legacy_assertions(&self) -> bool {
207 self.legacy_assertions
208 }
209
210 pub fn set_legacy_assertions(&mut self, legacy_assertions: bool) {
213 self.legacy_assertions = legacy_assertions;
214 }
215
216 pub fn deploy_create2_deployer(&mut self) -> eyre::Result<()> {
218 trace!("deploying local create2 deployer");
219 let create2_deployer_account = self
220 .backend()
221 .basic_ref(DEFAULT_CREATE2_DEPLOYER)?
222 .ok_or_else(|| BackendError::MissingAccount(DEFAULT_CREATE2_DEPLOYER))?;
223
224 if create2_deployer_account.code.is_none_or(|code| code.is_empty()) {
226 let creator = DEFAULT_CREATE2_DEPLOYER_DEPLOYER;
227
228 let initial_balance = self.get_balance(creator)?;
230 self.set_balance(creator, U256::MAX)?;
231
232 let res =
233 self.deploy(creator, DEFAULT_CREATE2_DEPLOYER_CODE.into(), U256::ZERO, None)?;
234 trace!(create2=?res.address, "deployed local create2 deployer");
235
236 self.set_balance(creator, initial_balance)?;
237 }
238 Ok(())
239 }
240
241 pub fn set_balance(&mut self, address: Address, amount: U256) -> BackendResult<()> {
243 trace!(?address, ?amount, "setting account balance");
244 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
245 account.balance = amount;
246 self.backend_mut().insert_account_info(address, account);
247 Ok(())
248 }
249
250 pub fn get_balance(&self, address: Address) -> BackendResult<U256> {
252 Ok(self.backend().basic_ref(address)?.map(|acc| acc.balance).unwrap_or_default())
253 }
254
255 pub fn set_nonce(&mut self, address: Address, nonce: u64) -> BackendResult<()> {
257 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
258 account.nonce = nonce;
259 self.backend_mut().insert_account_info(address, account);
260 self.env_mut().tx.nonce = nonce;
261 Ok(())
262 }
263
264 pub fn get_nonce(&self, address: Address) -> BackendResult<u64> {
266 Ok(self.backend().basic_ref(address)?.map(|acc| acc.nonce).unwrap_or_default())
267 }
268
269 pub fn set_code(&mut self, address: Address, code: Bytecode) -> BackendResult<()> {
271 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
272 account.code_hash = keccak256(code.original_byte_slice());
273 account.code = Some(code);
274 self.backend_mut().insert_account_info(address, account);
275 Ok(())
276 }
277
278 pub fn set_storage(
280 &mut self,
281 address: Address,
282 storage: HashMap<U256, U256>,
283 ) -> BackendResult<()> {
284 self.backend_mut().replace_account_storage(address, storage)?;
285 Ok(())
286 }
287
288 pub fn set_storage_slot(
290 &mut self,
291 address: Address,
292 slot: U256,
293 value: U256,
294 ) -> BackendResult<()> {
295 self.backend_mut().insert_account_storage(address, slot, value)?;
296 Ok(())
297 }
298
299 pub fn is_empty_code(&self, address: Address) -> BackendResult<bool> {
301 Ok(self.backend().basic_ref(address)?.map(|acc| acc.is_empty_code_hash()).unwrap_or(true))
302 }
303
304 #[inline]
305 pub fn set_tracing(&mut self, mode: TraceMode) -> &mut Self {
306 self.inspector_mut().tracing(mode);
307 self
308 }
309
310 #[inline]
311 pub fn set_script_execution(&mut self, script_address: Address) {
312 self.inspector_mut().script(script_address);
313 }
314
315 #[inline]
316 pub fn set_trace_printer(&mut self, trace_printer: bool) -> &mut Self {
317 self.inspector_mut().print(trace_printer);
318 self
319 }
320
321 #[inline]
322 pub fn create2_deployer(&self) -> Address {
323 self.inspector().create2_deployer
324 }
325
326 pub fn deploy(
331 &mut self,
332 from: Address,
333 code: Bytes,
334 value: U256,
335 rd: Option<&RevertDecoder>,
336 ) -> Result<DeployResult, EvmError> {
337 let env = self.build_test_env(from, TxKind::Create, code, value);
338 self.deploy_with_env(env, rd)
339 }
340
341 #[instrument(name = "deploy", level = "debug", skip_all)]
348 pub fn deploy_with_env(
349 &mut self,
350 env: Env,
351 rd: Option<&RevertDecoder>,
352 ) -> Result<DeployResult, EvmError> {
353 assert!(
354 matches!(env.tx.kind, TxKind::Create),
355 "Expected create transaction, got {:?}",
356 env.tx.kind
357 );
358 trace!(sender=%env.tx.caller, "deploying contract");
359
360 let mut result = self.transact_with_env(env)?;
361 result = result.into_result(rd)?;
362 let Some(Output::Create(_, Some(address))) = result.out else {
363 panic!("Deployment succeeded, but no address was returned: {result:#?}");
364 };
365
366 self.backend_mut().add_persistent_account(address);
369
370 debug!(%address, "deployed contract");
371
372 Ok(DeployResult { raw: result, address })
373 }
374
375 #[instrument(name = "setup", level = "debug", skip_all)]
382 pub fn setup(
383 &mut self,
384 from: Option<Address>,
385 to: Address,
386 rd: Option<&RevertDecoder>,
387 ) -> Result<RawCallResult, EvmError> {
388 trace!(?from, ?to, "setting up contract");
389
390 let from = from.unwrap_or(CALLER);
391 self.backend_mut().set_test_contract(to).set_caller(from);
392 let calldata = Bytes::from_static(&ITest::setUpCall::SELECTOR);
393 let mut res = self.transact_raw(from, to, calldata, U256::ZERO)?;
394 res = res.into_result(rd)?;
395
396 self.env_mut().evm_env.block_env = res.env.evm_env.block_env.clone();
398 self.env_mut().evm_env.cfg_env.chain_id = res.env.evm_env.cfg_env.chain_id;
400
401 let success =
402 self.is_raw_call_success(to, Cow::Borrowed(&res.state_changeset), &res, false);
403 if !success {
404 return Err(res.into_execution_error("execution error".to_string()).into());
405 }
406
407 Ok(res)
408 }
409
410 pub fn call(
412 &self,
413 from: Address,
414 to: Address,
415 func: &Function,
416 args: &[DynSolValue],
417 value: U256,
418 rd: Option<&RevertDecoder>,
419 ) -> Result<CallResult, EvmError> {
420 let calldata = Bytes::from(func.abi_encode_input(args)?);
421 let result = self.call_raw(from, to, calldata, value)?;
422 result.into_decoded_result(func, rd)
423 }
424
425 pub fn call_sol<C: SolCall>(
427 &self,
428 from: Address,
429 to: Address,
430 args: &C,
431 value: U256,
432 rd: Option<&RevertDecoder>,
433 ) -> Result<CallResult<C::Return>, EvmError> {
434 let calldata = Bytes::from(args.abi_encode());
435 let mut raw = self.call_raw(from, to, calldata, value)?;
436 raw = raw.into_result(rd)?;
437 Ok(CallResult { decoded_result: C::abi_decode_returns(&raw.result)?, raw })
438 }
439
440 pub fn transact(
442 &mut self,
443 from: Address,
444 to: Address,
445 func: &Function,
446 args: &[DynSolValue],
447 value: U256,
448 rd: Option<&RevertDecoder>,
449 ) -> Result<CallResult, EvmError> {
450 let calldata = Bytes::from(func.abi_encode_input(args)?);
451 let result = self.transact_raw(from, to, calldata, value)?;
452 result.into_decoded_result(func, rd)
453 }
454
455 pub fn call_raw(
457 &self,
458 from: Address,
459 to: Address,
460 calldata: Bytes,
461 value: U256,
462 ) -> eyre::Result<RawCallResult> {
463 let env = self.build_test_env(from, TxKind::Call(to), calldata, value);
464 self.call_with_env(env)
465 }
466
467 pub fn call_raw_with_authorization(
470 &mut self,
471 from: Address,
472 to: Address,
473 calldata: Bytes,
474 value: U256,
475 authorization_list: Vec<SignedAuthorization>,
476 ) -> eyre::Result<RawCallResult> {
477 let mut env = self.build_test_env(from, to.into(), calldata, value);
478 env.tx.set_signed_authorization(authorization_list);
479 env.tx.tx_type = 4;
480 self.call_with_env(env)
481 }
482
483 pub fn transact_raw(
485 &mut self,
486 from: Address,
487 to: Address,
488 calldata: Bytes,
489 value: U256,
490 ) -> eyre::Result<RawCallResult> {
491 let env = self.build_test_env(from, TxKind::Call(to), calldata, value);
492 self.transact_with_env(env)
493 }
494
495 pub fn transact_raw_with_authorization(
498 &mut self,
499 from: Address,
500 to: Address,
501 calldata: Bytes,
502 value: U256,
503 authorization_list: Vec<SignedAuthorization>,
504 ) -> eyre::Result<RawCallResult> {
505 let mut env = self.build_test_env(from, TxKind::Call(to), calldata, value);
506 env.tx.set_signed_authorization(authorization_list);
507 env.tx.tx_type = 4;
508 self.transact_with_env(env)
509 }
510
511 #[instrument(name = "call", level = "debug", skip_all)]
515 pub fn call_with_env(&self, mut env: Env) -> eyre::Result<RawCallResult> {
516 let mut stack = self.inspector().clone();
517 let mut backend = CowBackend::new_borrowed(self.backend());
518 let result = backend.inspect(&mut env, stack.as_inspector())?;
519 convert_executed_result(env, stack, result, backend.has_state_snapshot_failure())
520 }
521
522 #[instrument(name = "transact", level = "debug", skip_all)]
524 pub fn transact_with_env(&mut self, mut env: Env) -> eyre::Result<RawCallResult> {
525 let mut stack = self.inspector().clone();
526 let backend = self.backend_mut();
527 let result = backend.inspect(&mut env, stack.as_inspector())?;
528 let mut result =
529 convert_executed_result(env, stack, result, backend.has_state_snapshot_failure())?;
530 self.commit(&mut result);
531 Ok(result)
532 }
533
534 #[instrument(name = "commit", level = "debug", skip_all)]
539 fn commit(&mut self, result: &mut RawCallResult) {
540 self.backend_mut().commit(result.state_changeset.clone());
542
543 self.inspector_mut().cheatcodes = result.cheatcodes.take();
545 if let Some(cheats) = self.inspector_mut().cheatcodes.as_mut() {
546 cheats.broadcastable_transactions.clear();
548 cheats.ignored_traces.ignored.clear();
549
550 if let Some(last_pause_call) = cheats.ignored_traces.last_pause_call.as_mut() {
553 *last_pause_call = (0, 0);
554 }
555 }
556
557 self.inspector_mut().set_env(&result.env);
559 }
560
561 pub fn is_raw_call_mut_success(
566 &self,
567 address: Address,
568 call_result: &mut RawCallResult,
569 should_fail: bool,
570 ) -> bool {
571 self.is_raw_call_success(
572 address,
573 Cow::Owned(std::mem::take(&mut call_result.state_changeset)),
574 call_result,
575 should_fail,
576 )
577 }
578
579 pub fn is_raw_call_success(
583 &self,
584 address: Address,
585 state_changeset: Cow<'_, StateChangeset>,
586 call_result: &RawCallResult,
587 should_fail: bool,
588 ) -> bool {
589 if call_result.has_state_snapshot_failure {
590 return should_fail;
592 }
593 self.is_success(address, call_result.reverted, state_changeset, should_fail)
594 }
595
596 pub fn is_success(
618 &self,
619 address: Address,
620 reverted: bool,
621 state_changeset: Cow<'_, StateChangeset>,
622 should_fail: bool,
623 ) -> bool {
624 let success = self.is_success_raw(address, reverted, state_changeset);
625 should_fail ^ success
626 }
627
628 #[instrument(name = "is_success", level = "debug", skip_all)]
629 fn is_success_raw(
630 &self,
631 address: Address,
632 reverted: bool,
633 state_changeset: Cow<'_, StateChangeset>,
634 ) -> bool {
635 if reverted {
637 return false;
638 }
639
640 if self.backend().has_state_snapshot_failure() {
642 return false;
643 }
644
645 if let Some(acc) = state_changeset.get(&CHEATCODE_ADDRESS)
647 && let Some(failed_slot) = acc.storage.get(&GLOBAL_FAIL_SLOT)
648 && !failed_slot.present_value().is_zero()
649 {
650 return false;
651 }
652 if let Ok(failed_slot) = self.backend().storage_ref(CHEATCODE_ADDRESS, GLOBAL_FAIL_SLOT)
653 && !failed_slot.is_zero()
654 {
655 return false;
656 }
657
658 if !self.legacy_assertions {
659 return true;
660 }
661
662 {
664 let mut backend = self.backend().clone_empty();
666
667 for address in [address, CHEATCODE_ADDRESS] {
670 let Ok(acc) = self.backend().basic_ref(address) else { return false };
671 backend.insert_account_info(address, acc.unwrap_or_default());
672 }
673
674 backend.commit(state_changeset.into_owned());
679
680 let executor = self.clone_with_backend(backend);
682 let call = executor.call_sol(CALLER, address, &ITest::failedCall {}, U256::ZERO, None);
683 match call {
684 Ok(CallResult { raw: _, decoded_result: failed }) => {
685 trace!(failed, "DSTest::failed()");
686 !failed
687 }
688 Err(err) => {
689 trace!(%err, "failed to call DSTest::failed()");
690 true
691 }
692 }
693 }
694 }
695
696 fn build_test_env(&self, caller: Address, kind: TxKind, data: Bytes, value: U256) -> Env {
701 Env {
702 evm_env: EvmEnv {
703 cfg_env: {
704 let mut cfg = self.env().evm_env.cfg_env.clone();
705 cfg.spec = self.spec_id();
706 cfg
707 },
708 block_env: BlockEnv {
712 basefee: 0,
713 gas_limit: self.gas_limit,
714 ..self.env().evm_env.block_env.clone()
715 },
716 },
717 tx: TxEnv {
718 caller,
719 kind,
720 data,
721 value,
722 gas_price: 0,
724 gas_priority_fee: None,
725 gas_limit: self.gas_limit,
726 chain_id: Some(self.env().evm_env.cfg_env.chain_id),
727 ..self.env().tx.clone()
728 },
729 }
730 }
731
732 pub fn call_sol_default<C: SolCall>(&self, to: Address, args: &C) -> C::Return
733 where
734 C::Return: Default,
735 {
736 self.call_sol(CALLER, to, args, U256::ZERO, None)
737 .map(|c| c.decoded_result)
738 .inspect_err(|e| warn!(target: "forge::test", "failed calling {:?}: {e}", C::SIGNATURE))
739 .unwrap_or_default()
740 }
741}
742
743#[derive(Debug, thiserror::Error)]
745#[error("execution reverted: {reason} (gas: {})", raw.gas_used)]
746pub struct ExecutionErr {
747 pub raw: RawCallResult,
749 pub reason: String,
751}
752
753impl std::ops::Deref for ExecutionErr {
754 type Target = RawCallResult;
755
756 #[inline]
757 fn deref(&self) -> &Self::Target {
758 &self.raw
759 }
760}
761
762impl std::ops::DerefMut for ExecutionErr {
763 #[inline]
764 fn deref_mut(&mut self) -> &mut Self::Target {
765 &mut self.raw
766 }
767}
768
769#[derive(Debug, thiserror::Error)]
770pub enum EvmError {
771 #[error(transparent)]
773 Execution(#[from] Box<ExecutionErr>),
774 #[error(transparent)]
776 Abi(#[from] alloy_dyn_abi::Error),
777 #[error("{0}")]
779 Skip(SkipReason),
780 #[error("{0}")]
782 Eyre(
783 #[from]
784 #[source]
785 eyre::Report,
786 ),
787}
788
789impl From<ExecutionErr> for EvmError {
790 fn from(err: ExecutionErr) -> Self {
791 Self::Execution(Box::new(err))
792 }
793}
794
795impl From<alloy_sol_types::Error> for EvmError {
796 fn from(err: alloy_sol_types::Error) -> Self {
797 Self::Abi(err.into())
798 }
799}
800
801#[derive(Debug)]
803pub struct DeployResult {
804 pub raw: RawCallResult,
806 pub address: Address,
808}
809
810impl std::ops::Deref for DeployResult {
811 type Target = RawCallResult;
812
813 #[inline]
814 fn deref(&self) -> &Self::Target {
815 &self.raw
816 }
817}
818
819impl std::ops::DerefMut for DeployResult {
820 #[inline]
821 fn deref_mut(&mut self) -> &mut Self::Target {
822 &mut self.raw
823 }
824}
825
826impl From<DeployResult> for RawCallResult {
827 fn from(d: DeployResult) -> Self {
828 d.raw
829 }
830}
831
832#[derive(Debug)]
834pub struct RawCallResult {
835 pub exit_reason: Option<InstructionResult>,
837 pub reverted: bool,
839 pub has_state_snapshot_failure: bool,
844 pub result: Bytes,
846 pub gas_used: u64,
848 pub gas_refunded: u64,
850 pub stipend: u64,
852 pub logs: Vec<Log>,
854 pub labels: AddressHashMap<String>,
856 pub traces: Option<SparsedTraceArena>,
858 pub line_coverage: Option<HitMaps>,
860 pub edge_coverage: Option<Vec<u8>>,
862 pub transactions: Option<BroadcastableTransactions>,
864 pub state_changeset: StateChangeset,
866 pub env: Env,
868 pub cheatcodes: Option<Box<Cheatcodes>>,
870 pub out: Option<Output>,
872 pub chisel_state: Option<(Vec<U256>, Vec<u8>)>,
874 pub reverter: Option<Address>,
875}
876
877impl Default for RawCallResult {
878 fn default() -> Self {
879 Self {
880 exit_reason: None,
881 reverted: false,
882 has_state_snapshot_failure: false,
883 result: Bytes::new(),
884 gas_used: 0,
885 gas_refunded: 0,
886 stipend: 0,
887 logs: Vec::new(),
888 labels: HashMap::default(),
889 traces: None,
890 line_coverage: None,
891 edge_coverage: None,
892 transactions: None,
893 state_changeset: HashMap::default(),
894 env: Env::default(),
895 cheatcodes: Default::default(),
896 out: None,
897 chisel_state: None,
898 reverter: None,
899 }
900 }
901}
902
903impl RawCallResult {
904 pub fn from_evm_result(r: Result<Self, EvmError>) -> eyre::Result<(Self, Option<String>)> {
906 match r {
907 Ok(r) => Ok((r, None)),
908 Err(EvmError::Execution(e)) => Ok((e.raw, Some(e.reason))),
909 Err(e) => Err(e.into()),
910 }
911 }
912
913 pub fn from_execution_result(r: Result<Self, ExecutionErr>) -> (Self, Option<String>) {
915 match r {
916 Ok(r) => (r, None),
917 Err(e) => (e.raw, Some(e.reason)),
918 }
919 }
920
921 pub fn into_evm_error(self, rd: Option<&RevertDecoder>) -> EvmError {
923 if let Some(reason) = SkipReason::decode(&self.result) {
924 return EvmError::Skip(reason);
925 }
926 let reason = rd.unwrap_or_default().decode(&self.result, self.exit_reason);
927 EvmError::Execution(Box::new(self.into_execution_error(reason)))
928 }
929
930 pub fn into_execution_error(self, reason: String) -> ExecutionErr {
932 ExecutionErr { raw: self, reason }
933 }
934
935 pub fn into_result(self, rd: Option<&RevertDecoder>) -> Result<Self, EvmError> {
937 if let Some(reason) = self.exit_reason
938 && reason.is_ok()
939 {
940 Ok(self)
941 } else {
942 Err(self.into_evm_error(rd))
943 }
944 }
945
946 pub fn into_decoded_result(
948 mut self,
949 func: &Function,
950 rd: Option<&RevertDecoder>,
951 ) -> Result<CallResult, EvmError> {
952 self = self.into_result(rd)?;
953 let mut result = func.abi_decode_output(&self.result)?;
954 let decoded_result = if result.len() == 1 {
955 result.pop().unwrap()
956 } else {
957 DynSolValue::Tuple(result)
959 };
960 Ok(CallResult { raw: self, decoded_result })
961 }
962
963 pub fn transactions(&self) -> Option<&BroadcastableTransactions> {
965 self.cheatcodes.as_ref().map(|c| &c.broadcastable_transactions)
966 }
967
968 pub fn merge_edge_coverage(&mut self, history_map: &mut [u8]) -> (bool, bool) {
971 let mut new_coverage = false;
972 let mut is_edge = false;
973 if let Some(x) = &mut self.edge_coverage {
974 for (curr, hist) in std::iter::zip(x, history_map) {
977 if *curr > 0 {
979 let bucket = match *curr {
981 0 => 0,
982 1 => 1,
983 2 => 2,
984 3 => 4,
985 4..=7 => 8,
986 8..=15 => 16,
987 16..=31 => 32,
988 32..=127 => 64,
989 128..=255 => 128,
990 };
991
992 if *hist < bucket {
994 if *hist == 0 {
995 is_edge = true;
997 }
998 *hist = bucket;
999 new_coverage = true;
1000 }
1001
1002 *curr = 0;
1004 }
1005 }
1006 }
1007 (new_coverage, is_edge)
1008 }
1009}
1010
1011pub struct CallResult<T = DynSolValue> {
1013 pub raw: RawCallResult,
1015 pub decoded_result: T,
1017}
1018
1019impl std::ops::Deref for CallResult {
1020 type Target = RawCallResult;
1021
1022 #[inline]
1023 fn deref(&self) -> &Self::Target {
1024 &self.raw
1025 }
1026}
1027
1028impl std::ops::DerefMut for CallResult {
1029 #[inline]
1030 fn deref_mut(&mut self) -> &mut Self::Target {
1031 &mut self.raw
1032 }
1033}
1034
1035fn convert_executed_result(
1037 env: Env,
1038 inspector: InspectorStack,
1039 ResultAndState { result, state: state_changeset }: ResultAndState,
1040 has_state_snapshot_failure: bool,
1041) -> eyre::Result<RawCallResult> {
1042 let (exit_reason, gas_refunded, gas_used, out, exec_logs) = match result {
1043 ExecutionResult::Success { reason, gas_used, gas_refunded, output, logs, .. } => {
1044 (reason.into(), gas_refunded, gas_used, Some(output), logs)
1045 }
1046 ExecutionResult::Revert { gas_used, output } => {
1047 (InstructionResult::Revert, 0_u64, gas_used, Some(Output::Call(output)), vec![])
1049 }
1050 ExecutionResult::Halt { reason, gas_used } => {
1051 (reason.into(), 0_u64, gas_used, None, vec![])
1052 }
1053 };
1054 let gas = revm::interpreter::gas::calculate_initial_tx_gas(
1055 env.evm_env.cfg_env.spec,
1056 &env.tx.data,
1057 env.tx.kind.is_create(),
1058 env.tx.access_list.len().try_into()?,
1059 0,
1060 0,
1061 );
1062
1063 let result = match &out {
1064 Some(Output::Call(data)) => data.clone(),
1065 _ => Bytes::new(),
1066 };
1067
1068 let InspectorData {
1069 mut logs,
1070 labels,
1071 traces,
1072 line_coverage,
1073 edge_coverage,
1074 cheatcodes,
1075 chisel_state,
1076 reverter,
1077 } = inspector.collect();
1078
1079 if logs.is_empty() {
1080 logs = exec_logs;
1081 }
1082
1083 let transactions = cheatcodes
1084 .as_ref()
1085 .map(|c| c.broadcastable_transactions.clone())
1086 .filter(|txs| !txs.is_empty());
1087
1088 Ok(RawCallResult {
1089 exit_reason: Some(exit_reason),
1090 reverted: !matches!(exit_reason, return_ok!()),
1091 has_state_snapshot_failure,
1092 result,
1093 gas_used,
1094 gas_refunded,
1095 stipend: gas.initial_gas,
1096 logs,
1097 labels,
1098 traces,
1099 line_coverage,
1100 edge_coverage,
1101 transactions,
1102 state_changeset,
1103 env,
1104 cheatcodes,
1105 out,
1106 chisel_state,
1107 reverter,
1108 })
1109}
1110
1111pub struct FuzzTestTimer {
1113 inner: Option<(Instant, Duration)>,
1115}
1116
1117impl FuzzTestTimer {
1118 pub fn new(timeout: Option<u32>) -> Self {
1119 Self { inner: timeout.map(|timeout| (Instant::now(), Duration::from_secs(timeout.into()))) }
1120 }
1121
1122 pub fn is_enabled(&self) -> bool {
1124 self.inner.is_some()
1125 }
1126
1127 pub fn is_timed_out(&self) -> bool {
1129 self.inner.is_some_and(|(start, duration)| start.elapsed() > duration)
1130 }
1131}
1132
1133#[derive(Clone)]
1135pub struct FailFast {
1136 inner: Option<Arc<AtomicBool>>,
1139}
1140
1141impl FailFast {
1142 pub fn new(fail_fast: bool) -> Self {
1143 Self { inner: fail_fast.then_some(Arc::new(AtomicBool::new(false))) }
1144 }
1145
1146 pub fn is_enabled(&self) -> bool {
1148 self.inner.is_some()
1149 }
1150
1151 pub fn record_fail(&self) {
1153 if let Some(fail_fast) = &self.inner {
1154 fail_fast.store(true, Ordering::Relaxed);
1155 }
1156 }
1157
1158 pub fn should_stop(&self) -> bool {
1160 self.inner.as_ref().map(|flag| flag.load(Ordering::Relaxed)).unwrap_or(false)
1161 }
1162}