1use crate::{
10 inspectors::{
11 cheatcodes::BroadcastableTransactions, Cheatcodes, InspectorData, InspectorStack,
12 },
13 Env,
14};
15use alloy_dyn_abi::{DynSolValue, FunctionExt, JsonAbiExt};
16use alloy_json_abi::Function;
17use alloy_primitives::{
18 map::{AddressHashMap, HashMap},
19 Address, Bytes, Log, TxKind, U256,
20};
21use alloy_sol_types::{sol, SolCall};
22use foundry_evm_core::{
23 backend::{Backend, BackendError, BackendResult, CowBackend, DatabaseExt, GLOBAL_FAIL_SLOT},
24 constants::{
25 CALLER, CHEATCODE_ADDRESS, CHEATCODE_CONTRACT_HASH, DEFAULT_CREATE2_DEPLOYER,
26 DEFAULT_CREATE2_DEPLOYER_CODE, DEFAULT_CREATE2_DEPLOYER_DEPLOYER,
27 },
28 decode::{RevertDecoder, SkipReason},
29 utils::StateChangeset,
30 EvmEnv, InspectorExt,
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::{return_ok, InstructionResult},
43 primitives::hardfork::SpecId,
44};
45use std::{
46 borrow::Cow,
47 time::{Duration, Instant},
48};
49
50mod builder;
51pub use builder::ExecutorBuilder;
52
53pub mod fuzz;
54pub use fuzz::FuzzedExecutor;
55
56pub mod invariant;
57pub use invariant::InvariantExecutor;
58
59mod trace;
60pub use trace::TracingExecutor;
61
62sol! {
63 interface ITest {
64 function setUp() external;
65 function failed() external view returns (bool failed);
66
67 #[derive(Default)]
68 function beforeTestSetup(bytes4 testSelector) public view returns (bytes[] memory beforeTestCalldata);
69 }
70}
71
72#[derive(Clone, Debug)]
84pub struct Executor {
85 backend: Backend,
91 env: Env,
93 inspector: InspectorStack,
95 gas_limit: u64,
97 legacy_assertions: bool,
99}
100
101impl Executor {
102 #[inline]
104 pub fn builder() -> ExecutorBuilder {
105 ExecutorBuilder::new()
106 }
107
108 #[inline]
110 pub fn new(
111 mut backend: Backend,
112 env: Env,
113 inspector: InspectorStack,
114 gas_limit: u64,
115 legacy_assertions: bool,
116 ) -> Self {
117 backend.insert_account_info(
120 CHEATCODE_ADDRESS,
121 revm::state::AccountInfo {
122 code: Some(Bytecode::new_raw(Bytes::from_static(&[0]))),
123 code_hash: CHEATCODE_CONTRACT_HASH,
126 ..Default::default()
127 },
128 );
129
130 Self { backend, env, inspector, gas_limit, legacy_assertions }
131 }
132
133 fn clone_with_backend(&self, backend: Backend) -> Self {
134 let env = Env::new_with_spec_id(
135 self.env.evm_env.cfg_env.clone(),
136 self.env.evm_env.block_env.clone(),
137 self.env.tx.clone(),
138 self.spec_id(),
139 );
140 Self::new(backend, env, self.inspector().clone(), self.gas_limit, self.legacy_assertions)
141 }
142
143 pub fn backend(&self) -> &Backend {
145 &self.backend
146 }
147
148 pub fn backend_mut(&mut self) -> &mut Backend {
150 &mut self.backend
151 }
152
153 pub fn env(&self) -> &Env {
155 &self.env
156 }
157
158 pub fn env_mut(&mut self) -> &mut Env {
160 &mut self.env
161 }
162
163 pub fn inspector(&self) -> &InspectorStack {
165 &self.inspector
166 }
167
168 pub fn inspector_mut(&mut self) -> &mut InspectorStack {
170 &mut self.inspector
171 }
172
173 pub fn spec_id(&self) -> SpecId {
175 self.env.evm_env.cfg_env.spec
176 }
177
178 pub fn set_spec_id(&mut self, spec_id: SpecId) {
180 self.env.evm_env.cfg_env.spec = spec_id;
181 }
182
183 pub fn gas_limit(&self) -> u64 {
188 self.gas_limit
189 }
190
191 pub fn set_gas_limit(&mut self, gas_limit: u64) {
193 self.gas_limit = gas_limit;
194 }
195
196 pub fn legacy_assertions(&self) -> bool {
199 self.legacy_assertions
200 }
201
202 pub fn set_legacy_assertions(&mut self, legacy_assertions: bool) {
205 self.legacy_assertions = legacy_assertions;
206 }
207
208 pub fn deploy_create2_deployer(&mut self) -> eyre::Result<()> {
210 trace!("deploying local create2 deployer");
211 let create2_deployer_account = self
212 .backend()
213 .basic_ref(DEFAULT_CREATE2_DEPLOYER)?
214 .ok_or_else(|| BackendError::MissingAccount(DEFAULT_CREATE2_DEPLOYER))?;
215
216 if create2_deployer_account.code.is_none_or(|code| code.is_empty()) {
218 let creator = DEFAULT_CREATE2_DEPLOYER_DEPLOYER;
219
220 let initial_balance = self.get_balance(creator)?;
222 self.set_balance(creator, U256::MAX)?;
223
224 let res =
225 self.deploy(creator, DEFAULT_CREATE2_DEPLOYER_CODE.into(), U256::ZERO, None)?;
226 trace!(create2=?res.address, "deployed local create2 deployer");
227
228 self.set_balance(creator, initial_balance)?;
229 }
230 Ok(())
231 }
232
233 pub fn set_balance(&mut self, address: Address, amount: U256) -> BackendResult<()> {
235 trace!(?address, ?amount, "setting account balance");
236 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
237 account.balance = amount;
238 self.backend_mut().insert_account_info(address, account);
239 Ok(())
240 }
241
242 pub fn get_balance(&self, address: Address) -> BackendResult<U256> {
244 Ok(self.backend().basic_ref(address)?.map(|acc| acc.balance).unwrap_or_default())
245 }
246
247 pub fn set_nonce(&mut self, address: Address, nonce: u64) -> BackendResult<()> {
249 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
250 account.nonce = nonce;
251 self.backend_mut().insert_account_info(address, account);
252 self.env_mut().tx.nonce = nonce;
253 Ok(())
254 }
255
256 pub fn get_nonce(&self, address: Address) -> BackendResult<u64> {
258 Ok(self.backend().basic_ref(address)?.map(|acc| acc.nonce).unwrap_or_default())
259 }
260
261 pub fn is_empty_code(&self, address: Address) -> BackendResult<bool> {
263 Ok(self.backend().basic_ref(address)?.map(|acc| acc.is_empty_code_hash()).unwrap_or(true))
264 }
265
266 #[inline]
267 pub fn set_tracing(&mut self, mode: TraceMode) -> &mut Self {
268 self.inspector_mut().tracing(mode);
269 self
270 }
271
272 #[inline]
273 pub fn set_script_execution(&mut self, script_address: Address) {
274 self.inspector_mut().script(script_address);
275 }
276
277 #[inline]
278 pub fn set_trace_printer(&mut self, trace_printer: bool) -> &mut Self {
279 self.inspector_mut().print(trace_printer);
280 self
281 }
282
283 #[inline]
284 pub fn create2_deployer(&self) -> Address {
285 self.inspector().create2_deployer()
286 }
287
288 pub fn deploy(
293 &mut self,
294 from: Address,
295 code: Bytes,
296 value: U256,
297 rd: Option<&RevertDecoder>,
298 ) -> Result<DeployResult, EvmError> {
299 let env = self.build_test_env(from, TxKind::Create, code, value);
300 self.deploy_with_env(env, rd)
301 }
302
303 #[instrument(name = "deploy", level = "debug", skip_all)]
310 pub fn deploy_with_env(
311 &mut self,
312 env: Env,
313 rd: Option<&RevertDecoder>,
314 ) -> Result<DeployResult, EvmError> {
315 assert!(
316 matches!(env.tx.kind, TxKind::Create),
317 "Expected create transaction, got {:?}",
318 env.tx.kind
319 );
320 trace!(sender=%env.tx.caller, "deploying contract");
321
322 let mut result = self.transact_with_env(env)?;
323 result = result.into_result(rd)?;
324 let Some(Output::Create(_, Some(address))) = result.out else {
325 panic!("Deployment succeeded, but no address was returned: {result:#?}");
326 };
327
328 self.backend_mut().add_persistent_account(address);
331
332 debug!(%address, "deployed contract");
333
334 Ok(DeployResult { raw: result, address })
335 }
336
337 #[instrument(name = "setup", level = "debug", skip_all)]
344 pub fn setup(
345 &mut self,
346 from: Option<Address>,
347 to: Address,
348 rd: Option<&RevertDecoder>,
349 ) -> Result<RawCallResult, EvmError> {
350 trace!(?from, ?to, "setting up contract");
351
352 let from = from.unwrap_or(CALLER);
353 self.backend_mut().set_test_contract(to).set_caller(from);
354 let calldata = Bytes::from_static(&ITest::setUpCall::SELECTOR);
355 let mut res = self.transact_raw(from, to, calldata, U256::ZERO)?;
356 res = res.into_result(rd)?;
357
358 self.env_mut().evm_env.block_env = res.env.evm_env.block_env.clone();
360 self.env_mut().evm_env.cfg_env.chain_id = res.env.evm_env.cfg_env.chain_id;
362
363 let success =
364 self.is_raw_call_success(to, Cow::Borrowed(&res.state_changeset), &res, false);
365 if !success {
366 return Err(res.into_execution_error("execution error".to_string()).into());
367 }
368
369 Ok(res)
370 }
371
372 pub fn call(
374 &self,
375 from: Address,
376 to: Address,
377 func: &Function,
378 args: &[DynSolValue],
379 value: U256,
380 rd: Option<&RevertDecoder>,
381 ) -> Result<CallResult, EvmError> {
382 let calldata = Bytes::from(func.abi_encode_input(args)?);
383 let result = self.call_raw(from, to, calldata, value)?;
384 result.into_decoded_result(func, rd)
385 }
386
387 pub fn call_sol<C: SolCall>(
389 &self,
390 from: Address,
391 to: Address,
392 args: &C,
393 value: U256,
394 rd: Option<&RevertDecoder>,
395 ) -> Result<CallResult<C::Return>, EvmError> {
396 let calldata = Bytes::from(args.abi_encode());
397 let mut raw = self.call_raw(from, to, calldata, value)?;
398 raw = raw.into_result(rd)?;
399 Ok(CallResult { decoded_result: C::abi_decode_returns(&raw.result)?, raw })
400 }
401
402 pub fn transact(
404 &mut self,
405 from: Address,
406 to: Address,
407 func: &Function,
408 args: &[DynSolValue],
409 value: U256,
410 rd: Option<&RevertDecoder>,
411 ) -> Result<CallResult, EvmError> {
412 let calldata = Bytes::from(func.abi_encode_input(args)?);
413 let result = self.transact_raw(from, to, calldata, value)?;
414 result.into_decoded_result(func, rd)
415 }
416
417 pub fn call_raw(
419 &self,
420 from: Address,
421 to: Address,
422 calldata: Bytes,
423 value: U256,
424 ) -> eyre::Result<RawCallResult> {
425 let env = self.build_test_env(from, TxKind::Call(to), calldata, value);
426 self.call_with_env(env)
427 }
428
429 pub fn call_raw_with_authorization(
432 &mut self,
433 from: Address,
434 to: Address,
435 calldata: Bytes,
436 value: U256,
437 authorization_list: Vec<SignedAuthorization>,
438 ) -> eyre::Result<RawCallResult> {
439 let mut env = self.build_test_env(from, to.into(), calldata, value);
440 env.tx.set_signed_authorization(authorization_list);
441 env.tx.tx_type = 4;
442 self.call_with_env(env)
443 }
444
445 pub fn transact_raw(
447 &mut self,
448 from: Address,
449 to: Address,
450 calldata: Bytes,
451 value: U256,
452 ) -> eyre::Result<RawCallResult> {
453 let env = self.build_test_env(from, TxKind::Call(to), calldata, value);
454 self.transact_with_env(env)
455 }
456
457 #[instrument(name = "call", level = "debug", skip_all)]
461 pub fn call_with_env(&self, mut env: Env) -> eyre::Result<RawCallResult> {
462 let mut inspector = self.inspector().clone();
463 let mut backend = CowBackend::new_borrowed(self.backend());
464 let result = backend.inspect(&mut env, &mut inspector)?;
465 convert_executed_result(env, inspector, result, backend.has_state_snapshot_failure())
466 }
467
468 #[instrument(name = "transact", level = "debug", skip_all)]
470 pub fn transact_with_env(&mut self, mut env: Env) -> eyre::Result<RawCallResult> {
471 let mut inspector = self.inspector().clone();
472 let backend = self.backend_mut();
473 let result = backend.inspect(&mut env, &mut inspector)?;
474 let mut result =
475 convert_executed_result(env, inspector, result, backend.has_state_snapshot_failure())?;
476 self.commit(&mut result);
477 Ok(result)
478 }
479
480 #[instrument(name = "commit", level = "debug", skip_all)]
485 fn commit(&mut self, result: &mut RawCallResult) {
486 self.backend_mut().commit(result.state_changeset.clone());
488
489 self.inspector_mut().cheatcodes = result.cheatcodes.take();
491 if let Some(cheats) = self.inspector_mut().cheatcodes.as_mut() {
492 cheats.broadcastable_transactions.clear();
494 cheats.ignored_traces.ignored.clear();
495
496 if let Some(last_pause_call) = cheats.ignored_traces.last_pause_call.as_mut() {
499 *last_pause_call = (0, 0);
500 }
501 }
502
503 self.inspector_mut().set_env(&result.env);
505 }
506
507 pub fn is_raw_call_mut_success(
512 &self,
513 address: Address,
514 call_result: &mut RawCallResult,
515 should_fail: bool,
516 ) -> bool {
517 self.is_raw_call_success(
518 address,
519 Cow::Owned(std::mem::take(&mut call_result.state_changeset)),
520 call_result,
521 should_fail,
522 )
523 }
524
525 pub fn is_raw_call_success(
529 &self,
530 address: Address,
531 state_changeset: Cow<'_, StateChangeset>,
532 call_result: &RawCallResult,
533 should_fail: bool,
534 ) -> bool {
535 if call_result.has_state_snapshot_failure {
536 return should_fail;
538 }
539 self.is_success(address, call_result.reverted, state_changeset, should_fail)
540 }
541
542 pub fn is_success(
564 &self,
565 address: Address,
566 reverted: bool,
567 state_changeset: Cow<'_, StateChangeset>,
568 should_fail: bool,
569 ) -> bool {
570 let success = self.is_success_raw(address, reverted, state_changeset);
571 should_fail ^ success
572 }
573
574 #[instrument(name = "is_success", level = "debug", skip_all)]
575 fn is_success_raw(
576 &self,
577 address: Address,
578 reverted: bool,
579 state_changeset: Cow<'_, StateChangeset>,
580 ) -> bool {
581 if reverted {
583 return false;
584 }
585
586 if self.backend().has_state_snapshot_failure() {
588 return false;
589 }
590
591 if let Some(acc) = state_changeset.get(&CHEATCODE_ADDRESS) {
593 if let Some(failed_slot) = acc.storage.get(&GLOBAL_FAIL_SLOT) {
594 if !failed_slot.present_value().is_zero() {
595 return false;
596 }
597 }
598 }
599 if let Ok(failed_slot) = self.backend().storage_ref(CHEATCODE_ADDRESS, GLOBAL_FAIL_SLOT) {
600 if !failed_slot.is_zero() {
601 return false;
602 }
603 }
604
605 if !self.legacy_assertions {
606 return true;
607 }
608
609 {
611 let mut backend = self.backend().clone_empty();
613
614 for address in [address, CHEATCODE_ADDRESS] {
617 let Ok(acc) = self.backend().basic_ref(address) else { return false };
618 backend.insert_account_info(address, acc.unwrap_or_default());
619 }
620
621 backend.commit(state_changeset.into_owned());
626
627 let executor = self.clone_with_backend(backend);
629 let call = executor.call_sol(CALLER, address, &ITest::failedCall {}, U256::ZERO, None);
630 match call {
631 Ok(CallResult { raw: _, decoded_result: failed }) => {
632 trace!(failed, "DSTest::failed()");
633 !failed
634 }
635 Err(err) => {
636 trace!(%err, "failed to call DSTest::failed()");
637 true
638 }
639 }
640 }
641 }
642
643 fn build_test_env(&self, caller: Address, kind: TxKind, data: Bytes, value: U256) -> Env {
648 Env {
649 evm_env: EvmEnv {
650 cfg_env: {
651 let mut cfg = self.env().evm_env.cfg_env.clone();
652 cfg.spec = self.spec_id();
653 cfg
654 },
655 block_env: BlockEnv {
659 basefee: 0,
660 gas_limit: self.gas_limit,
661 ..self.env().evm_env.block_env.clone()
662 },
663 },
664 tx: TxEnv {
665 caller,
666 kind,
667 data,
668 value,
669 gas_price: 0,
671 gas_priority_fee: None,
672 gas_limit: self.gas_limit,
673 chain_id: Some(self.env().evm_env.cfg_env.chain_id),
674 ..self.env().tx.clone()
675 },
676 }
677 }
678
679 pub fn call_sol_default<C: SolCall>(&self, to: Address, args: &C) -> C::Return
680 where
681 C::Return: Default,
682 {
683 self.call_sol(CALLER, to, args, U256::ZERO, None)
684 .map(|c| c.decoded_result)
685 .inspect_err(|e| warn!(target: "forge::test", "failed calling {:?}: {e}", C::SIGNATURE))
686 .unwrap_or_default()
687 }
688}
689
690#[derive(Debug, thiserror::Error)]
692#[error("execution reverted: {reason} (gas: {})", raw.gas_used)]
693pub struct ExecutionErr {
694 pub raw: RawCallResult,
696 pub reason: String,
698}
699
700impl std::ops::Deref for ExecutionErr {
701 type Target = RawCallResult;
702
703 #[inline]
704 fn deref(&self) -> &Self::Target {
705 &self.raw
706 }
707}
708
709impl std::ops::DerefMut for ExecutionErr {
710 #[inline]
711 fn deref_mut(&mut self) -> &mut Self::Target {
712 &mut self.raw
713 }
714}
715
716#[derive(Debug, thiserror::Error)]
717pub enum EvmError {
718 #[error(transparent)]
720 Execution(#[from] Box<ExecutionErr>),
721 #[error(transparent)]
723 Abi(#[from] alloy_dyn_abi::Error),
724 #[error("{0}")]
726 Skip(SkipReason),
727 #[error("{0}")]
729 Eyre(
730 #[from]
731 #[source]
732 eyre::Report,
733 ),
734}
735
736impl From<ExecutionErr> for EvmError {
737 fn from(err: ExecutionErr) -> Self {
738 Self::Execution(Box::new(err))
739 }
740}
741
742impl From<alloy_sol_types::Error> for EvmError {
743 fn from(err: alloy_sol_types::Error) -> Self {
744 Self::Abi(err.into())
745 }
746}
747
748#[derive(Debug)]
750pub struct DeployResult {
751 pub raw: RawCallResult,
753 pub address: Address,
755}
756
757impl std::ops::Deref for DeployResult {
758 type Target = RawCallResult;
759
760 #[inline]
761 fn deref(&self) -> &Self::Target {
762 &self.raw
763 }
764}
765
766impl std::ops::DerefMut for DeployResult {
767 #[inline]
768 fn deref_mut(&mut self) -> &mut Self::Target {
769 &mut self.raw
770 }
771}
772
773impl From<DeployResult> for RawCallResult {
774 fn from(d: DeployResult) -> Self {
775 d.raw
776 }
777}
778
779#[derive(Debug)]
781pub struct RawCallResult {
782 pub exit_reason: InstructionResult,
784 pub reverted: bool,
786 pub has_state_snapshot_failure: bool,
791 pub result: Bytes,
793 pub gas_used: u64,
795 pub gas_refunded: u64,
797 pub stipend: u64,
799 pub logs: Vec<Log>,
801 pub labels: AddressHashMap<String>,
803 pub traces: Option<SparsedTraceArena>,
805 pub coverage: Option<HitMaps>,
807 pub transactions: Option<BroadcastableTransactions>,
809 pub state_changeset: StateChangeset,
811 pub env: Env,
813 pub cheatcodes: Option<Cheatcodes>,
815 pub out: Option<Output>,
817 pub chisel_state: Option<(Vec<U256>, Vec<u8>, InstructionResult)>,
819}
820
821impl Default for RawCallResult {
822 fn default() -> Self {
823 Self {
824 exit_reason: InstructionResult::Continue,
825 reverted: false,
826 has_state_snapshot_failure: false,
827 result: Bytes::new(),
828 gas_used: 0,
829 gas_refunded: 0,
830 stipend: 0,
831 logs: Vec::new(),
832 labels: HashMap::default(),
833 traces: None,
834 coverage: None,
835 transactions: None,
836 state_changeset: HashMap::default(),
837 env: Env::default(),
838 cheatcodes: Default::default(),
839 out: None,
840 chisel_state: None,
841 }
842 }
843}
844
845impl RawCallResult {
846 pub fn from_evm_result(r: Result<Self, EvmError>) -> eyre::Result<(Self, Option<String>)> {
848 match r {
849 Ok(r) => Ok((r, None)),
850 Err(EvmError::Execution(e)) => Ok((e.raw, Some(e.reason))),
851 Err(e) => Err(e.into()),
852 }
853 }
854
855 pub fn from_execution_result(r: Result<Self, ExecutionErr>) -> (Self, Option<String>) {
857 match r {
858 Ok(r) => (r, None),
859 Err(e) => (e.raw, Some(e.reason)),
860 }
861 }
862
863 pub fn into_evm_error(self, rd: Option<&RevertDecoder>) -> EvmError {
865 if let Some(reason) = SkipReason::decode(&self.result) {
866 return EvmError::Skip(reason);
867 }
868 let reason = rd.unwrap_or_default().decode(&self.result, Some(self.exit_reason));
869 EvmError::Execution(Box::new(self.into_execution_error(reason)))
870 }
871
872 pub fn into_execution_error(self, reason: String) -> ExecutionErr {
874 ExecutionErr { raw: self, reason }
875 }
876
877 pub fn into_result(self, rd: Option<&RevertDecoder>) -> Result<Self, EvmError> {
879 if self.exit_reason.is_ok() {
880 Ok(self)
881 } else {
882 Err(self.into_evm_error(rd))
883 }
884 }
885
886 pub fn into_decoded_result(
888 mut self,
889 func: &Function,
890 rd: Option<&RevertDecoder>,
891 ) -> Result<CallResult, EvmError> {
892 self = self.into_result(rd)?;
893 let mut result = func.abi_decode_output(&self.result)?;
894 let decoded_result = if result.len() == 1 {
895 result.pop().unwrap()
896 } else {
897 DynSolValue::Tuple(result)
899 };
900 Ok(CallResult { raw: self, decoded_result })
901 }
902
903 pub fn transactions(&self) -> Option<&BroadcastableTransactions> {
905 self.cheatcodes.as_ref().map(|c| &c.broadcastable_transactions)
906 }
907}
908
909pub struct CallResult<T = DynSolValue> {
911 pub raw: RawCallResult,
913 pub decoded_result: T,
915}
916
917impl std::ops::Deref for CallResult {
918 type Target = RawCallResult;
919
920 #[inline]
921 fn deref(&self) -> &Self::Target {
922 &self.raw
923 }
924}
925
926impl std::ops::DerefMut for CallResult {
927 #[inline]
928 fn deref_mut(&mut self) -> &mut Self::Target {
929 &mut self.raw
930 }
931}
932
933fn convert_executed_result(
935 env: Env,
936 inspector: InspectorStack,
937 ResultAndState { result, state: state_changeset }: ResultAndState,
938 has_state_snapshot_failure: bool,
939) -> eyre::Result<RawCallResult> {
940 let (exit_reason, gas_refunded, gas_used, out, exec_logs) = match result {
941 ExecutionResult::Success { reason, gas_used, gas_refunded, output, logs, .. } => {
942 (reason.into(), gas_refunded, gas_used, Some(output), logs)
943 }
944 ExecutionResult::Revert { gas_used, output } => {
945 (InstructionResult::Revert, 0_u64, gas_used, Some(Output::Call(output)), vec![])
947 }
948 ExecutionResult::Halt { reason, gas_used } => {
949 (reason.into(), 0_u64, gas_used, None, vec![])
950 }
951 };
952 let gas = revm::interpreter::gas::calculate_initial_tx_gas(
953 env.evm_env.cfg_env.spec,
954 &env.tx.data,
955 env.tx.kind.is_create(),
956 env.tx.access_list.len().try_into()?,
957 0,
958 0,
959 );
960
961 let result = match &out {
962 Some(Output::Call(data)) => data.clone(),
963 _ => Bytes::new(),
964 };
965
966 let InspectorData { mut logs, labels, traces, coverage, cheatcodes, chisel_state } =
967 inspector.collect();
968
969 if logs.is_empty() {
970 logs = exec_logs;
971 }
972
973 let transactions = cheatcodes
974 .as_ref()
975 .map(|c| c.broadcastable_transactions.clone())
976 .filter(|txs| !txs.is_empty());
977
978 Ok(RawCallResult {
979 exit_reason,
980 reverted: !matches!(exit_reason, return_ok!()),
981 has_state_snapshot_failure,
982 result,
983 gas_used,
984 gas_refunded,
985 stipend: gas.initial_gas,
986 logs,
987 labels,
988 traces,
989 coverage,
990 transactions,
991 state_changeset,
992 env,
993 cheatcodes,
994 out,
995 chisel_state,
996 })
997}
998
999pub struct FuzzTestTimer {
1001 inner: Option<(Instant, Duration)>,
1003}
1004
1005impl FuzzTestTimer {
1006 pub fn new(timeout: Option<u32>) -> Self {
1007 Self { inner: timeout.map(|timeout| (Instant::now(), Duration::from_secs(timeout.into()))) }
1008 }
1009
1010 pub fn is_timed_out(&self) -> bool {
1012 self.inner.is_some_and(|(start, duration)| start.elapsed() > duration)
1013 }
1014}