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
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 set_code(&mut self, address: Address, code: Bytecode) -> BackendResult<()> {
263 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
264 account.code_hash = keccak256(code.original_byte_slice());
265 account.code = Some(code);
266 self.backend_mut().insert_account_info(address, account);
267 Ok(())
268 }
269
270 pub fn set_storage(
272 &mut self,
273 address: Address,
274 storage: HashMap<U256, U256>,
275 ) -> BackendResult<()> {
276 self.backend_mut().replace_account_storage(address, storage)?;
277 Ok(())
278 }
279
280 pub fn set_storage_slot(
282 &mut self,
283 address: Address,
284 slot: U256,
285 value: U256,
286 ) -> BackendResult<()> {
287 self.backend_mut().insert_account_storage(address, slot, value)?;
288 Ok(())
289 }
290
291 pub fn is_empty_code(&self, address: Address) -> BackendResult<bool> {
293 Ok(self.backend().basic_ref(address)?.map(|acc| acc.is_empty_code_hash()).unwrap_or(true))
294 }
295
296 #[inline]
297 pub fn set_tracing(&mut self, mode: TraceMode) -> &mut Self {
298 self.inspector_mut().tracing(mode);
299 self
300 }
301
302 #[inline]
303 pub fn set_script_execution(&mut self, script_address: Address) {
304 self.inspector_mut().script(script_address);
305 }
306
307 #[inline]
308 pub fn set_trace_printer(&mut self, trace_printer: bool) -> &mut Self {
309 self.inspector_mut().print(trace_printer);
310 self
311 }
312
313 #[inline]
314 pub fn create2_deployer(&self) -> Address {
315 self.inspector().create2_deployer()
316 }
317
318 pub fn deploy(
323 &mut self,
324 from: Address,
325 code: Bytes,
326 value: U256,
327 rd: Option<&RevertDecoder>,
328 ) -> Result<DeployResult, EvmError> {
329 let env = self.build_test_env(from, TxKind::Create, code, value);
330 self.deploy_with_env(env, rd)
331 }
332
333 #[instrument(name = "deploy", level = "debug", skip_all)]
340 pub fn deploy_with_env(
341 &mut self,
342 env: Env,
343 rd: Option<&RevertDecoder>,
344 ) -> Result<DeployResult, EvmError> {
345 assert!(
346 matches!(env.tx.kind, TxKind::Create),
347 "Expected create transaction, got {:?}",
348 env.tx.kind
349 );
350 trace!(sender=%env.tx.caller, "deploying contract");
351
352 let mut result = self.transact_with_env(env)?;
353 result = result.into_result(rd)?;
354 let Some(Output::Create(_, Some(address))) = result.out else {
355 panic!("Deployment succeeded, but no address was returned: {result:#?}");
356 };
357
358 self.backend_mut().add_persistent_account(address);
361
362 debug!(%address, "deployed contract");
363
364 Ok(DeployResult { raw: result, address })
365 }
366
367 #[instrument(name = "setup", level = "debug", skip_all)]
374 pub fn setup(
375 &mut self,
376 from: Option<Address>,
377 to: Address,
378 rd: Option<&RevertDecoder>,
379 ) -> Result<RawCallResult, EvmError> {
380 trace!(?from, ?to, "setting up contract");
381
382 let from = from.unwrap_or(CALLER);
383 self.backend_mut().set_test_contract(to).set_caller(from);
384 let calldata = Bytes::from_static(&ITest::setUpCall::SELECTOR);
385 let mut res = self.transact_raw(from, to, calldata, U256::ZERO)?;
386 res = res.into_result(rd)?;
387
388 self.env_mut().evm_env.block_env = res.env.evm_env.block_env.clone();
390 self.env_mut().evm_env.cfg_env.chain_id = res.env.evm_env.cfg_env.chain_id;
392
393 let success =
394 self.is_raw_call_success(to, Cow::Borrowed(&res.state_changeset), &res, false);
395 if !success {
396 return Err(res.into_execution_error("execution error".to_string()).into());
397 }
398
399 Ok(res)
400 }
401
402 pub fn call(
404 &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.call_raw(from, to, calldata, value)?;
414 result.into_decoded_result(func, rd)
415 }
416
417 pub fn call_sol<C: SolCall>(
419 &self,
420 from: Address,
421 to: Address,
422 args: &C,
423 value: U256,
424 rd: Option<&RevertDecoder>,
425 ) -> Result<CallResult<C::Return>, EvmError> {
426 let calldata = Bytes::from(args.abi_encode());
427 let mut raw = self.call_raw(from, to, calldata, value)?;
428 raw = raw.into_result(rd)?;
429 Ok(CallResult { decoded_result: C::abi_decode_returns(&raw.result)?, raw })
430 }
431
432 pub fn transact(
434 &mut self,
435 from: Address,
436 to: Address,
437 func: &Function,
438 args: &[DynSolValue],
439 value: U256,
440 rd: Option<&RevertDecoder>,
441 ) -> Result<CallResult, EvmError> {
442 let calldata = Bytes::from(func.abi_encode_input(args)?);
443 let result = self.transact_raw(from, to, calldata, value)?;
444 result.into_decoded_result(func, rd)
445 }
446
447 pub fn call_raw(
449 &self,
450 from: Address,
451 to: Address,
452 calldata: Bytes,
453 value: U256,
454 ) -> eyre::Result<RawCallResult> {
455 let env = self.build_test_env(from, TxKind::Call(to), calldata, value);
456 self.call_with_env(env)
457 }
458
459 pub fn call_raw_with_authorization(
462 &mut self,
463 from: Address,
464 to: Address,
465 calldata: Bytes,
466 value: U256,
467 authorization_list: Vec<SignedAuthorization>,
468 ) -> eyre::Result<RawCallResult> {
469 let mut env = self.build_test_env(from, to.into(), calldata, value);
470 env.tx.set_signed_authorization(authorization_list);
471 env.tx.tx_type = 4;
472 self.call_with_env(env)
473 }
474
475 pub fn transact_raw(
477 &mut self,
478 from: Address,
479 to: Address,
480 calldata: Bytes,
481 value: U256,
482 ) -> eyre::Result<RawCallResult> {
483 let env = self.build_test_env(from, TxKind::Call(to), calldata, value);
484 self.transact_with_env(env)
485 }
486
487 #[instrument(name = "call", level = "debug", skip_all)]
491 pub fn call_with_env(&self, mut env: Env) -> eyre::Result<RawCallResult> {
492 let mut inspector = self.inspector().clone();
493 let mut backend = CowBackend::new_borrowed(self.backend());
494 let result = backend.inspect(&mut env, &mut inspector)?;
495 convert_executed_result(env, inspector, result, backend.has_state_snapshot_failure())
496 }
497
498 #[instrument(name = "transact", level = "debug", skip_all)]
500 pub fn transact_with_env(&mut self, mut env: Env) -> eyre::Result<RawCallResult> {
501 let mut inspector = self.inspector().clone();
502 let backend = self.backend_mut();
503 let result = backend.inspect(&mut env, &mut inspector)?;
504 let mut result =
505 convert_executed_result(env, inspector, result, backend.has_state_snapshot_failure())?;
506 self.commit(&mut result);
507 Ok(result)
508 }
509
510 #[instrument(name = "commit", level = "debug", skip_all)]
515 fn commit(&mut self, result: &mut RawCallResult) {
516 self.backend_mut().commit(result.state_changeset.clone());
518
519 self.inspector_mut().cheatcodes = result.cheatcodes.take();
521 if let Some(cheats) = self.inspector_mut().cheatcodes.as_mut() {
522 cheats.broadcastable_transactions.clear();
524 cheats.ignored_traces.ignored.clear();
525
526 if let Some(last_pause_call) = cheats.ignored_traces.last_pause_call.as_mut() {
529 *last_pause_call = (0, 0);
530 }
531 }
532
533 self.inspector_mut().set_env(&result.env);
535 }
536
537 pub fn is_raw_call_mut_success(
542 &self,
543 address: Address,
544 call_result: &mut RawCallResult,
545 should_fail: bool,
546 ) -> bool {
547 self.is_raw_call_success(
548 address,
549 Cow::Owned(std::mem::take(&mut call_result.state_changeset)),
550 call_result,
551 should_fail,
552 )
553 }
554
555 pub fn is_raw_call_success(
559 &self,
560 address: Address,
561 state_changeset: Cow<'_, StateChangeset>,
562 call_result: &RawCallResult,
563 should_fail: bool,
564 ) -> bool {
565 if call_result.has_state_snapshot_failure {
566 return should_fail;
568 }
569 self.is_success(address, call_result.reverted, state_changeset, should_fail)
570 }
571
572 pub fn is_success(
594 &self,
595 address: Address,
596 reverted: bool,
597 state_changeset: Cow<'_, StateChangeset>,
598 should_fail: bool,
599 ) -> bool {
600 let success = self.is_success_raw(address, reverted, state_changeset);
601 should_fail ^ success
602 }
603
604 #[instrument(name = "is_success", level = "debug", skip_all)]
605 fn is_success_raw(
606 &self,
607 address: Address,
608 reverted: bool,
609 state_changeset: Cow<'_, StateChangeset>,
610 ) -> bool {
611 if reverted {
613 return false;
614 }
615
616 if self.backend().has_state_snapshot_failure() {
618 return false;
619 }
620
621 if let Some(acc) = state_changeset.get(&CHEATCODE_ADDRESS)
623 && let Some(failed_slot) = acc.storage.get(&GLOBAL_FAIL_SLOT)
624 && !failed_slot.present_value().is_zero()
625 {
626 return false;
627 }
628 if let Ok(failed_slot) = self.backend().storage_ref(CHEATCODE_ADDRESS, GLOBAL_FAIL_SLOT)
629 && !failed_slot.is_zero()
630 {
631 return false;
632 }
633
634 if !self.legacy_assertions {
635 return true;
636 }
637
638 {
640 let mut backend = self.backend().clone_empty();
642
643 for address in [address, CHEATCODE_ADDRESS] {
646 let Ok(acc) = self.backend().basic_ref(address) else { return false };
647 backend.insert_account_info(address, acc.unwrap_or_default());
648 }
649
650 backend.commit(state_changeset.into_owned());
655
656 let executor = self.clone_with_backend(backend);
658 let call = executor.call_sol(CALLER, address, &ITest::failedCall {}, U256::ZERO, None);
659 match call {
660 Ok(CallResult { raw: _, decoded_result: failed }) => {
661 trace!(failed, "DSTest::failed()");
662 !failed
663 }
664 Err(err) => {
665 trace!(%err, "failed to call DSTest::failed()");
666 true
667 }
668 }
669 }
670 }
671
672 fn build_test_env(&self, caller: Address, kind: TxKind, data: Bytes, value: U256) -> Env {
677 Env {
678 evm_env: EvmEnv {
679 cfg_env: {
680 let mut cfg = self.env().evm_env.cfg_env.clone();
681 cfg.spec = self.spec_id();
682 cfg
683 },
684 block_env: BlockEnv {
688 basefee: 0,
689 gas_limit: self.gas_limit,
690 ..self.env().evm_env.block_env.clone()
691 },
692 },
693 tx: TxEnv {
694 caller,
695 kind,
696 data,
697 value,
698 gas_price: 0,
700 gas_priority_fee: None,
701 gas_limit: self.gas_limit,
702 chain_id: Some(self.env().evm_env.cfg_env.chain_id),
703 ..self.env().tx.clone()
704 },
705 }
706 }
707
708 pub fn call_sol_default<C: SolCall>(&self, to: Address, args: &C) -> C::Return
709 where
710 C::Return: Default,
711 {
712 self.call_sol(CALLER, to, args, U256::ZERO, None)
713 .map(|c| c.decoded_result)
714 .inspect_err(|e| warn!(target: "forge::test", "failed calling {:?}: {e}", C::SIGNATURE))
715 .unwrap_or_default()
716 }
717}
718
719#[derive(Debug, thiserror::Error)]
721#[error("execution reverted: {reason} (gas: {})", raw.gas_used)]
722pub struct ExecutionErr {
723 pub raw: RawCallResult,
725 pub reason: String,
727}
728
729impl std::ops::Deref for ExecutionErr {
730 type Target = RawCallResult;
731
732 #[inline]
733 fn deref(&self) -> &Self::Target {
734 &self.raw
735 }
736}
737
738impl std::ops::DerefMut for ExecutionErr {
739 #[inline]
740 fn deref_mut(&mut self) -> &mut Self::Target {
741 &mut self.raw
742 }
743}
744
745#[derive(Debug, thiserror::Error)]
746pub enum EvmError {
747 #[error(transparent)]
749 Execution(#[from] Box<ExecutionErr>),
750 #[error(transparent)]
752 Abi(#[from] alloy_dyn_abi::Error),
753 #[error("{0}")]
755 Skip(SkipReason),
756 #[error("{0}")]
758 Eyre(
759 #[from]
760 #[source]
761 eyre::Report,
762 ),
763}
764
765impl From<ExecutionErr> for EvmError {
766 fn from(err: ExecutionErr) -> Self {
767 Self::Execution(Box::new(err))
768 }
769}
770
771impl From<alloy_sol_types::Error> for EvmError {
772 fn from(err: alloy_sol_types::Error) -> Self {
773 Self::Abi(err.into())
774 }
775}
776
777#[derive(Debug)]
779pub struct DeployResult {
780 pub raw: RawCallResult,
782 pub address: Address,
784}
785
786impl std::ops::Deref for DeployResult {
787 type Target = RawCallResult;
788
789 #[inline]
790 fn deref(&self) -> &Self::Target {
791 &self.raw
792 }
793}
794
795impl std::ops::DerefMut for DeployResult {
796 #[inline]
797 fn deref_mut(&mut self) -> &mut Self::Target {
798 &mut self.raw
799 }
800}
801
802impl From<DeployResult> for RawCallResult {
803 fn from(d: DeployResult) -> Self {
804 d.raw
805 }
806}
807
808#[derive(Debug)]
810pub struct RawCallResult {
811 pub exit_reason: Option<InstructionResult>,
813 pub reverted: bool,
815 pub has_state_snapshot_failure: bool,
820 pub result: Bytes,
822 pub gas_used: u64,
824 pub gas_refunded: u64,
826 pub stipend: u64,
828 pub logs: Vec<Log>,
830 pub labels: AddressHashMap<String>,
832 pub traces: Option<SparsedTraceArena>,
834 pub line_coverage: Option<HitMaps>,
836 pub edge_coverage: Option<Vec<u8>>,
838 pub transactions: Option<BroadcastableTransactions>,
840 pub state_changeset: StateChangeset,
842 pub env: Env,
844 pub cheatcodes: Option<Cheatcodes>,
846 pub out: Option<Output>,
848 pub chisel_state: Option<(Vec<U256>, Vec<u8>, Option<InstructionResult>)>,
850 pub reverter: Option<Address>,
851}
852
853impl Default for RawCallResult {
854 fn default() -> Self {
855 Self {
856 exit_reason: None,
857 reverted: false,
858 has_state_snapshot_failure: false,
859 result: Bytes::new(),
860 gas_used: 0,
861 gas_refunded: 0,
862 stipend: 0,
863 logs: Vec::new(),
864 labels: HashMap::default(),
865 traces: None,
866 line_coverage: None,
867 edge_coverage: None,
868 transactions: None,
869 state_changeset: HashMap::default(),
870 env: Env::default(),
871 cheatcodes: Default::default(),
872 out: None,
873 chisel_state: None,
874 reverter: None,
875 }
876 }
877}
878
879impl RawCallResult {
880 pub fn from_evm_result(r: Result<Self, EvmError>) -> eyre::Result<(Self, Option<String>)> {
882 match r {
883 Ok(r) => Ok((r, None)),
884 Err(EvmError::Execution(e)) => Ok((e.raw, Some(e.reason))),
885 Err(e) => Err(e.into()),
886 }
887 }
888
889 pub fn from_execution_result(r: Result<Self, ExecutionErr>) -> (Self, Option<String>) {
891 match r {
892 Ok(r) => (r, None),
893 Err(e) => (e.raw, Some(e.reason)),
894 }
895 }
896
897 pub fn into_evm_error(self, rd: Option<&RevertDecoder>) -> EvmError {
899 if let Some(reason) = SkipReason::decode(&self.result) {
900 return EvmError::Skip(reason);
901 }
902 let reason = rd.unwrap_or_default().decode(&self.result, self.exit_reason);
903 EvmError::Execution(Box::new(self.into_execution_error(reason)))
904 }
905
906 pub fn into_execution_error(self, reason: String) -> ExecutionErr {
908 ExecutionErr { raw: self, reason }
909 }
910
911 pub fn into_result(self, rd: Option<&RevertDecoder>) -> Result<Self, EvmError> {
913 if let Some(reason) = self.exit_reason
914 && reason.is_ok()
915 {
916 Ok(self)
917 } else {
918 Err(self.into_evm_error(rd))
919 }
920 }
921
922 pub fn into_decoded_result(
924 mut self,
925 func: &Function,
926 rd: Option<&RevertDecoder>,
927 ) -> Result<CallResult, EvmError> {
928 self = self.into_result(rd)?;
929 let mut result = func.abi_decode_output(&self.result)?;
930 let decoded_result = if result.len() == 1 {
931 result.pop().unwrap()
932 } else {
933 DynSolValue::Tuple(result)
935 };
936 Ok(CallResult { raw: self, decoded_result })
937 }
938
939 pub fn transactions(&self) -> Option<&BroadcastableTransactions> {
941 self.cheatcodes.as_ref().map(|c| &c.broadcastable_transactions)
942 }
943
944 pub fn merge_edge_coverage(&mut self, history_map: &mut [u8]) -> (bool, bool) {
947 let mut new_coverage = false;
948 let mut is_edge = false;
949 if let Some(x) = &mut self.edge_coverage {
950 for (curr, hist) in std::iter::zip(x, history_map) {
953 if *curr > 0 {
955 let bucket = match *curr {
957 0 => 0,
958 1 => 1,
959 2 => 2,
960 3 => 4,
961 4..=7 => 8,
962 8..=15 => 16,
963 16..=31 => 32,
964 32..=127 => 64,
965 128..=255 => 128,
966 };
967
968 if *hist < bucket {
970 if *hist == 0 {
971 is_edge = true;
973 }
974 *hist = bucket;
975 new_coverage = true;
976 }
977
978 *curr = 0;
980 }
981 }
982 }
983 (new_coverage, is_edge)
984 }
985}
986
987pub struct CallResult<T = DynSolValue> {
989 pub raw: RawCallResult,
991 pub decoded_result: T,
993}
994
995impl std::ops::Deref for CallResult {
996 type Target = RawCallResult;
997
998 #[inline]
999 fn deref(&self) -> &Self::Target {
1000 &self.raw
1001 }
1002}
1003
1004impl std::ops::DerefMut for CallResult {
1005 #[inline]
1006 fn deref_mut(&mut self) -> &mut Self::Target {
1007 &mut self.raw
1008 }
1009}
1010
1011fn convert_executed_result(
1013 env: Env,
1014 inspector: InspectorStack,
1015 ResultAndState { result, state: state_changeset }: ResultAndState,
1016 has_state_snapshot_failure: bool,
1017) -> eyre::Result<RawCallResult> {
1018 let (exit_reason, gas_refunded, gas_used, out, exec_logs) = match result {
1019 ExecutionResult::Success { reason, gas_used, gas_refunded, output, logs, .. } => {
1020 (reason.into(), gas_refunded, gas_used, Some(output), logs)
1021 }
1022 ExecutionResult::Revert { gas_used, output } => {
1023 (InstructionResult::Revert, 0_u64, gas_used, Some(Output::Call(output)), vec![])
1025 }
1026 ExecutionResult::Halt { reason, gas_used } => {
1027 (reason.into(), 0_u64, gas_used, None, vec![])
1028 }
1029 };
1030 let gas = revm::interpreter::gas::calculate_initial_tx_gas(
1031 env.evm_env.cfg_env.spec,
1032 &env.tx.data,
1033 env.tx.kind.is_create(),
1034 env.tx.access_list.len().try_into()?,
1035 0,
1036 0,
1037 );
1038
1039 let result = match &out {
1040 Some(Output::Call(data)) => data.clone(),
1041 _ => Bytes::new(),
1042 };
1043
1044 let InspectorData {
1045 mut logs,
1046 labels,
1047 traces,
1048 line_coverage,
1049 edge_coverage,
1050 cheatcodes,
1051 chisel_state,
1052 reverter,
1053 } = inspector.collect();
1054
1055 if logs.is_empty() {
1056 logs = exec_logs;
1057 }
1058
1059 let transactions = cheatcodes
1060 .as_ref()
1061 .map(|c| c.broadcastable_transactions.clone())
1062 .filter(|txs| !txs.is_empty());
1063
1064 Ok(RawCallResult {
1065 exit_reason: Some(exit_reason),
1066 reverted: !matches!(exit_reason, return_ok!()),
1067 has_state_snapshot_failure,
1068 result,
1069 gas_used,
1070 gas_refunded,
1071 stipend: gas.initial_gas,
1072 logs,
1073 labels,
1074 traces,
1075 line_coverage,
1076 edge_coverage,
1077 transactions,
1078 state_changeset,
1079 env,
1080 cheatcodes,
1081 out,
1082 chisel_state,
1083 reverter,
1084 })
1085}
1086
1087pub struct FuzzTestTimer {
1089 inner: Option<(Instant, Duration)>,
1091}
1092
1093impl FuzzTestTimer {
1094 pub fn new(timeout: Option<u32>) -> Self {
1095 Self { inner: timeout.map(|timeout| (Instant::now(), Duration::from_secs(timeout.into()))) }
1096 }
1097
1098 pub fn is_timed_out(&self) -> bool {
1100 self.inner.is_some_and(|(start, duration)| start.elapsed() > duration)
1101 }
1102}