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, InspectorExt,
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 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
62pub mod strategy;
63pub use strategy::{
64 EvmExecutorStrategyRunner, ExecutorStrategy, ExecutorStrategyContext, ExecutorStrategyExt,
65 ExecutorStrategyRunner,
66};
67
68sol! {
69 interface ITest {
70 function setUp() external;
71 function failed() external view returns (bool failed);
72
73 #[derive(Default)]
74 function beforeTestSetup(bytes4 testSelector) public view returns (bytes[] memory beforeTestCalldata);
75 }
76}
77
78#[derive(Clone, Debug)]
90pub struct Executor {
91 backend: Backend,
97 env: Env,
99 inspector: InspectorStack,
101 gas_limit: u64,
103 legacy_assertions: bool,
105 pub strategy: ExecutorStrategy,
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 strategy: ExecutorStrategy,
125 ) -> Self {
126 backend.insert_account_info(
129 CHEATCODE_ADDRESS,
130 revm::state::AccountInfo {
131 code: Some(Bytecode::new_raw(Bytes::from_static(&[0]))),
132 code_hash: CHEATCODE_CONTRACT_HASH,
135 ..Default::default()
136 },
137 );
138
139 Self { strategy, backend, env, inspector, gas_limit, legacy_assertions }
140 }
141
142 fn clone_with_backend(&self, backend: Backend) -> Self {
143 let env = Env::new_with_spec_id(
144 self.env.evm_env.cfg_env.clone(),
145 self.env.evm_env.block_env.clone(),
146 self.env.tx.clone(),
147 self.spec_id(),
148 );
149 Self::new(
150 backend,
151 env,
152 self.inspector().clone(),
153 self.gas_limit,
154 self.legacy_assertions,
155 self.strategy.clone(),
156 )
157 }
158
159 pub fn backend(&self) -> &Backend {
161 &self.backend
162 }
163
164 pub fn backend_mut(&mut self) -> &mut Backend {
166 &mut self.backend
167 }
168
169 pub fn env(&self) -> &Env {
171 &self.env
172 }
173
174 pub fn env_mut(&mut self) -> &mut Env {
176 &mut self.env
177 }
178
179 pub fn inspector(&self) -> &InspectorStack {
181 &self.inspector
182 }
183
184 pub fn inspector_mut(&mut self) -> &mut InspectorStack {
186 &mut self.inspector
187 }
188
189 pub fn spec_id(&self) -> SpecId {
191 self.env.evm_env.cfg_env.spec
192 }
193
194 pub fn set_spec_id(&mut self, spec_id: SpecId) {
196 self.env.evm_env.cfg_env.spec = spec_id;
197 }
198
199 pub fn gas_limit(&self) -> u64 {
204 self.gas_limit
205 }
206
207 pub fn set_gas_limit(&mut self, gas_limit: u64) {
209 self.gas_limit = gas_limit;
210 }
211
212 pub fn legacy_assertions(&self) -> bool {
215 self.legacy_assertions
216 }
217
218 pub fn set_legacy_assertions(&mut self, legacy_assertions: bool) {
221 self.legacy_assertions = legacy_assertions;
222 }
223
224 pub fn deploy_create2_deployer(&mut self) -> eyre::Result<()> {
226 trace!("deploying local create2 deployer");
227 let create2_deployer_account = self
228 .backend()
229 .basic_ref(DEFAULT_CREATE2_DEPLOYER)?
230 .ok_or_else(|| BackendError::MissingAccount(DEFAULT_CREATE2_DEPLOYER))?;
231
232 if create2_deployer_account.code.is_none_or(|code| code.is_empty()) {
234 let creator = DEFAULT_CREATE2_DEPLOYER_DEPLOYER;
235
236 let initial_balance = self.get_balance(creator)?;
238 self.set_balance(creator, U256::MAX)?;
239
240 let res =
241 self.deploy(creator, DEFAULT_CREATE2_DEPLOYER_CODE.into(), U256::ZERO, None)?;
242 trace!(create2=?res.address, "deployed local create2 deployer");
243
244 self.set_balance(creator, initial_balance)?;
245 }
246 Ok(())
247 }
248
249 pub fn set_balance(&mut self, address: Address, amount: U256) -> BackendResult<()> {
251 trace!(?address, ?amount, "setting account balance");
252 self.strategy.runner.set_balance(self, address, amount)
253 }
254
255 pub fn get_balance(&mut self, address: Address) -> BackendResult<U256> {
257 self.strategy.runner.get_balance(self, address)
258 }
259
260 pub fn set_nonce(&mut self, address: Address, nonce: u64) -> BackendResult<()> {
262 self.strategy.runner.set_nonce(self, address, nonce)
263 }
264
265 pub fn get_nonce(&mut self, address: Address) -> BackendResult<u64> {
267 self.strategy.runner.get_nonce(self, address)
268 }
269
270 pub fn set_code(&mut self, address: Address, code: Bytecode) -> BackendResult<()> {
272 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
273 account.code_hash = keccak256(code.original_byte_slice());
274 account.code = Some(code);
275 self.backend_mut().insert_account_info(address, account);
276 Ok(())
277 }
278
279 pub fn set_storage(
281 &mut self,
282 address: Address,
283 storage: HashMap<U256, U256>,
284 ) -> BackendResult<()> {
285 self.backend_mut().replace_account_storage(address, storage)?;
286 Ok(())
287 }
288
289 pub fn set_storage_slot(
291 &mut self,
292 address: Address,
293 slot: U256,
294 value: U256,
295 ) -> BackendResult<()> {
296 self.backend_mut().insert_account_storage(address, slot, value)?;
297 Ok(())
298 }
299
300 pub fn is_empty_code(&self, address: Address) -> BackendResult<bool> {
302 Ok(self.backend().basic_ref(address)?.map(|acc| acc.is_empty_code_hash()).unwrap_or(true))
303 }
304
305 #[inline]
306 pub fn set_tracing(&mut self, mode: TraceMode) -> &mut Self {
307 self.inspector_mut().tracing(mode);
308 self
309 }
310
311 #[inline]
312 pub fn set_script_execution(&mut self, script_address: Address) {
313 self.inspector_mut().script(script_address);
314 }
315
316 #[inline]
317 pub fn set_trace_printer(&mut self, trace_printer: bool) -> &mut Self {
318 self.inspector_mut().print(trace_printer);
319 self
320 }
321
322 #[inline]
323 pub fn create2_deployer(&self) -> Address {
324 self.inspector().create2_deployer()
325 }
326
327 pub fn deploy(
332 &mut self,
333 from: Address,
334 code: Bytes,
335 value: U256,
336 rd: Option<&RevertDecoder>,
337 ) -> Result<DeployResult, EvmError> {
338 let env = self.build_test_env(from, TxKind::Create, code, value);
339 self.deploy_with_env(env, rd)
340 }
341
342 #[instrument(name = "deploy", level = "debug", skip_all)]
349 pub fn deploy_with_env(
350 &mut self,
351 env: Env,
352 rd: Option<&RevertDecoder>,
353 ) -> Result<DeployResult, EvmError> {
354 assert!(
355 matches!(env.tx.kind, TxKind::Create),
356 "Expected create transaction, got {:?}",
357 env.tx.kind
358 );
359 trace!(sender=%env.tx.caller, "deploying contract");
360
361 let mut result = self.transact_with_env(env)?;
362 result = result.into_result(rd)?;
363 let Some(Output::Create(_, Some(address))) = result.out else {
364 panic!("Deployment succeeded, but no address was returned: {result:#?}");
365 };
366
367 self.backend_mut().add_persistent_account(address);
370
371 debug!(%address, "deployed contract");
372
373 Ok(DeployResult { raw: result, address })
374 }
375
376 #[instrument(name = "setup", level = "debug", skip_all)]
383 pub fn setup(
384 &mut self,
385 from: Option<Address>,
386 to: Address,
387 rd: Option<&RevertDecoder>,
388 ) -> Result<RawCallResult, EvmError> {
389 trace!(?from, ?to, "setting up contract");
390
391 let from = from.unwrap_or(CALLER);
392 self.backend_mut().set_test_contract(to).set_caller(from);
393 let calldata = Bytes::from_static(&ITest::setUpCall::SELECTOR);
394 let mut res = self.transact_raw(from, to, calldata, U256::ZERO)?;
395 res = res.into_result(rd)?;
396
397 self.env_mut().evm_env.block_env = res.env.evm_env.block_env.clone();
399 self.env_mut().evm_env.cfg_env.chain_id = res.env.evm_env.cfg_env.chain_id;
401
402 let success =
403 self.is_raw_call_success(to, Cow::Borrowed(&res.state_changeset), &res, false);
404 if !success {
405 return Err(res.into_execution_error("execution error".to_string()).into());
406 }
407
408 Ok(res)
409 }
410
411 pub fn call(
413 &self,
414 from: Address,
415 to: Address,
416 func: &Function,
417 args: &[DynSolValue],
418 value: U256,
419 rd: Option<&RevertDecoder>,
420 ) -> Result<CallResult, EvmError> {
421 let calldata = Bytes::from(func.abi_encode_input(args)?);
422 let result = self.call_raw(from, to, calldata, value)?;
423 result.into_decoded_result(func, rd)
424 }
425
426 pub fn call_sol<C: SolCall>(
428 &self,
429 from: Address,
430 to: Address,
431 args: &C,
432 value: U256,
433 rd: Option<&RevertDecoder>,
434 ) -> Result<CallResult<C::Return>, EvmError> {
435 let calldata = Bytes::from(args.abi_encode());
436 let mut raw = self.call_raw(from, to, calldata, value)?;
437 raw = raw.into_result(rd)?;
438 Ok(CallResult { decoded_result: C::abi_decode_returns(&raw.result)?, raw })
439 }
440
441 pub fn transact(
443 &mut self,
444 from: Address,
445 to: Address,
446 func: &Function,
447 args: &[DynSolValue],
448 value: U256,
449 rd: Option<&RevertDecoder>,
450 ) -> Result<CallResult, EvmError> {
451 let calldata = Bytes::from(func.abi_encode_input(args)?);
452 let result = self.transact_raw(from, to, calldata, value)?;
453 result.into_decoded_result(func, rd)
454 }
455
456 pub fn call_raw(
458 &self,
459 from: Address,
460 to: Address,
461 calldata: Bytes,
462 value: U256,
463 ) -> eyre::Result<RawCallResult> {
464 let env = self.build_test_env(from, TxKind::Call(to), calldata, value);
465 self.call_with_env(env)
466 }
467
468 pub fn call_raw_with_authorization(
471 &mut self,
472 from: Address,
473 to: Address,
474 calldata: Bytes,
475 value: U256,
476 authorization_list: Vec<SignedAuthorization>,
477 ) -> eyre::Result<RawCallResult> {
478 let mut env = self.build_test_env(from, to.into(), calldata, value);
479 env.tx.set_signed_authorization(authorization_list);
480 env.tx.tx_type = 4;
481 self.call_with_env(env)
482 }
483
484 pub fn transact_raw(
486 &mut self,
487 from: Address,
488 to: Address,
489 calldata: Bytes,
490 value: U256,
491 ) -> eyre::Result<RawCallResult> {
492 let env = self.build_test_env(from, TxKind::Call(to), calldata, value);
493 self.transact_with_env(env)
494 }
495
496 #[instrument(name = "call", level = "debug", skip_all)]
500 pub fn call_with_env(&self, mut env: Env) -> eyre::Result<RawCallResult> {
501 let mut inspector = self.inspector().clone();
502 let mut backend = CowBackend::new_borrowed(self.backend());
503 let result = self.strategy.runner.call(
504 self.strategy.context.as_ref(),
505 &mut backend,
506 &mut env,
507 &self.env,
508 &mut inspector,
509 )?;
510 convert_executed_result(env, inspector, result, backend.has_state_snapshot_failure())
511 }
512
513 #[instrument(name = "transact", level = "debug", skip_all)]
515 pub fn transact_with_env(&mut self, mut env: Env) -> eyre::Result<RawCallResult> {
516 let mut inspector = self.inspector().clone();
517 let backend = &mut self.backend;
518 let result = self.strategy.runner.transact(
519 self.strategy.context.as_mut(),
520 backend,
521 &mut env,
522 &self.env,
523 &mut inspector,
524 )?;
525 let mut result =
526 convert_executed_result(env, inspector, result, backend.has_state_snapshot_failure())?;
527 self.commit(&mut result);
528 Ok(result)
529 }
530
531 #[instrument(name = "commit", level = "debug", skip_all)]
536 fn commit(&mut self, result: &mut RawCallResult) {
537 self.backend_mut().commit(result.state_changeset.clone());
539
540 self.inspector_mut().cheatcodes = result.cheatcodes.take();
542 if let Some(cheats) = self.inspector_mut().cheatcodes.as_mut() {
543 cheats.broadcastable_transactions.clear();
545 cheats.ignored_traces.ignored.clear();
546
547 if let Some(last_pause_call) = cheats.ignored_traces.last_pause_call.as_mut() {
550 *last_pause_call = (0, 0);
551 }
552 }
553
554 self.inspector_mut().set_env(&result.env);
556 }
557
558 pub fn is_raw_call_mut_success(
563 &self,
564 address: Address,
565 call_result: &mut RawCallResult,
566 should_fail: bool,
567 ) -> bool {
568 self.is_raw_call_success(
569 address,
570 Cow::Owned(std::mem::take(&mut call_result.state_changeset)),
571 call_result,
572 should_fail,
573 )
574 }
575
576 pub fn is_raw_call_success(
580 &self,
581 address: Address,
582 state_changeset: Cow<'_, StateChangeset>,
583 call_result: &RawCallResult,
584 should_fail: bool,
585 ) -> bool {
586 if call_result.has_state_snapshot_failure {
587 return should_fail;
589 }
590 self.is_success(address, call_result.reverted, state_changeset, should_fail)
591 }
592
593 pub fn is_success(
615 &self,
616 address: Address,
617 reverted: bool,
618 state_changeset: Cow<'_, StateChangeset>,
619 should_fail: bool,
620 ) -> bool {
621 let success = self.is_success_raw(address, reverted, state_changeset);
622 should_fail ^ success
623 }
624
625 #[instrument(name = "is_success", level = "debug", skip_all)]
626 fn is_success_raw(
627 &self,
628 address: Address,
629 reverted: bool,
630 state_changeset: Cow<'_, StateChangeset>,
631 ) -> bool {
632 if reverted {
634 return false;
635 }
636
637 if self.backend().has_state_snapshot_failure() {
639 return false;
640 }
641
642 if let Some(acc) = state_changeset.get(&CHEATCODE_ADDRESS)
644 && let Some(failed_slot) = acc.storage.get(&GLOBAL_FAIL_SLOT)
645 && !failed_slot.present_value().is_zero()
646 {
647 return false;
648 }
649 if let Ok(failed_slot) = self.backend().storage_ref(CHEATCODE_ADDRESS, GLOBAL_FAIL_SLOT)
650 && !failed_slot.is_zero()
651 {
652 return false;
653 }
654
655 if !self.legacy_assertions {
656 return true;
657 }
658
659 {
661 let mut backend = self.backend().clone_empty();
663
664 for address in [address, CHEATCODE_ADDRESS] {
667 let Ok(acc) = self.backend().basic_ref(address) else { return false };
668 backend.insert_account_info(address, acc.unwrap_or_default());
669 }
670
671 backend.commit(state_changeset.into_owned());
676
677 let executor = self.clone_with_backend(backend);
679 let call = executor.call_sol(CALLER, address, &ITest::failedCall {}, U256::ZERO, None);
680 match call {
681 Ok(CallResult { raw: _, decoded_result: failed }) => {
682 trace!(failed, "DSTest::failed()");
683 !failed
684 }
685 Err(err) => {
686 trace!(%err, "failed to call DSTest::failed()");
687 true
688 }
689 }
690 }
691 }
692
693 fn build_test_env(&self, caller: Address, kind: TxKind, data: Bytes, value: U256) -> Env {
698 Env {
699 evm_env: EvmEnv {
700 cfg_env: {
701 let mut cfg = self.env().evm_env.cfg_env.clone();
702 cfg.spec = self.spec_id();
703 cfg
704 },
705 block_env: BlockEnv {
709 basefee: 0,
710 gas_limit: self.gas_limit,
711 ..self.env().evm_env.block_env.clone()
712 },
713 },
714 tx: TxEnv {
715 caller,
716 kind,
717 data,
718 value,
719 gas_price: 0,
721 gas_priority_fee: None,
722 gas_limit: self.gas_limit,
723 chain_id: Some(self.env().evm_env.cfg_env.chain_id),
724 ..self.env().tx.clone()
725 },
726 }
727 }
728
729 pub fn call_sol_default<C: SolCall>(&self, to: Address, args: &C) -> C::Return
730 where
731 C::Return: Default,
732 {
733 self.call_sol(CALLER, to, args, U256::ZERO, None)
734 .map(|c| c.decoded_result)
735 .inspect_err(|e| warn!(target: "forge::test", "failed calling {:?}: {e}", C::SIGNATURE))
736 .unwrap_or_default()
737 }
738}
739
740#[derive(Debug, thiserror::Error)]
742#[error("execution reverted: {reason} (gas: {})", raw.gas_used)]
743pub struct ExecutionErr {
744 pub raw: RawCallResult,
746 pub reason: String,
748}
749
750impl std::ops::Deref for ExecutionErr {
751 type Target = RawCallResult;
752
753 #[inline]
754 fn deref(&self) -> &Self::Target {
755 &self.raw
756 }
757}
758
759impl std::ops::DerefMut for ExecutionErr {
760 #[inline]
761 fn deref_mut(&mut self) -> &mut Self::Target {
762 &mut self.raw
763 }
764}
765
766#[derive(Debug, thiserror::Error)]
767pub enum EvmError {
768 #[error(transparent)]
770 Execution(#[from] Box<ExecutionErr>),
771 #[error(transparent)]
773 Abi(#[from] alloy_dyn_abi::Error),
774 #[error("{0}")]
776 Skip(SkipReason),
777 #[error("{0}")]
779 Eyre(
780 #[from]
781 #[source]
782 eyre::Report,
783 ),
784}
785
786impl From<ExecutionErr> for EvmError {
787 fn from(err: ExecutionErr) -> Self {
788 Self::Execution(Box::new(err))
789 }
790}
791
792impl From<alloy_sol_types::Error> for EvmError {
793 fn from(err: alloy_sol_types::Error) -> Self {
794 Self::Abi(err.into())
795 }
796}
797
798#[derive(Debug)]
800pub struct DeployResult {
801 pub raw: RawCallResult,
803 pub address: Address,
805}
806
807impl std::ops::Deref for DeployResult {
808 type Target = RawCallResult;
809
810 #[inline]
811 fn deref(&self) -> &Self::Target {
812 &self.raw
813 }
814}
815
816impl std::ops::DerefMut for DeployResult {
817 #[inline]
818 fn deref_mut(&mut self) -> &mut Self::Target {
819 &mut self.raw
820 }
821}
822
823impl From<DeployResult> for RawCallResult {
824 fn from(d: DeployResult) -> Self {
825 d.raw
826 }
827}
828
829#[derive(Debug)]
831pub struct RawCallResult {
832 pub exit_reason: Option<InstructionResult>,
834 pub reverted: bool,
836 pub has_state_snapshot_failure: bool,
841 pub result: Bytes,
843 pub gas_used: u64,
845 pub gas_refunded: u64,
847 pub stipend: u64,
849 pub logs: Vec<Log>,
851 pub labels: AddressHashMap<String>,
853 pub traces: Option<SparsedTraceArena>,
855 pub line_coverage: Option<HitMaps>,
857 pub edge_coverage: Option<Vec<u8>>,
859 pub transactions: Option<BroadcastableTransactions>,
861 pub state_changeset: StateChangeset,
863 pub env: Env,
865 pub cheatcodes: Option<Cheatcodes>,
867 pub out: Option<Output>,
869 pub chisel_state: Option<(Vec<U256>, Vec<u8>, Option<InstructionResult>)>,
871 pub reverter: Option<Address>,
872}
873
874impl Default for RawCallResult {
875 fn default() -> Self {
876 Self {
877 exit_reason: None,
878 reverted: false,
879 has_state_snapshot_failure: false,
880 result: Bytes::new(),
881 gas_used: 0,
882 gas_refunded: 0,
883 stipend: 0,
884 logs: Vec::new(),
885 labels: HashMap::default(),
886 traces: None,
887 line_coverage: None,
888 edge_coverage: None,
889 transactions: None,
890 state_changeset: HashMap::default(),
891 env: Env::default(),
892 cheatcodes: Default::default(),
893 out: None,
894 chisel_state: None,
895 reverter: None,
896 }
897 }
898}
899
900impl RawCallResult {
901 pub fn from_evm_result(r: Result<Self, EvmError>) -> eyre::Result<(Self, Option<String>)> {
903 match r {
904 Ok(r) => Ok((r, None)),
905 Err(EvmError::Execution(e)) => Ok((e.raw, Some(e.reason))),
906 Err(e) => Err(e.into()),
907 }
908 }
909
910 pub fn from_execution_result(r: Result<Self, ExecutionErr>) -> (Self, Option<String>) {
912 match r {
913 Ok(r) => (r, None),
914 Err(e) => (e.raw, Some(e.reason)),
915 }
916 }
917
918 pub fn into_evm_error(self, rd: Option<&RevertDecoder>) -> EvmError {
920 if let Some(reason) = SkipReason::decode(&self.result) {
921 return EvmError::Skip(reason);
922 }
923 let reason = rd.unwrap_or_default().decode(&self.result, self.exit_reason);
924 EvmError::Execution(Box::new(self.into_execution_error(reason)))
925 }
926
927 pub fn into_execution_error(self, reason: String) -> ExecutionErr {
929 ExecutionErr { raw: self, reason }
930 }
931
932 pub fn into_result(self, rd: Option<&RevertDecoder>) -> Result<Self, EvmError> {
934 if let Some(reason) = self.exit_reason
935 && reason.is_ok()
936 {
937 Ok(self)
938 } else {
939 Err(self.into_evm_error(rd))
940 }
941 }
942
943 pub fn into_decoded_result(
945 mut self,
946 func: &Function,
947 rd: Option<&RevertDecoder>,
948 ) -> Result<CallResult, EvmError> {
949 self = self.into_result(rd)?;
950 let mut result = func.abi_decode_output(&self.result)?;
951 let decoded_result = if result.len() == 1 {
952 result.pop().unwrap()
953 } else {
954 DynSolValue::Tuple(result)
956 };
957 Ok(CallResult { raw: self, decoded_result })
958 }
959
960 pub fn transactions(&self) -> Option<&BroadcastableTransactions> {
962 self.cheatcodes.as_ref().map(|c| &c.broadcastable_transactions)
963 }
964
965 pub fn merge_edge_coverage(&mut self, history_map: &mut [u8]) -> (bool, bool) {
968 let mut new_coverage = false;
969 let mut is_edge = false;
970 if let Some(x) = &mut self.edge_coverage {
971 for (curr, hist) in std::iter::zip(x, history_map) {
974 if *curr > 0 {
976 let bucket = match *curr {
978 0 => 0,
979 1 => 1,
980 2 => 2,
981 3 => 4,
982 4..=7 => 8,
983 8..=15 => 16,
984 16..=31 => 32,
985 32..=127 => 64,
986 128..=255 => 128,
987 };
988
989 if *hist < bucket {
991 if *hist == 0 {
992 is_edge = true;
994 }
995 *hist = bucket;
996 new_coverage = true;
997 }
998
999 *curr = 0;
1001 }
1002 }
1003 }
1004 (new_coverage, is_edge)
1005 }
1006}
1007
1008pub struct CallResult<T = DynSolValue> {
1010 pub raw: RawCallResult,
1012 pub decoded_result: T,
1014}
1015
1016impl std::ops::Deref for CallResult {
1017 type Target = RawCallResult;
1018
1019 #[inline]
1020 fn deref(&self) -> &Self::Target {
1021 &self.raw
1022 }
1023}
1024
1025impl std::ops::DerefMut for CallResult {
1026 #[inline]
1027 fn deref_mut(&mut self) -> &mut Self::Target {
1028 &mut self.raw
1029 }
1030}
1031
1032fn convert_executed_result(
1034 env: Env,
1035 inspector: InspectorStack,
1036 ResultAndState { result, state: state_changeset }: ResultAndState,
1037 has_state_snapshot_failure: bool,
1038) -> eyre::Result<RawCallResult> {
1039 let (exit_reason, gas_refunded, gas_used, out, exec_logs) = match result {
1040 ExecutionResult::Success { reason, gas_used, gas_refunded, output, logs, .. } => {
1041 (reason.into(), gas_refunded, gas_used, Some(output), logs)
1042 }
1043 ExecutionResult::Revert { gas_used, output } => {
1044 (InstructionResult::Revert, 0_u64, gas_used, Some(Output::Call(output)), vec![])
1046 }
1047 ExecutionResult::Halt { reason, gas_used } => {
1048 (reason.into(), 0_u64, gas_used, None, vec![])
1049 }
1050 };
1051 let gas = revm::interpreter::gas::calculate_initial_tx_gas(
1052 env.evm_env.cfg_env.spec,
1053 &env.tx.data,
1054 env.tx.kind.is_create(),
1055 env.tx.access_list.len().try_into()?,
1056 0,
1057 0,
1058 );
1059
1060 let result = match &out {
1061 Some(Output::Call(data)) => data.clone(),
1062 _ => Bytes::new(),
1063 };
1064
1065 let InspectorData {
1066 mut logs,
1067 labels,
1068 traces,
1069 line_coverage,
1070 edge_coverage,
1071 cheatcodes,
1072 chisel_state,
1073 reverter,
1074 } = inspector.collect();
1075
1076 if logs.is_empty() {
1077 logs = exec_logs;
1078 }
1079
1080 let transactions = cheatcodes
1081 .as_ref()
1082 .map(|c| c.broadcastable_transactions.clone())
1083 .filter(|txs| !txs.is_empty());
1084
1085 Ok(RawCallResult {
1086 exit_reason: Some(exit_reason),
1087 reverted: !matches!(exit_reason, return_ok!()),
1088 has_state_snapshot_failure,
1089 result,
1090 gas_used,
1091 gas_refunded,
1092 stipend: gas.initial_gas,
1093 logs,
1094 labels,
1095 traces,
1096 line_coverage,
1097 edge_coverage,
1098 transactions,
1099 state_changeset,
1100 env,
1101 cheatcodes,
1102 out,
1103 chisel_state,
1104 reverter,
1105 })
1106}
1107
1108pub struct FuzzTestTimer {
1110 inner: Option<(Instant, Duration)>,
1112}
1113
1114impl FuzzTestTimer {
1115 pub fn new(timeout: Option<u32>) -> Self {
1116 Self { inner: timeout.map(|timeout| (Instant::now(), Duration::from_secs(timeout.into()))) }
1117 }
1118
1119 pub fn is_timed_out(&self) -> bool {
1121 self.inner.is_some_and(|(start, duration)| start.elapsed() > duration)
1122 }
1123}