1use crate::inspectors::{
10 cheatcodes::BroadcastableTransactions, Cheatcodes, InspectorData, InspectorStack,
11};
12use alloy_dyn_abi::{DynSolValue, FunctionExt, JsonAbiExt};
13use alloy_json_abi::Function;
14use alloy_primitives::{
15 map::{AddressHashMap, HashMap},
16 Address, Bytes, Log, U256,
17};
18use alloy_sol_types::{sol, SolCall};
19use foundry_evm_core::{
20 backend::{Backend, BackendError, BackendResult, CowBackend, DatabaseExt, GLOBAL_FAIL_SLOT},
21 constants::{
22 CALLER, CHEATCODE_ADDRESS, CHEATCODE_CONTRACT_HASH, DEFAULT_CREATE2_DEPLOYER,
23 DEFAULT_CREATE2_DEPLOYER_CODE, DEFAULT_CREATE2_DEPLOYER_DEPLOYER,
24 },
25 decode::{RevertDecoder, SkipReason},
26 utils::StateChangeset,
27 InspectorExt,
28};
29use foundry_evm_coverage::HitMaps;
30use foundry_evm_traces::{SparsedTraceArena, TraceMode};
31use revm::{
32 db::{DatabaseCommit, DatabaseRef},
33 interpreter::{return_ok, InstructionResult},
34 primitives::{
35 AuthorizationList, BlockEnv, Bytecode, Env, EnvWithHandlerCfg, ExecutionResult, Output,
36 ResultAndState, SignedAuthorization, SpecId, TxEnv, TxKind,
37 },
38};
39use std::{
40 borrow::Cow,
41 time::{Duration, Instant},
42};
43
44mod builder;
45pub use builder::ExecutorBuilder;
46
47pub mod fuzz;
48pub use fuzz::FuzzedExecutor;
49
50pub mod invariant;
51pub use invariant::InvariantExecutor;
52
53mod trace;
54pub use trace::TracingExecutor;
55
56sol! {
57 interface ITest {
58 function setUp() external;
59 function failed() external view returns (bool failed);
60
61 #[derive(Default)]
62 function beforeTestSetup(bytes4 testSelector) public view returns (bytes[] memory beforeTestCalldata);
63 }
64}
65
66#[derive(Clone, Debug)]
78pub struct Executor {
79 backend: Backend,
85 env: EnvWithHandlerCfg,
87 inspector: InspectorStack,
89 gas_limit: u64,
91 legacy_assertions: bool,
93}
94
95impl Executor {
96 #[inline]
98 pub fn builder() -> ExecutorBuilder {
99 ExecutorBuilder::new()
100 }
101
102 #[inline]
104 pub fn new(
105 mut backend: Backend,
106 env: EnvWithHandlerCfg,
107 inspector: InspectorStack,
108 gas_limit: u64,
109 legacy_assertions: bool,
110 ) -> Self {
111 backend.insert_account_info(
114 CHEATCODE_ADDRESS,
115 revm::primitives::AccountInfo {
116 code: Some(Bytecode::new_raw(Bytes::from_static(&[0]))),
117 code_hash: CHEATCODE_CONTRACT_HASH,
120 ..Default::default()
121 },
122 );
123
124 Self { backend, env, inspector, gas_limit, legacy_assertions }
125 }
126
127 fn clone_with_backend(&self, backend: Backend) -> Self {
128 let env = EnvWithHandlerCfg::new_with_spec_id(Box::new(self.env().clone()), self.spec_id());
129 Self::new(backend, env, self.inspector().clone(), self.gas_limit, self.legacy_assertions)
130 }
131
132 pub fn backend(&self) -> &Backend {
134 &self.backend
135 }
136
137 pub fn backend_mut(&mut self) -> &mut Backend {
139 &mut self.backend
140 }
141
142 pub fn env(&self) -> &Env {
144 &self.env.env
145 }
146
147 pub fn env_mut(&mut self) -> &mut Env {
149 &mut self.env.env
150 }
151
152 pub fn inspector(&self) -> &InspectorStack {
154 &self.inspector
155 }
156
157 pub fn inspector_mut(&mut self) -> &mut InspectorStack {
159 &mut self.inspector
160 }
161
162 pub fn spec_id(&self) -> SpecId {
164 self.env.spec_id()
165 }
166
167 pub fn set_spec_id(&mut self, spec_id: SpecId) {
169 self.env.handler_cfg.spec_id = spec_id;
170 }
171
172 pub fn gas_limit(&self) -> u64 {
177 self.gas_limit
178 }
179
180 pub fn set_gas_limit(&mut self, gas_limit: u64) {
182 self.gas_limit = gas_limit;
183 }
184
185 pub fn legacy_assertions(&self) -> bool {
188 self.legacy_assertions
189 }
190
191 pub fn set_legacy_assertions(&mut self, legacy_assertions: bool) {
194 self.legacy_assertions = legacy_assertions;
195 }
196
197 pub fn deploy_create2_deployer(&mut self) -> eyre::Result<()> {
199 trace!("deploying local create2 deployer");
200 let create2_deployer_account = self
201 .backend()
202 .basic_ref(DEFAULT_CREATE2_DEPLOYER)?
203 .ok_or_else(|| BackendError::MissingAccount(DEFAULT_CREATE2_DEPLOYER))?;
204
205 if create2_deployer_account.code.is_none_or(|code| code.is_empty()) {
207 let creator = DEFAULT_CREATE2_DEPLOYER_DEPLOYER;
208
209 let initial_balance = self.get_balance(creator)?;
211 self.set_balance(creator, U256::MAX)?;
212
213 let res =
214 self.deploy(creator, DEFAULT_CREATE2_DEPLOYER_CODE.into(), U256::ZERO, None)?;
215 trace!(create2=?res.address, "deployed local create2 deployer");
216
217 self.set_balance(creator, initial_balance)?;
218 }
219 Ok(())
220 }
221
222 pub fn set_balance(&mut self, address: Address, amount: U256) -> BackendResult<()> {
224 trace!(?address, ?amount, "setting account balance");
225 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
226 account.balance = amount;
227 self.backend_mut().insert_account_info(address, account);
228 Ok(())
229 }
230
231 pub fn get_balance(&self, address: Address) -> BackendResult<U256> {
233 Ok(self.backend().basic_ref(address)?.map(|acc| acc.balance).unwrap_or_default())
234 }
235
236 pub fn set_nonce(&mut self, address: Address, nonce: u64) -> BackendResult<()> {
238 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
239 account.nonce = nonce;
240 self.backend_mut().insert_account_info(address, account);
241 Ok(())
242 }
243
244 pub fn get_nonce(&self, address: Address) -> BackendResult<u64> {
246 Ok(self.backend().basic_ref(address)?.map(|acc| acc.nonce).unwrap_or_default())
247 }
248
249 pub fn is_empty_code(&self, address: Address) -> BackendResult<bool> {
251 Ok(self.backend().basic_ref(address)?.map(|acc| acc.is_empty_code_hash()).unwrap_or(true))
252 }
253
254 #[inline]
255 pub fn set_tracing(&mut self, mode: TraceMode) -> &mut Self {
256 self.inspector_mut().tracing(mode);
257 self
258 }
259
260 #[inline]
261 pub fn set_trace_printer(&mut self, trace_printer: bool) -> &mut Self {
262 self.inspector_mut().print(trace_printer);
263 self
264 }
265
266 #[inline]
267 pub fn create2_deployer(&self) -> Address {
268 self.inspector().create2_deployer()
269 }
270
271 pub fn deploy(
276 &mut self,
277 from: Address,
278 code: Bytes,
279 value: U256,
280 rd: Option<&RevertDecoder>,
281 ) -> Result<DeployResult, EvmError> {
282 let env = self.build_test_env(from, TxKind::Create, code, value);
283 self.deploy_with_env(env, rd)
284 }
285
286 #[instrument(name = "deploy", level = "debug", skip_all)]
293 pub fn deploy_with_env(
294 &mut self,
295 env: EnvWithHandlerCfg,
296 rd: Option<&RevertDecoder>,
297 ) -> Result<DeployResult, EvmError> {
298 assert!(
299 matches!(env.tx.transact_to, TxKind::Create),
300 "Expected create transaction, got {:?}",
301 env.tx.transact_to
302 );
303 trace!(sender=%env.tx.caller, "deploying contract");
304
305 let mut result = self.transact_with_env(env)?;
306 result = result.into_result(rd)?;
307 let Some(Output::Create(_, Some(address))) = result.out else {
308 panic!("Deployment succeeded, but no address was returned: {result:#?}");
309 };
310
311 self.backend_mut().add_persistent_account(address);
314
315 debug!(%address, "deployed contract");
316
317 Ok(DeployResult { raw: result, address })
318 }
319
320 #[instrument(name = "setup", level = "debug", skip_all)]
327 pub fn setup(
328 &mut self,
329 from: Option<Address>,
330 to: Address,
331 rd: Option<&RevertDecoder>,
332 ) -> Result<RawCallResult, EvmError> {
333 trace!(?from, ?to, "setting up contract");
334
335 let from = from.unwrap_or(CALLER);
336 self.backend_mut().set_test_contract(to).set_caller(from);
337 let calldata = Bytes::from_static(&ITest::setUpCall::SELECTOR);
338 let mut res = self.transact_raw(from, to, calldata, U256::ZERO)?;
339 res = res.into_result(rd)?;
340
341 self.env_mut().block = res.env.block.clone();
343 self.env_mut().cfg.chain_id = res.env.cfg.chain_id;
345
346 let success =
347 self.is_raw_call_success(to, Cow::Borrowed(&res.state_changeset), &res, false);
348 if !success {
349 return Err(res.into_execution_error("execution error".to_string()).into());
350 }
351
352 Ok(res)
353 }
354
355 pub fn call(
357 &self,
358 from: Address,
359 to: Address,
360 func: &Function,
361 args: &[DynSolValue],
362 value: U256,
363 rd: Option<&RevertDecoder>,
364 ) -> Result<CallResult, EvmError> {
365 let calldata = Bytes::from(func.abi_encode_input(args)?);
366 let result = self.call_raw(from, to, calldata, value)?;
367 result.into_decoded_result(func, rd)
368 }
369
370 pub fn call_sol<C: SolCall>(
372 &self,
373 from: Address,
374 to: Address,
375 args: &C,
376 value: U256,
377 rd: Option<&RevertDecoder>,
378 ) -> Result<CallResult<C::Return>, EvmError> {
379 let calldata = Bytes::from(args.abi_encode());
380 let mut raw = self.call_raw(from, to, calldata, value)?;
381 raw = raw.into_result(rd)?;
382 Ok(CallResult { decoded_result: C::abi_decode_returns(&raw.result, false)?, raw })
383 }
384
385 pub fn transact(
387 &mut self,
388 from: Address,
389 to: Address,
390 func: &Function,
391 args: &[DynSolValue],
392 value: U256,
393 rd: Option<&RevertDecoder>,
394 ) -> Result<CallResult, EvmError> {
395 let calldata = Bytes::from(func.abi_encode_input(args)?);
396 let result = self.transact_raw(from, to, calldata, value)?;
397 result.into_decoded_result(func, rd)
398 }
399
400 pub fn call_raw(
402 &self,
403 from: Address,
404 to: Address,
405 calldata: Bytes,
406 value: U256,
407 ) -> eyre::Result<RawCallResult> {
408 let env = self.build_test_env(from, TxKind::Call(to), calldata, value);
409 self.call_with_env(env)
410 }
411
412 pub fn call_raw_with_authorization(
415 &mut self,
416 from: Address,
417 to: Address,
418 calldata: Bytes,
419 value: U256,
420 authorization_list: Vec<SignedAuthorization>,
421 ) -> eyre::Result<RawCallResult> {
422 let mut env = self.build_test_env(from, to.into(), calldata, value);
423 env.tx.authorization_list = Some(AuthorizationList::Signed(authorization_list));
424 self.call_with_env(env)
425 }
426
427 pub fn transact_raw(
429 &mut self,
430 from: Address,
431 to: Address,
432 calldata: Bytes,
433 value: U256,
434 ) -> eyre::Result<RawCallResult> {
435 let env = self.build_test_env(from, TxKind::Call(to), calldata, value);
436 self.transact_with_env(env)
437 }
438
439 #[instrument(name = "call", level = "debug", skip_all)]
443 pub fn call_with_env(&self, mut env: EnvWithHandlerCfg) -> eyre::Result<RawCallResult> {
444 let mut inspector = self.inspector().clone();
445 let mut backend = CowBackend::new_borrowed(self.backend());
446 let result = backend.inspect(&mut env, &mut inspector)?;
447 convert_executed_result(env, inspector, result, backend.has_state_snapshot_failure())
448 }
449
450 #[instrument(name = "transact", level = "debug", skip_all)]
452 pub fn transact_with_env(&mut self, mut env: EnvWithHandlerCfg) -> eyre::Result<RawCallResult> {
453 let mut inspector = self.inspector().clone();
454 let backend = self.backend_mut();
455 let result = backend.inspect(&mut env, &mut inspector)?;
456 let mut result =
457 convert_executed_result(env, inspector, result, backend.has_state_snapshot_failure())?;
458 self.commit(&mut result);
459 Ok(result)
460 }
461
462 #[instrument(name = "commit", level = "debug", skip_all)]
467 fn commit(&mut self, result: &mut RawCallResult) {
468 self.backend_mut().commit(result.state_changeset.clone());
470
471 self.inspector_mut().cheatcodes = result.cheatcodes.take();
473 if let Some(cheats) = self.inspector_mut().cheatcodes.as_mut() {
474 cheats.broadcastable_transactions.clear();
476 cheats.ignored_traces.ignored.clear();
477
478 if let Some(last_pause_call) = cheats.ignored_traces.last_pause_call.as_mut() {
481 *last_pause_call = (0, 0);
482 }
483 }
484
485 self.inspector_mut().set_env(&result.env);
487 }
488
489 pub fn is_raw_call_mut_success(
494 &self,
495 address: Address,
496 call_result: &mut RawCallResult,
497 should_fail: bool,
498 ) -> bool {
499 self.is_raw_call_success(
500 address,
501 Cow::Owned(std::mem::take(&mut call_result.state_changeset)),
502 call_result,
503 should_fail,
504 )
505 }
506
507 pub fn is_raw_call_success(
511 &self,
512 address: Address,
513 state_changeset: Cow<'_, StateChangeset>,
514 call_result: &RawCallResult,
515 should_fail: bool,
516 ) -> bool {
517 if call_result.has_state_snapshot_failure {
518 return should_fail;
520 }
521 self.is_success(address, call_result.reverted, state_changeset, should_fail)
522 }
523
524 pub fn is_success(
546 &self,
547 address: Address,
548 reverted: bool,
549 state_changeset: Cow<'_, StateChangeset>,
550 should_fail: bool,
551 ) -> bool {
552 let success = self.is_success_raw(address, reverted, state_changeset);
553 should_fail ^ success
554 }
555
556 #[instrument(name = "is_success", level = "debug", skip_all)]
557 fn is_success_raw(
558 &self,
559 address: Address,
560 reverted: bool,
561 state_changeset: Cow<'_, StateChangeset>,
562 ) -> bool {
563 if reverted {
565 return false;
566 }
567
568 if self.backend().has_state_snapshot_failure() {
570 return false;
571 }
572
573 if let Some(acc) = state_changeset.get(&CHEATCODE_ADDRESS) {
575 if let Some(failed_slot) = acc.storage.get(&GLOBAL_FAIL_SLOT) {
576 if !failed_slot.present_value().is_zero() {
577 return false;
578 }
579 }
580 }
581 if let Ok(failed_slot) = self.backend().storage_ref(CHEATCODE_ADDRESS, GLOBAL_FAIL_SLOT) {
582 if !failed_slot.is_zero() {
583 return false;
584 }
585 }
586
587 if !self.legacy_assertions {
588 return true;
589 }
590
591 {
593 let mut backend = self.backend().clone_empty();
595
596 for address in [address, CHEATCODE_ADDRESS] {
599 let Ok(acc) = self.backend().basic_ref(address) else { return false };
600 backend.insert_account_info(address, acc.unwrap_or_default());
601 }
602
603 backend.commit(state_changeset.into_owned());
608
609 let executor = self.clone_with_backend(backend);
611 let call = executor.call_sol(CALLER, address, &ITest::failedCall {}, U256::ZERO, None);
612 match call {
613 Ok(CallResult { raw: _, decoded_result: ITest::failedReturn { failed } }) => {
614 trace!(failed, "DSTest::failed()");
615 !failed
616 }
617 Err(err) => {
618 trace!(%err, "failed to call DSTest::failed()");
619 true
620 }
621 }
622 }
623 }
624
625 fn build_test_env(
630 &self,
631 caller: Address,
632 transact_to: TxKind,
633 data: Bytes,
634 value: U256,
635 ) -> EnvWithHandlerCfg {
636 let env = Env {
637 cfg: self.env().cfg.clone(),
638 block: BlockEnv {
642 basefee: U256::ZERO,
643 gas_limit: U256::from(self.gas_limit),
644 ..self.env().block.clone()
645 },
646 tx: TxEnv {
647 caller,
648 transact_to,
649 data,
650 value,
651 gas_price: U256::ZERO,
653 gas_priority_fee: None,
654 gas_limit: self.gas_limit,
655 ..self.env().tx.clone()
656 },
657 };
658
659 EnvWithHandlerCfg::new_with_spec_id(Box::new(env), self.spec_id())
660 }
661
662 pub fn call_sol_default<C: SolCall>(&self, to: Address, args: &C) -> C::Return
663 where
664 C::Return: Default,
665 {
666 self.call_sol(CALLER, to, args, U256::ZERO, None)
667 .map(|c| c.decoded_result)
668 .inspect_err(|e| warn!(target: "forge::test", "failed calling {:?}: {e}", C::SIGNATURE))
669 .unwrap_or_default()
670 }
671}
672
673#[derive(Debug, thiserror::Error)]
675#[error("execution reverted: {reason} (gas: {})", raw.gas_used)]
676pub struct ExecutionErr {
677 pub raw: RawCallResult,
679 pub reason: String,
681}
682
683impl std::ops::Deref for ExecutionErr {
684 type Target = RawCallResult;
685
686 #[inline]
687 fn deref(&self) -> &Self::Target {
688 &self.raw
689 }
690}
691
692impl std::ops::DerefMut for ExecutionErr {
693 #[inline]
694 fn deref_mut(&mut self) -> &mut Self::Target {
695 &mut self.raw
696 }
697}
698
699#[derive(Debug, thiserror::Error)]
700pub enum EvmError {
701 #[error(transparent)]
703 Execution(#[from] Box<ExecutionErr>),
704 #[error(transparent)]
706 Abi(#[from] alloy_dyn_abi::Error),
707 #[error("{0}")]
709 Skip(SkipReason),
710 #[error("{0}")]
712 Eyre(
713 #[from]
714 #[source]
715 eyre::Report,
716 ),
717}
718
719impl From<ExecutionErr> for EvmError {
720 fn from(err: ExecutionErr) -> Self {
721 Self::Execution(Box::new(err))
722 }
723}
724
725impl From<alloy_sol_types::Error> for EvmError {
726 fn from(err: alloy_sol_types::Error) -> Self {
727 Self::Abi(err.into())
728 }
729}
730
731#[derive(Debug)]
733pub struct DeployResult {
734 pub raw: RawCallResult,
736 pub address: Address,
738}
739
740impl std::ops::Deref for DeployResult {
741 type Target = RawCallResult;
742
743 #[inline]
744 fn deref(&self) -> &Self::Target {
745 &self.raw
746 }
747}
748
749impl std::ops::DerefMut for DeployResult {
750 #[inline]
751 fn deref_mut(&mut self) -> &mut Self::Target {
752 &mut self.raw
753 }
754}
755
756impl From<DeployResult> for RawCallResult {
757 fn from(d: DeployResult) -> Self {
758 d.raw
759 }
760}
761
762#[derive(Debug)]
764pub struct RawCallResult {
765 pub exit_reason: InstructionResult,
767 pub reverted: bool,
769 pub has_state_snapshot_failure: bool,
774 pub result: Bytes,
776 pub gas_used: u64,
778 pub gas_refunded: u64,
780 pub stipend: u64,
782 pub logs: Vec<Log>,
784 pub labels: AddressHashMap<String>,
786 pub traces: Option<SparsedTraceArena>,
788 pub coverage: Option<HitMaps>,
790 pub transactions: Option<BroadcastableTransactions>,
792 pub state_changeset: StateChangeset,
794 pub env: EnvWithHandlerCfg,
796 pub cheatcodes: Option<Cheatcodes>,
798 pub out: Option<Output>,
800 pub chisel_state: Option<(Vec<U256>, Vec<u8>, InstructionResult)>,
802}
803
804impl Default for RawCallResult {
805 fn default() -> Self {
806 Self {
807 exit_reason: InstructionResult::Continue,
808 reverted: false,
809 has_state_snapshot_failure: false,
810 result: Bytes::new(),
811 gas_used: 0,
812 gas_refunded: 0,
813 stipend: 0,
814 logs: Vec::new(),
815 labels: HashMap::default(),
816 traces: None,
817 coverage: None,
818 transactions: None,
819 state_changeset: HashMap::default(),
820 env: EnvWithHandlerCfg::new_with_spec_id(Box::default(), SpecId::LATEST),
821 cheatcodes: Default::default(),
822 out: None,
823 chisel_state: None,
824 }
825 }
826}
827
828impl RawCallResult {
829 pub fn from_evm_result(r: Result<Self, EvmError>) -> eyre::Result<(Self, Option<String>)> {
831 match r {
832 Ok(r) => Ok((r, None)),
833 Err(EvmError::Execution(e)) => Ok((e.raw, Some(e.reason))),
834 Err(e) => Err(e.into()),
835 }
836 }
837
838 pub fn from_execution_result(r: Result<Self, ExecutionErr>) -> (Self, Option<String>) {
840 match r {
841 Ok(r) => (r, None),
842 Err(e) => (e.raw, Some(e.reason)),
843 }
844 }
845
846 pub fn into_evm_error(self, rd: Option<&RevertDecoder>) -> EvmError {
848 if let Some(reason) = SkipReason::decode(&self.result) {
849 return EvmError::Skip(reason);
850 }
851 let reason = rd.unwrap_or_default().decode(&self.result, Some(self.exit_reason));
852 EvmError::Execution(Box::new(self.into_execution_error(reason)))
853 }
854
855 pub fn into_execution_error(self, reason: String) -> ExecutionErr {
857 ExecutionErr { raw: self, reason }
858 }
859
860 pub fn into_result(self, rd: Option<&RevertDecoder>) -> Result<Self, EvmError> {
862 if self.exit_reason.is_ok() {
863 Ok(self)
864 } else {
865 Err(self.into_evm_error(rd))
866 }
867 }
868
869 pub fn into_decoded_result(
871 mut self,
872 func: &Function,
873 rd: Option<&RevertDecoder>,
874 ) -> Result<CallResult, EvmError> {
875 self = self.into_result(rd)?;
876 let mut result = func.abi_decode_output(&self.result, false)?;
877 let decoded_result = if result.len() == 1 {
878 result.pop().unwrap()
879 } else {
880 DynSolValue::Tuple(result)
882 };
883 Ok(CallResult { raw: self, decoded_result })
884 }
885
886 pub fn transactions(&self) -> Option<&BroadcastableTransactions> {
888 self.cheatcodes.as_ref().map(|c| &c.broadcastable_transactions)
889 }
890}
891
892pub struct CallResult<T = DynSolValue> {
894 pub raw: RawCallResult,
896 pub decoded_result: T,
898}
899
900impl std::ops::Deref for CallResult {
901 type Target = RawCallResult;
902
903 #[inline]
904 fn deref(&self) -> &Self::Target {
905 &self.raw
906 }
907}
908
909impl std::ops::DerefMut for CallResult {
910 #[inline]
911 fn deref_mut(&mut self) -> &mut Self::Target {
912 &mut self.raw
913 }
914}
915
916fn convert_executed_result(
918 env: EnvWithHandlerCfg,
919 inspector: InspectorStack,
920 ResultAndState { result, state: state_changeset }: ResultAndState,
921 has_state_snapshot_failure: bool,
922) -> eyre::Result<RawCallResult> {
923 let (exit_reason, gas_refunded, gas_used, out, exec_logs) = match result {
924 ExecutionResult::Success { reason, gas_used, gas_refunded, output, logs, .. } => {
925 (reason.into(), gas_refunded, gas_used, Some(output), logs)
926 }
927 ExecutionResult::Revert { gas_used, output } => {
928 (InstructionResult::Revert, 0_u64, gas_used, Some(Output::Call(output)), vec![])
930 }
931 ExecutionResult::Halt { reason, gas_used } => {
932 (reason.into(), 0_u64, gas_used, None, vec![])
933 }
934 };
935 let gas = revm::interpreter::gas::calculate_initial_tx_gas(
936 env.spec_id(),
937 &env.tx.data,
938 env.tx.transact_to.is_create(),
939 &env.tx.access_list,
940 0,
941 );
942
943 let result = match &out {
944 Some(Output::Call(data)) => data.clone(),
945 _ => Bytes::new(),
946 };
947
948 let InspectorData { mut logs, labels, traces, coverage, cheatcodes, chisel_state } =
949 inspector.collect();
950
951 if logs.is_empty() {
952 logs = exec_logs;
953 }
954
955 let transactions = cheatcodes
956 .as_ref()
957 .map(|c| c.broadcastable_transactions.clone())
958 .filter(|txs| !txs.is_empty());
959
960 Ok(RawCallResult {
961 exit_reason,
962 reverted: !matches!(exit_reason, return_ok!()),
963 has_state_snapshot_failure,
964 result,
965 gas_used,
966 gas_refunded,
967 stipend: gas.initial_gas,
968 logs,
969 labels,
970 traces,
971 coverage,
972 transactions,
973 state_changeset,
974 env,
975 cheatcodes,
976 out,
977 chisel_state,
978 })
979}
980
981pub struct FuzzTestTimer {
983 inner: Option<(Instant, Duration)>,
985}
986
987impl FuzzTestTimer {
988 pub fn new(timeout: Option<u32>) -> Self {
989 Self { inner: timeout.map(|timeout| (Instant::now(), Duration::from_secs(timeout.into()))) }
990 }
991
992 pub fn is_timed_out(&self) -> bool {
994 self.inner.is_some_and(|(start, duration)| start.elapsed() > duration)
995 }
996}