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: Arc<Backend>,
102 env: Env,
104 inspector: InspectorStack,
106 gas_limit: u64,
108 legacy_assertions: bool,
110}
111
112impl Executor {
113 #[inline]
115 pub fn builder() -> ExecutorBuilder {
116 ExecutorBuilder::new()
117 }
118
119 #[inline]
121 pub fn new(
122 mut backend: Backend,
123 env: Env,
124 inspector: InspectorStack,
125 gas_limit: u64,
126 legacy_assertions: bool,
127 ) -> Self {
128 backend.insert_account_info(
131 CHEATCODE_ADDRESS,
132 revm::state::AccountInfo {
133 code: Some(Bytecode::new_raw(Bytes::from_static(&[0]))),
134 code_hash: CHEATCODE_CONTRACT_HASH,
137 ..Default::default()
138 },
139 );
140
141 Self { backend: Arc::new(backend), env, inspector, gas_limit, legacy_assertions }
142 }
143
144 fn clone_with_backend(&self, backend: Backend) -> Self {
145 let env = Env::new_with_spec_id(
146 self.env.evm_env.cfg_env.clone(),
147 self.env.evm_env.block_env.clone(),
148 self.env.tx.clone(),
149 self.spec_id(),
150 );
151 Self {
152 backend: Arc::new(backend),
153 env,
154 inspector: self.inspector().clone(),
155 gas_limit: self.gas_limit,
156 legacy_assertions: self.legacy_assertions,
157 }
158 }
159
160 pub fn backend(&self) -> &Backend {
162 &self.backend
163 }
164
165 pub fn backend_mut(&mut self) -> &mut Backend {
170 Arc::make_mut(&mut self.backend)
171 }
172
173 pub fn env(&self) -> &Env {
175 &self.env
176 }
177
178 pub fn env_mut(&mut self) -> &mut Env {
180 &mut self.env
181 }
182
183 pub fn inspector(&self) -> &InspectorStack {
185 &self.inspector
186 }
187
188 pub fn inspector_mut(&mut self) -> &mut InspectorStack {
190 &mut self.inspector
191 }
192
193 pub fn spec_id(&self) -> SpecId {
195 self.env.evm_env.cfg_env.spec
196 }
197
198 pub fn set_spec_id(&mut self, spec_id: SpecId) {
200 self.env.evm_env.cfg_env.spec = spec_id;
201 }
202
203 pub fn gas_limit(&self) -> u64 {
208 self.gas_limit
209 }
210
211 pub fn set_gas_limit(&mut self, gas_limit: u64) {
213 self.gas_limit = gas_limit;
214 }
215
216 pub fn legacy_assertions(&self) -> bool {
219 self.legacy_assertions
220 }
221
222 pub fn set_legacy_assertions(&mut self, legacy_assertions: bool) {
225 self.legacy_assertions = legacy_assertions;
226 }
227
228 pub fn deploy_create2_deployer(&mut self) -> eyre::Result<()> {
230 trace!("deploying local create2 deployer");
231 let create2_deployer_account = self
232 .backend()
233 .basic_ref(DEFAULT_CREATE2_DEPLOYER)?
234 .ok_or_else(|| BackendError::MissingAccount(DEFAULT_CREATE2_DEPLOYER))?;
235
236 if create2_deployer_account.code.is_none_or(|code| code.is_empty()) {
238 let creator = DEFAULT_CREATE2_DEPLOYER_DEPLOYER;
239
240 let initial_balance = self.get_balance(creator)?;
242 self.set_balance(creator, U256::MAX)?;
243
244 let res =
245 self.deploy(creator, DEFAULT_CREATE2_DEPLOYER_CODE.into(), U256::ZERO, None)?;
246 trace!(create2=?res.address, "deployed local create2 deployer");
247
248 self.set_balance(creator, initial_balance)?;
249 }
250 Ok(())
251 }
252
253 pub fn set_balance(&mut self, address: Address, amount: U256) -> BackendResult<()> {
255 trace!(?address, ?amount, "setting account balance");
256 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
257 account.balance = amount;
258 self.backend_mut().insert_account_info(address, account);
259 Ok(())
260 }
261
262 pub fn get_balance(&self, address: Address) -> BackendResult<U256> {
264 Ok(self.backend().basic_ref(address)?.map(|acc| acc.balance).unwrap_or_default())
265 }
266
267 pub fn set_nonce(&mut self, address: Address, nonce: u64) -> BackendResult<()> {
269 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
270 account.nonce = nonce;
271 self.backend_mut().insert_account_info(address, account);
272 self.env_mut().tx.nonce = nonce;
273 Ok(())
274 }
275
276 pub fn get_nonce(&self, address: Address) -> BackendResult<u64> {
278 Ok(self.backend().basic_ref(address)?.map(|acc| acc.nonce).unwrap_or_default())
279 }
280
281 pub fn set_code(&mut self, address: Address, code: Bytecode) -> BackendResult<()> {
283 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
284 account.code_hash = keccak256(code.original_byte_slice());
285 account.code = Some(code);
286 self.backend_mut().insert_account_info(address, account);
287 Ok(())
288 }
289
290 pub fn set_storage(
292 &mut self,
293 address: Address,
294 storage: HashMap<U256, U256>,
295 ) -> BackendResult<()> {
296 self.backend_mut().replace_account_storage(address, storage)?;
297 Ok(())
298 }
299
300 pub fn set_storage_slot(
302 &mut self,
303 address: Address,
304 slot: U256,
305 value: U256,
306 ) -> BackendResult<()> {
307 self.backend_mut().insert_account_storage(address, slot, value)?;
308 Ok(())
309 }
310
311 pub fn is_empty_code(&self, address: Address) -> BackendResult<bool> {
313 Ok(self.backend().basic_ref(address)?.map(|acc| acc.is_empty_code_hash()).unwrap_or(true))
314 }
315
316 #[inline]
317 pub fn set_tracing(&mut self, mode: TraceMode) -> &mut Self {
318 self.inspector_mut().tracing(mode);
319 self
320 }
321
322 #[inline]
323 pub fn set_script_execution(&mut self, script_address: Address) {
324 self.inspector_mut().script(script_address);
325 }
326
327 #[inline]
328 pub fn set_trace_printer(&mut self, trace_printer: bool) -> &mut Self {
329 self.inspector_mut().print(trace_printer);
330 self
331 }
332
333 #[inline]
334 pub fn create2_deployer(&self) -> Address {
335 self.inspector().create2_deployer
336 }
337
338 pub fn deploy(
343 &mut self,
344 from: Address,
345 code: Bytes,
346 value: U256,
347 rd: Option<&RevertDecoder>,
348 ) -> Result<DeployResult, EvmError> {
349 let env = self.build_test_env(from, TxKind::Create, code, value);
350 self.deploy_with_env(env, rd)
351 }
352
353 #[instrument(name = "deploy", level = "debug", skip_all)]
360 pub fn deploy_with_env(
361 &mut self,
362 env: Env,
363 rd: Option<&RevertDecoder>,
364 ) -> Result<DeployResult, EvmError> {
365 assert!(
366 matches!(env.tx.kind, TxKind::Create),
367 "Expected create transaction, got {:?}",
368 env.tx.kind
369 );
370 trace!(sender=%env.tx.caller, "deploying contract");
371
372 let mut result = self.transact_with_env(env)?;
373 result = result.into_result(rd)?;
374 let Some(Output::Create(_, Some(address))) = result.out else {
375 panic!("Deployment succeeded, but no address was returned: {result:#?}");
376 };
377
378 self.backend_mut().add_persistent_account(address);
381
382 trace!(%address, "deployed contract");
383
384 Ok(DeployResult { raw: result, address })
385 }
386
387 #[instrument(name = "setup", level = "debug", skip_all)]
394 pub fn setup(
395 &mut self,
396 from: Option<Address>,
397 to: Address,
398 rd: Option<&RevertDecoder>,
399 ) -> Result<RawCallResult, EvmError> {
400 trace!(?from, ?to, "setting up contract");
401
402 let from = from.unwrap_or(CALLER);
403 self.backend_mut().set_test_contract(to).set_caller(from);
404 let calldata = Bytes::from_static(&ITest::setUpCall::SELECTOR);
405 let mut res = self.transact_raw(from, to, calldata, U256::ZERO)?;
406 res = res.into_result(rd)?;
407
408 self.env_mut().evm_env.block_env = res.env.evm_env.block_env.clone();
410 self.env_mut().evm_env.cfg_env.chain_id = res.env.evm_env.cfg_env.chain_id;
412
413 let success =
414 self.is_raw_call_success(to, Cow::Borrowed(&res.state_changeset), &res, false);
415 if !success {
416 return Err(res.into_execution_error("execution error".to_string()).into());
417 }
418
419 Ok(res)
420 }
421
422 pub fn call(
424 &self,
425 from: Address,
426 to: Address,
427 func: &Function,
428 args: &[DynSolValue],
429 value: U256,
430 rd: Option<&RevertDecoder>,
431 ) -> Result<CallResult, EvmError> {
432 let calldata = Bytes::from(func.abi_encode_input(args)?);
433 let result = self.call_raw(from, to, calldata, value)?;
434 result.into_decoded_result(func, rd)
435 }
436
437 pub fn call_sol<C: SolCall>(
439 &self,
440 from: Address,
441 to: Address,
442 args: &C,
443 value: U256,
444 rd: Option<&RevertDecoder>,
445 ) -> Result<CallResult<C::Return>, EvmError> {
446 let calldata = Bytes::from(args.abi_encode());
447 let mut raw = self.call_raw(from, to, calldata, value)?;
448 raw = raw.into_result(rd)?;
449 Ok(CallResult { decoded_result: C::abi_decode_returns(&raw.result)?, raw })
450 }
451
452 pub fn transact(
454 &mut self,
455 from: Address,
456 to: Address,
457 func: &Function,
458 args: &[DynSolValue],
459 value: U256,
460 rd: Option<&RevertDecoder>,
461 ) -> Result<CallResult, EvmError> {
462 let calldata = Bytes::from(func.abi_encode_input(args)?);
463 let result = self.transact_raw(from, to, calldata, value)?;
464 result.into_decoded_result(func, rd)
465 }
466
467 pub fn call_raw(
469 &self,
470 from: Address,
471 to: Address,
472 calldata: Bytes,
473 value: U256,
474 ) -> eyre::Result<RawCallResult> {
475 let env = self.build_test_env(from, TxKind::Call(to), calldata, value);
476 self.call_with_env(env)
477 }
478
479 pub fn call_raw_with_authorization(
482 &mut self,
483 from: Address,
484 to: Address,
485 calldata: Bytes,
486 value: U256,
487 authorization_list: Vec<SignedAuthorization>,
488 ) -> eyre::Result<RawCallResult> {
489 let mut env = self.build_test_env(from, to.into(), calldata, value);
490 env.tx.set_signed_authorization(authorization_list);
491 env.tx.tx_type = 4;
492 self.call_with_env(env)
493 }
494
495 pub fn transact_raw(
497 &mut self,
498 from: Address,
499 to: Address,
500 calldata: Bytes,
501 value: U256,
502 ) -> eyre::Result<RawCallResult> {
503 let env = self.build_test_env(from, TxKind::Call(to), calldata, value);
504 self.transact_with_env(env)
505 }
506
507 pub fn transact_raw_with_authorization(
510 &mut self,
511 from: Address,
512 to: Address,
513 calldata: Bytes,
514 value: U256,
515 authorization_list: Vec<SignedAuthorization>,
516 ) -> eyre::Result<RawCallResult> {
517 let mut env = self.build_test_env(from, TxKind::Call(to), calldata, value);
518 env.tx.set_signed_authorization(authorization_list);
519 env.tx.tx_type = 4;
520 self.transact_with_env(env)
521 }
522
523 #[instrument(name = "call", level = "debug", skip_all)]
527 pub fn call_with_env(&self, mut env: Env) -> eyre::Result<RawCallResult> {
528 let mut stack = self.inspector().clone();
529 let mut backend = CowBackend::new_borrowed(self.backend());
530 let result = backend.inspect(&mut env, stack.as_inspector())?;
531 convert_executed_result(env, stack, result, backend.has_state_snapshot_failure())
532 }
533
534 #[instrument(name = "transact", level = "debug", skip_all)]
536 pub fn transact_with_env(&mut self, mut env: Env) -> eyre::Result<RawCallResult> {
537 let mut stack = self.inspector().clone();
538 let backend = self.backend_mut();
539 let result = backend.inspect(&mut env, stack.as_inspector())?;
540 let mut result =
541 convert_executed_result(env, stack, result, backend.has_state_snapshot_failure())?;
542 self.commit(&mut result);
543 Ok(result)
544 }
545
546 #[instrument(name = "commit", level = "debug", skip_all)]
551 fn commit(&mut self, result: &mut RawCallResult) {
552 self.backend_mut().commit(result.state_changeset.clone());
554
555 self.inspector_mut().cheatcodes = result.cheatcodes.take();
557 if let Some(cheats) = self.inspector_mut().cheatcodes.as_mut() {
558 cheats.broadcastable_transactions.clear();
560 cheats.ignored_traces.ignored.clear();
561
562 if let Some(last_pause_call) = cheats.ignored_traces.last_pause_call.as_mut() {
565 *last_pause_call = (0, 0);
566 }
567 }
568
569 self.inspector_mut().set_env(&result.env);
571 }
572
573 pub fn is_raw_call_mut_success(
578 &self,
579 address: Address,
580 call_result: &mut RawCallResult,
581 should_fail: bool,
582 ) -> bool {
583 self.is_raw_call_success(
584 address,
585 Cow::Owned(std::mem::take(&mut call_result.state_changeset)),
586 call_result,
587 should_fail,
588 )
589 }
590
591 pub fn is_raw_call_success(
595 &self,
596 address: Address,
597 state_changeset: Cow<'_, StateChangeset>,
598 call_result: &RawCallResult,
599 should_fail: bool,
600 ) -> bool {
601 if call_result.has_state_snapshot_failure {
602 return should_fail;
604 }
605 self.is_success(address, call_result.reverted, state_changeset, should_fail)
606 }
607
608 pub fn is_success(
630 &self,
631 address: Address,
632 reverted: bool,
633 state_changeset: Cow<'_, StateChangeset>,
634 should_fail: bool,
635 ) -> bool {
636 let success = self.is_success_raw(address, reverted, state_changeset);
637 should_fail ^ success
638 }
639
640 #[instrument(name = "is_success", level = "debug", skip_all)]
641 fn is_success_raw(
642 &self,
643 address: Address,
644 reverted: bool,
645 state_changeset: Cow<'_, StateChangeset>,
646 ) -> bool {
647 if reverted {
649 return false;
650 }
651
652 if self.backend().has_state_snapshot_failure() {
654 return false;
655 }
656
657 if let Some(acc) = state_changeset.get(&CHEATCODE_ADDRESS)
659 && let Some(failed_slot) = acc.storage.get(&GLOBAL_FAIL_SLOT)
660 && !failed_slot.present_value().is_zero()
661 {
662 return false;
663 }
664 if let Ok(failed_slot) = self.backend().storage_ref(CHEATCODE_ADDRESS, GLOBAL_FAIL_SLOT)
665 && !failed_slot.is_zero()
666 {
667 return false;
668 }
669
670 if !self.legacy_assertions {
671 return true;
672 }
673
674 {
676 let mut backend = self.backend().clone_empty();
678
679 for address in [address, CHEATCODE_ADDRESS] {
682 let Ok(acc) = self.backend().basic_ref(address) else { return false };
683 backend.insert_account_info(address, acc.unwrap_or_default());
684 }
685
686 backend.commit(state_changeset.into_owned());
691
692 let executor = self.clone_with_backend(backend);
694 let call = executor.call_sol(CALLER, address, &ITest::failedCall {}, U256::ZERO, None);
695 match call {
696 Ok(CallResult { raw: _, decoded_result: failed }) => {
697 trace!(failed, "DSTest::failed()");
698 !failed
699 }
700 Err(err) => {
701 trace!(%err, "failed to call DSTest::failed()");
702 true
703 }
704 }
705 }
706 }
707
708 fn build_test_env(&self, caller: Address, kind: TxKind, data: Bytes, value: U256) -> Env {
713 Env {
714 evm_env: EvmEnv {
715 cfg_env: {
716 let mut cfg = self.env().evm_env.cfg_env.clone();
717 cfg.spec = self.spec_id();
718 cfg
719 },
720 block_env: BlockEnv {
724 basefee: 0,
725 gas_limit: self.gas_limit,
726 ..self.env().evm_env.block_env.clone()
727 },
728 },
729 tx: TxEnv {
730 caller,
731 kind,
732 data,
733 value,
734 gas_price: 0,
736 gas_priority_fee: None,
737 gas_limit: self.gas_limit,
738 chain_id: Some(self.env().evm_env.cfg_env.chain_id),
739 ..self.env().tx.clone()
740 },
741 }
742 }
743
744 pub fn call_sol_default<C: SolCall>(&self, to: Address, args: &C) -> C::Return
745 where
746 C::Return: Default,
747 {
748 self.call_sol(CALLER, to, args, U256::ZERO, None)
749 .map(|c| c.decoded_result)
750 .inspect_err(|e| warn!(target: "forge::test", "failed calling {:?}: {e}", C::SIGNATURE))
751 .unwrap_or_default()
752 }
753}
754
755#[derive(Debug, thiserror::Error)]
757#[error("execution reverted: {reason} (gas: {})", raw.gas_used)]
758pub struct ExecutionErr {
759 pub raw: RawCallResult,
761 pub reason: String,
763}
764
765impl std::ops::Deref for ExecutionErr {
766 type Target = RawCallResult;
767
768 #[inline]
769 fn deref(&self) -> &Self::Target {
770 &self.raw
771 }
772}
773
774impl std::ops::DerefMut for ExecutionErr {
775 #[inline]
776 fn deref_mut(&mut self) -> &mut Self::Target {
777 &mut self.raw
778 }
779}
780
781#[derive(Debug, thiserror::Error)]
782pub enum EvmError {
783 #[error(transparent)]
785 Execution(#[from] Box<ExecutionErr>),
786 #[error(transparent)]
788 Abi(#[from] alloy_dyn_abi::Error),
789 #[error("{0}")]
791 Skip(SkipReason),
792 #[error("{0}")]
794 Eyre(
795 #[from]
796 #[source]
797 eyre::Report,
798 ),
799}
800
801impl From<ExecutionErr> for EvmError {
802 fn from(err: ExecutionErr) -> Self {
803 Self::Execution(Box::new(err))
804 }
805}
806
807impl From<alloy_sol_types::Error> for EvmError {
808 fn from(err: alloy_sol_types::Error) -> Self {
809 Self::Abi(err.into())
810 }
811}
812
813#[derive(Debug)]
815pub struct DeployResult {
816 pub raw: RawCallResult,
818 pub address: Address,
820}
821
822impl std::ops::Deref for DeployResult {
823 type Target = RawCallResult;
824
825 #[inline]
826 fn deref(&self) -> &Self::Target {
827 &self.raw
828 }
829}
830
831impl std::ops::DerefMut for DeployResult {
832 #[inline]
833 fn deref_mut(&mut self) -> &mut Self::Target {
834 &mut self.raw
835 }
836}
837
838impl From<DeployResult> for RawCallResult {
839 fn from(d: DeployResult) -> Self {
840 d.raw
841 }
842}
843
844#[derive(Debug)]
846pub struct RawCallResult {
847 pub exit_reason: Option<InstructionResult>,
849 pub reverted: bool,
851 pub has_state_snapshot_failure: bool,
856 pub result: Bytes,
858 pub gas_used: u64,
860 pub gas_refunded: u64,
862 pub stipend: u64,
864 pub logs: Vec<Log>,
866 pub labels: AddressHashMap<String>,
868 pub traces: Option<SparsedTraceArena>,
870 pub line_coverage: Option<HitMaps>,
872 pub edge_coverage: Option<Vec<u8>>,
874 pub transactions: Option<BroadcastableTransactions>,
876 pub state_changeset: StateChangeset,
878 pub env: Env,
880 pub cheatcodes: Option<Box<Cheatcodes>>,
882 pub out: Option<Output>,
884 pub chisel_state: Option<(Vec<U256>, Vec<u8>)>,
886 pub reverter: Option<Address>,
887}
888
889impl Default for RawCallResult {
890 fn default() -> Self {
891 Self {
892 exit_reason: None,
893 reverted: false,
894 has_state_snapshot_failure: false,
895 result: Bytes::new(),
896 gas_used: 0,
897 gas_refunded: 0,
898 stipend: 0,
899 logs: Vec::new(),
900 labels: HashMap::default(),
901 traces: None,
902 line_coverage: None,
903 edge_coverage: None,
904 transactions: None,
905 state_changeset: HashMap::default(),
906 env: Env::default(),
907 cheatcodes: Default::default(),
908 out: None,
909 chisel_state: None,
910 reverter: None,
911 }
912 }
913}
914
915impl RawCallResult {
916 pub fn from_evm_result(r: Result<Self, EvmError>) -> eyre::Result<(Self, Option<String>)> {
918 match r {
919 Ok(r) => Ok((r, None)),
920 Err(EvmError::Execution(e)) => Ok((e.raw, Some(e.reason))),
921 Err(e) => Err(e.into()),
922 }
923 }
924
925 pub fn into_evm_error(self, rd: Option<&RevertDecoder>) -> EvmError {
927 if let Some(reason) = SkipReason::decode(&self.result) {
928 return EvmError::Skip(reason);
929 }
930 let reason = rd.unwrap_or_default().decode(&self.result, self.exit_reason);
931 EvmError::Execution(Box::new(self.into_execution_error(reason)))
932 }
933
934 pub fn into_execution_error(self, reason: String) -> ExecutionErr {
936 ExecutionErr { raw: self, reason }
937 }
938
939 pub fn into_result(self, rd: Option<&RevertDecoder>) -> Result<Self, EvmError> {
941 if let Some(reason) = self.exit_reason
942 && reason.is_ok()
943 {
944 Ok(self)
945 } else {
946 Err(self.into_evm_error(rd))
947 }
948 }
949
950 pub fn into_decoded_result(
952 mut self,
953 func: &Function,
954 rd: Option<&RevertDecoder>,
955 ) -> Result<CallResult, EvmError> {
956 self = self.into_result(rd)?;
957 let mut result = func.abi_decode_output(&self.result)?;
958 let decoded_result = if result.len() == 1 {
959 result.pop().unwrap()
960 } else {
961 DynSolValue::Tuple(result)
963 };
964 Ok(CallResult { raw: self, decoded_result })
965 }
966
967 pub fn transactions(&self) -> Option<&BroadcastableTransactions> {
969 self.cheatcodes.as_ref().map(|c| &c.broadcastable_transactions)
970 }
971
972 pub fn merge_edge_coverage(&mut self, history_map: &mut [u8]) -> (bool, bool) {
975 let mut new_coverage = false;
976 let mut is_edge = false;
977 if let Some(x) = &mut self.edge_coverage {
978 for (curr, hist) in std::iter::zip(x, history_map) {
981 if *curr > 0 {
983 let bucket = match *curr {
985 0 => 0,
986 1 => 1,
987 2 => 2,
988 3 => 4,
989 4..=7 => 8,
990 8..=15 => 16,
991 16..=31 => 32,
992 32..=127 => 64,
993 128..=255 => 128,
994 };
995
996 if *hist < bucket {
998 if *hist == 0 {
999 is_edge = true;
1001 }
1002 *hist = bucket;
1003 new_coverage = true;
1004 }
1005
1006 *curr = 0;
1008 }
1009 }
1010 }
1011 (new_coverage, is_edge)
1012 }
1013}
1014
1015pub struct CallResult<T = DynSolValue> {
1017 pub raw: RawCallResult,
1019 pub decoded_result: T,
1021}
1022
1023impl std::ops::Deref for CallResult {
1024 type Target = RawCallResult;
1025
1026 #[inline]
1027 fn deref(&self) -> &Self::Target {
1028 &self.raw
1029 }
1030}
1031
1032impl std::ops::DerefMut for CallResult {
1033 #[inline]
1034 fn deref_mut(&mut self) -> &mut Self::Target {
1035 &mut self.raw
1036 }
1037}
1038
1039fn convert_executed_result(
1041 env: Env,
1042 inspector: InspectorStack,
1043 ResultAndState { result, state: state_changeset }: ResultAndState,
1044 has_state_snapshot_failure: bool,
1045) -> eyre::Result<RawCallResult> {
1046 let (exit_reason, gas_refunded, gas_used, out, exec_logs) = match result {
1047 ExecutionResult::Success { reason, gas_used, gas_refunded, output, logs, .. } => {
1048 (reason.into(), gas_refunded, gas_used, Some(output), logs)
1049 }
1050 ExecutionResult::Revert { gas_used, output } => {
1051 (InstructionResult::Revert, 0_u64, gas_used, Some(Output::Call(output)), vec![])
1053 }
1054 ExecutionResult::Halt { reason, gas_used } => {
1055 (reason.into(), 0_u64, gas_used, None, vec![])
1056 }
1057 };
1058 let gas = revm::interpreter::gas::calculate_initial_tx_gas(
1059 env.evm_env.cfg_env.spec,
1060 &env.tx.data,
1061 env.tx.kind.is_create(),
1062 env.tx.access_list.len().try_into()?,
1063 0,
1064 0,
1065 );
1066
1067 let result = match &out {
1068 Some(Output::Call(data)) => data.clone(),
1069 _ => Bytes::new(),
1070 };
1071
1072 let InspectorData {
1073 mut logs,
1074 labels,
1075 traces,
1076 line_coverage,
1077 edge_coverage,
1078 cheatcodes,
1079 chisel_state,
1080 reverter,
1081 } = inspector.collect();
1082
1083 if logs.is_empty() {
1084 logs = exec_logs;
1085 }
1086
1087 let transactions = cheatcodes
1088 .as_ref()
1089 .map(|c| c.broadcastable_transactions.clone())
1090 .filter(|txs| !txs.is_empty());
1091
1092 Ok(RawCallResult {
1093 exit_reason: Some(exit_reason),
1094 reverted: !matches!(exit_reason, return_ok!()),
1095 has_state_snapshot_failure,
1096 result,
1097 gas_used,
1098 gas_refunded,
1099 stipend: gas.initial_gas,
1100 logs,
1101 labels,
1102 traces,
1103 line_coverage,
1104 edge_coverage,
1105 transactions,
1106 state_changeset,
1107 env,
1108 cheatcodes,
1109 out,
1110 chisel_state,
1111 reverter,
1112 })
1113}
1114
1115pub struct FuzzTestTimer {
1117 inner: Option<(Instant, Duration)>,
1119}
1120
1121impl FuzzTestTimer {
1122 pub fn new(timeout: Option<u32>) -> Self {
1123 Self { inner: timeout.map(|timeout| (Instant::now(), Duration::from_secs(timeout.into()))) }
1124 }
1125
1126 pub fn is_enabled(&self) -> bool {
1128 self.inner.is_some()
1129 }
1130
1131 pub fn is_timed_out(&self) -> bool {
1133 self.inner.is_some_and(|(start, duration)| start.elapsed() > duration)
1134 }
1135}
1136
1137#[derive(Clone, Debug)]
1140pub struct EarlyExit {
1141 inner: Arc<AtomicBool>,
1143 fail_fast: bool,
1145}
1146
1147impl EarlyExit {
1148 pub fn new(fail_fast: bool) -> Self {
1149 Self { inner: Arc::new(AtomicBool::new(false)), fail_fast }
1150 }
1151
1152 pub fn record_failure(&self) {
1154 if self.fail_fast {
1155 self.inner.store(true, Ordering::Relaxed);
1156 }
1157 }
1158
1159 pub fn record_ctrl_c(&self) {
1161 self.inner.store(true, Ordering::Relaxed);
1162 }
1163
1164 pub fn should_stop(&self) -> bool {
1166 self.inner.load(Ordering::Relaxed)
1167 }
1168}