1use crate::{
10 Env,
11 inspectors::{
12 Cheatcodes, InspectorData, InspectorStack, cheatcodes::BroadcastableTransactions,
13 },
14};
15use alloy_dyn_abi::{DynSolValue, FunctionExt, JsonAbiExt};
16use alloy_json_abi::Function;
17use alloy_primitives::{
18 Address, Bytes, Log, TxKind, U256, keccak256,
19 map::{AddressHashMap, HashMap},
20};
21use alloy_sol_types::{SolCall, sol};
22use foundry_evm_core::{
23 EvmEnv,
24 backend::{Backend, BackendError, BackendResult, CowBackend, DatabaseExt, GLOBAL_FAIL_SLOT},
25 constants::{
26 CALLER, CHEATCODE_ADDRESS, CHEATCODE_CONTRACT_HASH, DEFAULT_CREATE2_DEPLOYER,
27 DEFAULT_CREATE2_DEPLOYER_CODE, DEFAULT_CREATE2_DEPLOYER_DEPLOYER,
28 },
29 decode::{RevertDecoder, SkipReason},
30 utils::StateChangeset,
31};
32use foundry_evm_coverage::HitMaps;
33use foundry_evm_traces::{SparsedTraceArena, TraceMode};
34use revm::{
35 bytecode::Bytecode,
36 context::{BlockEnv, TxEnv},
37 context_interface::{
38 result::{ExecutionResult, Output, ResultAndState},
39 transaction::SignedAuthorization,
40 },
41 database::{DatabaseCommit, DatabaseRef},
42 interpreter::{InstructionResult, return_ok},
43 primitives::hardfork::SpecId,
44};
45use std::{
46 borrow::Cow,
47 sync::{
48 Arc,
49 atomic::{AtomicBool, Ordering},
50 },
51 time::{Duration, Instant},
52};
53
54mod builder;
55pub use builder::ExecutorBuilder;
56
57pub mod fuzz;
58pub use fuzz::FuzzedExecutor;
59
60pub mod invariant;
61pub use invariant::InvariantExecutor;
62
63mod corpus;
64mod trace;
65
66pub use trace::TracingExecutor;
67
68const DURATION_BETWEEN_METRICS_REPORT: Duration = Duration::from_secs(5);
69
70sol! {
71 interface ITest {
72 function setUp() external;
73 function failed() external view returns (bool failed);
74
75 #[derive(Default)]
76 function beforeTestSetup(bytes4 testSelector) public view returns (bytes[] memory beforeTestCalldata);
77 }
78}
79
80#[derive(Clone, Debug)]
92pub struct Executor {
93 backend: Arc<Backend>,
102 env: Env,
104 inspector: InspectorStack,
106 gas_limit: u64,
108 legacy_assertions: bool,
110}
111
112impl Executor {
113 #[inline]
115 pub fn new(
116 mut backend: Backend,
117 env: Env,
118 inspector: InspectorStack,
119 gas_limit: u64,
120 legacy_assertions: bool,
121 ) -> Self {
122 backend.insert_account_info(
125 CHEATCODE_ADDRESS,
126 revm::state::AccountInfo {
127 code: Some(Bytecode::new_raw(Bytes::from_static(&[0]))),
128 code_hash: CHEATCODE_CONTRACT_HASH,
131 ..Default::default()
132 },
133 );
134
135 Self { backend: Arc::new(backend), env, inspector, gas_limit, legacy_assertions }
136 }
137
138 fn clone_with_backend(&self, backend: Backend) -> Self {
139 let env = Env::new_with_spec_id(
140 self.env.evm_env.cfg_env.clone(),
141 self.env.evm_env.block_env.clone(),
142 self.env.tx.clone(),
143 self.spec_id(),
144 );
145 Self {
146 backend: Arc::new(backend),
147 env,
148 inspector: self.inspector().clone(),
149 gas_limit: self.gas_limit,
150 legacy_assertions: self.legacy_assertions,
151 }
152 }
153
154 pub fn backend(&self) -> &Backend {
156 &self.backend
157 }
158
159 pub fn backend_mut(&mut self) -> &mut Backend {
164 Arc::make_mut(&mut self.backend)
165 }
166
167 pub fn env(&self) -> &Env {
169 &self.env
170 }
171
172 pub fn env_mut(&mut self) -> &mut Env {
174 &mut self.env
175 }
176
177 pub fn inspector(&self) -> &InspectorStack {
179 &self.inspector
180 }
181
182 pub fn inspector_mut(&mut self) -> &mut InspectorStack {
184 &mut self.inspector
185 }
186
187 pub fn spec_id(&self) -> SpecId {
189 self.env.evm_env.cfg_env.spec
190 }
191
192 pub fn set_spec_id(&mut self, spec_id: SpecId) {
194 self.env.evm_env.cfg_env.spec = spec_id;
195 }
196
197 pub fn gas_limit(&self) -> u64 {
202 self.gas_limit
203 }
204
205 pub fn set_gas_limit(&mut self, gas_limit: u64) {
207 self.gas_limit = gas_limit;
208 }
209
210 pub fn legacy_assertions(&self) -> bool {
213 self.legacy_assertions
214 }
215
216 pub fn set_legacy_assertions(&mut self, legacy_assertions: bool) {
219 self.legacy_assertions = legacy_assertions;
220 }
221
222 pub fn deploy_create2_deployer(&mut self) -> eyre::Result<()> {
224 trace!("deploying local create2 deployer");
225 let create2_deployer_account = self
226 .backend()
227 .basic_ref(DEFAULT_CREATE2_DEPLOYER)?
228 .ok_or_else(|| BackendError::MissingAccount(DEFAULT_CREATE2_DEPLOYER))?;
229
230 if create2_deployer_account.code.is_none_or(|code| code.is_empty()) {
232 let creator = DEFAULT_CREATE2_DEPLOYER_DEPLOYER;
233
234 let initial_balance = self.get_balance(creator)?;
236 self.set_balance(creator, U256::MAX)?;
237
238 let res =
239 self.deploy(creator, DEFAULT_CREATE2_DEPLOYER_CODE.into(), U256::ZERO, None)?;
240 trace!(create2=?res.address, "deployed local create2 deployer");
241
242 self.set_balance(creator, initial_balance)?;
243 }
244 Ok(())
245 }
246
247 pub fn set_balance(&mut self, address: Address, amount: U256) -> BackendResult<()> {
249 trace!(?address, ?amount, "setting account balance");
250 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
251 account.balance = amount;
252 self.backend_mut().insert_account_info(address, account);
253 Ok(())
254 }
255
256 pub fn get_balance(&self, address: Address) -> BackendResult<U256> {
258 Ok(self.backend().basic_ref(address)?.map(|acc| acc.balance).unwrap_or_default())
259 }
260
261 pub fn set_nonce(&mut self, address: Address, nonce: u64) -> BackendResult<()> {
263 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
264 account.nonce = nonce;
265 self.backend_mut().insert_account_info(address, account);
266 self.env_mut().tx.nonce = nonce;
267 Ok(())
268 }
269
270 pub fn get_nonce(&self, address: Address) -> BackendResult<u64> {
272 Ok(self.backend().basic_ref(address)?.map(|acc| acc.nonce).unwrap_or_default())
273 }
274
275 pub fn set_code(&mut self, address: Address, code: Bytecode) -> BackendResult<()> {
277 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
278 account.code_hash = keccak256(code.original_byte_slice());
279 account.code = Some(code);
280 self.backend_mut().insert_account_info(address, account);
281 Ok(())
282 }
283
284 pub fn set_storage(
286 &mut self,
287 address: Address,
288 storage: HashMap<U256, U256>,
289 ) -> BackendResult<()> {
290 self.backend_mut().replace_account_storage(address, storage)?;
291 Ok(())
292 }
293
294 pub fn set_storage_slot(
296 &mut self,
297 address: Address,
298 slot: U256,
299 value: U256,
300 ) -> BackendResult<()> {
301 self.backend_mut().insert_account_storage(address, slot, value)?;
302 Ok(())
303 }
304
305 pub fn is_empty_code(&self, address: Address) -> BackendResult<bool> {
307 Ok(self.backend().basic_ref(address)?.map(|acc| acc.is_empty_code_hash()).unwrap_or(true))
308 }
309
310 #[inline]
311 pub fn set_tracing(&mut self, mode: TraceMode) -> &mut Self {
312 self.inspector_mut().tracing(mode);
313 self
314 }
315
316 #[inline]
317 pub fn set_script_execution(&mut self, script_address: Address) {
318 self.inspector_mut().script(script_address);
319 }
320
321 #[inline]
322 pub fn set_trace_printer(&mut self, trace_printer: bool) -> &mut Self {
323 self.inspector_mut().print(trace_printer);
324 self
325 }
326
327 #[inline]
328 pub fn create2_deployer(&self) -> Address {
329 self.inspector().create2_deployer
330 }
331
332 pub fn deploy(
337 &mut self,
338 from: Address,
339 code: Bytes,
340 value: U256,
341 rd: Option<&RevertDecoder>,
342 ) -> Result<DeployResult, EvmError> {
343 let env = self.build_test_env(from, TxKind::Create, code, value);
344 self.deploy_with_env(env, rd)
345 }
346
347 #[instrument(name = "deploy", level = "debug", skip_all)]
354 pub fn deploy_with_env(
355 &mut self,
356 env: Env,
357 rd: Option<&RevertDecoder>,
358 ) -> Result<DeployResult, EvmError> {
359 assert!(
360 matches!(env.tx.kind, TxKind::Create),
361 "Expected create transaction, got {:?}",
362 env.tx.kind
363 );
364 trace!(sender=%env.tx.caller, "deploying contract");
365
366 let mut result = self.transact_with_env(env)?;
367 result = result.into_result(rd)?;
368 let Some(Output::Create(_, Some(address))) = result.out else {
369 panic!("Deployment succeeded, but no address was returned: {result:#?}");
370 };
371
372 self.backend_mut().add_persistent_account(address);
375
376 trace!(%address, "deployed contract");
377
378 Ok(DeployResult { raw: result, address })
379 }
380
381 #[instrument(name = "setup", level = "debug", skip_all)]
388 pub fn setup(
389 &mut self,
390 from: Option<Address>,
391 to: Address,
392 rd: Option<&RevertDecoder>,
393 ) -> Result<RawCallResult, EvmError> {
394 trace!(?from, ?to, "setting up contract");
395
396 let from = from.unwrap_or(CALLER);
397 self.backend_mut().set_test_contract(to).set_caller(from);
398 let calldata = Bytes::from_static(&ITest::setUpCall::SELECTOR);
399 let mut res = self.transact_raw(from, to, calldata, U256::ZERO)?;
400 res = res.into_result(rd)?;
401
402 self.env_mut().evm_env.block_env = res.env.evm_env.block_env.clone();
404 self.env_mut().evm_env.cfg_env.chain_id = res.env.evm_env.cfg_env.chain_id;
406
407 let success =
408 self.is_raw_call_success(to, Cow::Borrowed(&res.state_changeset), &res, false);
409 if !success {
410 return Err(res.into_execution_error("execution error".to_string()).into());
411 }
412
413 Ok(res)
414 }
415
416 pub fn call(
418 &self,
419 from: Address,
420 to: Address,
421 func: &Function,
422 args: &[DynSolValue],
423 value: U256,
424 rd: Option<&RevertDecoder>,
425 ) -> Result<CallResult, EvmError> {
426 let calldata = Bytes::from(func.abi_encode_input(args)?);
427 let result = self.call_raw(from, to, calldata, value)?;
428 result.into_decoded_result(func, rd)
429 }
430
431 pub fn call_sol<C: SolCall>(
433 &self,
434 from: Address,
435 to: Address,
436 args: &C,
437 value: U256,
438 rd: Option<&RevertDecoder>,
439 ) -> Result<CallResult<C::Return>, EvmError> {
440 let calldata = Bytes::from(args.abi_encode());
441 let mut raw = self.call_raw(from, to, calldata, value)?;
442 raw = raw.into_result(rd)?;
443 Ok(CallResult { decoded_result: C::abi_decode_returns(&raw.result)?, raw })
444 }
445
446 pub fn transact(
448 &mut self,
449 from: Address,
450 to: Address,
451 func: &Function,
452 args: &[DynSolValue],
453 value: U256,
454 rd: Option<&RevertDecoder>,
455 ) -> Result<CallResult, EvmError> {
456 let calldata = Bytes::from(func.abi_encode_input(args)?);
457 let result = self.transact_raw(from, to, calldata, value)?;
458 result.into_decoded_result(func, rd)
459 }
460
461 pub fn call_raw(
463 &self,
464 from: Address,
465 to: Address,
466 calldata: Bytes,
467 value: U256,
468 ) -> eyre::Result<RawCallResult> {
469 let env = self.build_test_env(from, TxKind::Call(to), calldata, value);
470 self.call_with_env(env)
471 }
472
473 pub fn call_raw_with_authorization(
476 &mut self,
477 from: Address,
478 to: Address,
479 calldata: Bytes,
480 value: U256,
481 authorization_list: Vec<SignedAuthorization>,
482 ) -> eyre::Result<RawCallResult> {
483 let mut env = self.build_test_env(from, to.into(), calldata, value);
484 env.tx.set_signed_authorization(authorization_list);
485 env.tx.tx_type = 4;
486 self.call_with_env(env)
487 }
488
489 pub fn transact_raw(
491 &mut self,
492 from: Address,
493 to: Address,
494 calldata: Bytes,
495 value: U256,
496 ) -> eyre::Result<RawCallResult> {
497 let env = self.build_test_env(from, TxKind::Call(to), calldata, value);
498 self.transact_with_env(env)
499 }
500
501 pub fn transact_raw_with_authorization(
504 &mut self,
505 from: Address,
506 to: Address,
507 calldata: Bytes,
508 value: U256,
509 authorization_list: Vec<SignedAuthorization>,
510 ) -> eyre::Result<RawCallResult> {
511 let mut env = self.build_test_env(from, TxKind::Call(to), calldata, value);
512 env.tx.set_signed_authorization(authorization_list);
513 env.tx.tx_type = 4;
514 self.transact_with_env(env)
515 }
516
517 #[instrument(name = "call", level = "debug", skip_all)]
521 pub fn call_with_env(&self, mut env: Env) -> eyre::Result<RawCallResult> {
522 let mut stack = self.inspector().clone();
523 let mut backend = CowBackend::new_borrowed(self.backend());
524 let result = backend.inspect(&mut env, stack.as_inspector())?;
525 convert_executed_result(env, stack, result, backend.has_state_snapshot_failure())
526 }
527
528 #[instrument(name = "transact", level = "debug", skip_all)]
530 pub fn transact_with_env(&mut self, mut env: Env) -> eyre::Result<RawCallResult> {
531 let mut stack = self.inspector().clone();
532 let backend = self.backend_mut();
533 let result = backend.inspect(&mut env, stack.as_inspector())?;
534 let mut result =
535 convert_executed_result(env, stack, result, backend.has_state_snapshot_failure())?;
536 self.commit(&mut result);
537 Ok(result)
538 }
539
540 #[instrument(name = "commit", level = "debug", skip_all)]
545 fn commit(&mut self, result: &mut RawCallResult) {
546 self.backend_mut().commit(result.state_changeset.clone());
548
549 self.inspector_mut().cheatcodes = result.cheatcodes.take();
551 if let Some(cheats) = self.inspector_mut().cheatcodes.as_mut() {
552 cheats.broadcastable_transactions.clear();
554 cheats.ignored_traces.ignored.clear();
555
556 if let Some(last_pause_call) = cheats.ignored_traces.last_pause_call.as_mut() {
559 *last_pause_call = (0, 0);
560 }
561 }
562
563 self.inspector_mut().set_env(&result.env);
565 }
566
567 pub fn is_raw_call_mut_success(
572 &self,
573 address: Address,
574 call_result: &mut RawCallResult,
575 should_fail: bool,
576 ) -> bool {
577 self.is_raw_call_success(
578 address,
579 Cow::Owned(std::mem::take(&mut call_result.state_changeset)),
580 call_result,
581 should_fail,
582 )
583 }
584
585 pub fn is_raw_call_success(
589 &self,
590 address: Address,
591 state_changeset: Cow<'_, StateChangeset>,
592 call_result: &RawCallResult,
593 should_fail: bool,
594 ) -> bool {
595 if call_result.has_state_snapshot_failure {
596 return should_fail;
598 }
599 self.is_success(address, call_result.reverted, state_changeset, should_fail)
600 }
601
602 pub fn is_success(
624 &self,
625 address: Address,
626 reverted: bool,
627 state_changeset: Cow<'_, StateChangeset>,
628 should_fail: bool,
629 ) -> bool {
630 let success = self.is_success_raw(address, reverted, state_changeset);
631 should_fail ^ success
632 }
633
634 #[instrument(name = "is_success", level = "debug", skip_all)]
635 fn is_success_raw(
636 &self,
637 address: Address,
638 reverted: bool,
639 state_changeset: Cow<'_, StateChangeset>,
640 ) -> bool {
641 if reverted {
643 return false;
644 }
645
646 if self.backend().has_state_snapshot_failure() {
648 return false;
649 }
650
651 if let Some(acc) = state_changeset.get(&CHEATCODE_ADDRESS)
653 && let Some(failed_slot) = acc.storage.get(&GLOBAL_FAIL_SLOT)
654 && !failed_slot.present_value().is_zero()
655 {
656 return false;
657 }
658 if let Ok(failed_slot) = self.backend().storage_ref(CHEATCODE_ADDRESS, GLOBAL_FAIL_SLOT)
659 && !failed_slot.is_zero()
660 {
661 return false;
662 }
663
664 if !self.legacy_assertions {
665 return true;
666 }
667
668 {
670 let mut backend = self.backend().clone_empty();
672
673 for address in [address, CHEATCODE_ADDRESS] {
676 let Ok(acc) = self.backend().basic_ref(address) else { return false };
677 backend.insert_account_info(address, acc.unwrap_or_default());
678 }
679
680 backend.commit(state_changeset.into_owned());
685
686 let executor = self.clone_with_backend(backend);
688 let call = executor.call_sol(CALLER, address, &ITest::failedCall {}, U256::ZERO, None);
689 match call {
690 Ok(CallResult { raw: _, decoded_result: failed }) => {
691 trace!(failed, "DSTest::failed()");
692 !failed
693 }
694 Err(err) => {
695 trace!(%err, "failed to call DSTest::failed()");
696 true
697 }
698 }
699 }
700 }
701
702 fn build_test_env(&self, caller: Address, kind: TxKind, data: Bytes, value: U256) -> Env {
707 Env {
708 evm_env: EvmEnv {
709 cfg_env: {
710 let mut cfg = self.env().evm_env.cfg_env.clone();
711 cfg.spec = self.spec_id();
712 cfg
713 },
714 block_env: BlockEnv {
718 basefee: 0,
719 gas_limit: self.gas_limit,
720 ..self.env().evm_env.block_env.clone()
721 },
722 },
723 tx: TxEnv {
724 caller,
725 kind,
726 data,
727 value,
728 gas_price: 0,
730 gas_priority_fee: None,
731 gas_limit: self.gas_limit,
732 chain_id: Some(self.env().evm_env.cfg_env.chain_id),
733 ..self.env().tx.clone()
734 },
735 }
736 }
737
738 pub fn call_sol_default<C: SolCall>(&self, to: Address, args: &C) -> C::Return
739 where
740 C::Return: Default,
741 {
742 self.call_sol(CALLER, to, args, U256::ZERO, None)
743 .map(|c| c.decoded_result)
744 .inspect_err(|e| warn!(target: "forge::test", "failed calling {:?}: {e}", C::SIGNATURE))
745 .unwrap_or_default()
746 }
747}
748
749#[derive(Debug, thiserror::Error)]
751#[error("execution reverted: {reason} (gas: {})", raw.gas_used)]
752pub struct ExecutionErr {
753 pub raw: RawCallResult,
755 pub reason: String,
757}
758
759impl std::ops::Deref for ExecutionErr {
760 type Target = RawCallResult;
761
762 #[inline]
763 fn deref(&self) -> &Self::Target {
764 &self.raw
765 }
766}
767
768impl std::ops::DerefMut for ExecutionErr {
769 #[inline]
770 fn deref_mut(&mut self) -> &mut Self::Target {
771 &mut self.raw
772 }
773}
774
775#[derive(Debug, thiserror::Error)]
776pub enum EvmError {
777 #[error(transparent)]
779 Execution(#[from] Box<ExecutionErr>),
780 #[error(transparent)]
782 Abi(#[from] alloy_dyn_abi::Error),
783 #[error("{0}")]
785 Skip(SkipReason),
786 #[error("{0}")]
788 Eyre(
789 #[from]
790 #[source]
791 eyre::Report,
792 ),
793}
794
795impl From<ExecutionErr> for EvmError {
796 fn from(err: ExecutionErr) -> Self {
797 Self::Execution(Box::new(err))
798 }
799}
800
801impl From<alloy_sol_types::Error> for EvmError {
802 fn from(err: alloy_sol_types::Error) -> Self {
803 Self::Abi(err.into())
804 }
805}
806
807#[derive(Debug)]
809pub struct DeployResult {
810 pub raw: RawCallResult,
812 pub address: Address,
814}
815
816impl std::ops::Deref for DeployResult {
817 type Target = RawCallResult;
818
819 #[inline]
820 fn deref(&self) -> &Self::Target {
821 &self.raw
822 }
823}
824
825impl std::ops::DerefMut for DeployResult {
826 #[inline]
827 fn deref_mut(&mut self) -> &mut Self::Target {
828 &mut self.raw
829 }
830}
831
832impl From<DeployResult> for RawCallResult {
833 fn from(d: DeployResult) -> Self {
834 d.raw
835 }
836}
837
838#[derive(Debug)]
840pub struct RawCallResult {
841 pub exit_reason: Option<InstructionResult>,
843 pub reverted: bool,
845 pub has_state_snapshot_failure: bool,
850 pub result: Bytes,
852 pub gas_used: u64,
854 pub gas_refunded: u64,
856 pub stipend: u64,
858 pub logs: Vec<Log>,
860 pub labels: AddressHashMap<String>,
862 pub traces: Option<SparsedTraceArena>,
864 pub line_coverage: Option<HitMaps>,
866 pub edge_coverage: Option<Vec<u8>>,
868 pub transactions: Option<BroadcastableTransactions>,
870 pub state_changeset: StateChangeset,
872 pub env: Env,
874 pub cheatcodes: Option<Box<Cheatcodes>>,
876 pub out: Option<Output>,
878 pub chisel_state: Option<(Vec<U256>, Vec<u8>)>,
880 pub reverter: Option<Address>,
881}
882
883impl Default for RawCallResult {
884 fn default() -> Self {
885 Self {
886 exit_reason: None,
887 reverted: false,
888 has_state_snapshot_failure: false,
889 result: Bytes::new(),
890 gas_used: 0,
891 gas_refunded: 0,
892 stipend: 0,
893 logs: Vec::new(),
894 labels: HashMap::default(),
895 traces: None,
896 line_coverage: None,
897 edge_coverage: None,
898 transactions: None,
899 state_changeset: HashMap::default(),
900 env: Env::default(),
901 cheatcodes: Default::default(),
902 out: None,
903 chisel_state: None,
904 reverter: None,
905 }
906 }
907}
908
909impl RawCallResult {
910 pub fn from_evm_result(r: Result<Self, EvmError>) -> eyre::Result<(Self, Option<String>)> {
912 match r {
913 Ok(r) => Ok((r, None)),
914 Err(EvmError::Execution(e)) => Ok((e.raw, Some(e.reason))),
915 Err(e) => Err(e.into()),
916 }
917 }
918
919 pub fn into_evm_error(self, rd: Option<&RevertDecoder>) -> EvmError {
921 if let Some(reason) = SkipReason::decode(&self.result) {
922 return EvmError::Skip(reason);
923 }
924 let reason = rd.unwrap_or_default().decode(&self.result, self.exit_reason);
925 EvmError::Execution(Box::new(self.into_execution_error(reason)))
926 }
927
928 pub fn into_execution_error(self, reason: String) -> ExecutionErr {
930 ExecutionErr { raw: self, reason }
931 }
932
933 pub fn into_result(self, rd: Option<&RevertDecoder>) -> Result<Self, EvmError> {
935 if let Some(reason) = self.exit_reason
936 && reason.is_ok()
937 {
938 Ok(self)
939 } else {
940 Err(self.into_evm_error(rd))
941 }
942 }
943
944 pub fn into_decoded_result(
946 mut self,
947 func: &Function,
948 rd: Option<&RevertDecoder>,
949 ) -> Result<CallResult, EvmError> {
950 self = self.into_result(rd)?;
951 let mut result = func.abi_decode_output(&self.result)?;
952 let decoded_result = if result.len() == 1 {
953 result.pop().unwrap()
954 } else {
955 DynSolValue::Tuple(result)
957 };
958 Ok(CallResult { raw: self, decoded_result })
959 }
960
961 pub fn transactions(&self) -> Option<&BroadcastableTransactions> {
963 self.cheatcodes.as_ref().map(|c| &c.broadcastable_transactions)
964 }
965
966 pub fn merge_edge_coverage(&mut self, history_map: &mut [u8]) -> (bool, bool) {
969 let mut new_coverage = false;
970 let mut is_edge = false;
971 if let Some(x) = &mut self.edge_coverage {
972 for (curr, hist) in std::iter::zip(x, history_map) {
975 if *curr > 0 {
977 let bucket = match *curr {
979 0 => 0,
980 1 => 1,
981 2 => 2,
982 3 => 4,
983 4..=7 => 8,
984 8..=15 => 16,
985 16..=31 => 32,
986 32..=127 => 64,
987 128..=255 => 128,
988 };
989
990 if *hist < bucket {
992 if *hist == 0 {
993 is_edge = true;
995 }
996 *hist = bucket;
997 new_coverage = true;
998 }
999
1000 *curr = 0;
1002 }
1003 }
1004 }
1005 (new_coverage, is_edge)
1006 }
1007}
1008
1009pub struct CallResult<T = DynSolValue> {
1011 pub raw: RawCallResult,
1013 pub decoded_result: T,
1015}
1016
1017impl std::ops::Deref for CallResult {
1018 type Target = RawCallResult;
1019
1020 #[inline]
1021 fn deref(&self) -> &Self::Target {
1022 &self.raw
1023 }
1024}
1025
1026impl std::ops::DerefMut for CallResult {
1027 #[inline]
1028 fn deref_mut(&mut self) -> &mut Self::Target {
1029 &mut self.raw
1030 }
1031}
1032
1033fn convert_executed_result(
1035 env: Env,
1036 inspector: InspectorStack,
1037 ResultAndState { result, state: state_changeset }: ResultAndState,
1038 has_state_snapshot_failure: bool,
1039) -> eyre::Result<RawCallResult> {
1040 let (exit_reason, gas_refunded, gas_used, out, exec_logs) = match result {
1041 ExecutionResult::Success { reason, gas_used, gas_refunded, output, logs, .. } => {
1042 (reason.into(), gas_refunded, gas_used, Some(output), logs)
1043 }
1044 ExecutionResult::Revert { gas_used, output } => {
1045 (InstructionResult::Revert, 0_u64, gas_used, Some(Output::Call(output)), vec![])
1047 }
1048 ExecutionResult::Halt { reason, gas_used } => {
1049 (reason.into(), 0_u64, gas_used, None, vec![])
1050 }
1051 };
1052 let gas = revm::interpreter::gas::calculate_initial_tx_gas(
1053 env.evm_env.cfg_env.spec,
1054 &env.tx.data,
1055 env.tx.kind.is_create(),
1056 env.tx.access_list.len().try_into()?,
1057 0,
1058 0,
1059 );
1060
1061 let result = match &out {
1062 Some(Output::Call(data)) => data.clone(),
1063 _ => Bytes::new(),
1064 };
1065
1066 let InspectorData {
1067 mut logs,
1068 labels,
1069 traces,
1070 line_coverage,
1071 edge_coverage,
1072 cheatcodes,
1073 chisel_state,
1074 reverter,
1075 } = inspector.collect();
1076
1077 if logs.is_empty() {
1078 logs = exec_logs;
1079 }
1080
1081 let transactions = cheatcodes
1082 .as_ref()
1083 .map(|c| c.broadcastable_transactions.clone())
1084 .filter(|txs| !txs.is_empty());
1085
1086 Ok(RawCallResult {
1087 exit_reason: Some(exit_reason),
1088 reverted: !matches!(exit_reason, return_ok!()),
1089 has_state_snapshot_failure,
1090 result,
1091 gas_used,
1092 gas_refunded,
1093 stipend: gas.initial_gas,
1094 logs,
1095 labels,
1096 traces,
1097 line_coverage,
1098 edge_coverage,
1099 transactions,
1100 state_changeset,
1101 env,
1102 cheatcodes,
1103 out,
1104 chisel_state,
1105 reverter,
1106 })
1107}
1108
1109pub struct FuzzTestTimer {
1111 inner: Option<(Instant, Duration)>,
1113}
1114
1115impl FuzzTestTimer {
1116 pub fn new(timeout: Option<u32>) -> Self {
1117 Self { inner: timeout.map(|timeout| (Instant::now(), Duration::from_secs(timeout.into()))) }
1118 }
1119
1120 pub fn is_enabled(&self) -> bool {
1122 self.inner.is_some()
1123 }
1124
1125 pub fn is_timed_out(&self) -> bool {
1127 self.inner.is_some_and(|(start, duration)| start.elapsed() > duration)
1128 }
1129}
1130
1131#[derive(Clone, Debug)]
1134pub struct EarlyExit {
1135 inner: Arc<AtomicBool>,
1137 fail_fast: bool,
1139}
1140
1141impl EarlyExit {
1142 pub fn new(fail_fast: bool) -> Self {
1143 Self { inner: Arc::new(AtomicBool::new(false)), fail_fast }
1144 }
1145
1146 pub fn record_failure(&self) {
1148 if self.fail_fast {
1149 self.inner.store(true, Ordering::Relaxed);
1150 }
1151 }
1152
1153 pub fn record_ctrl_c(&self) {
1155 self.inner.store(true, Ordering::Relaxed);
1156 }
1157
1158 pub fn should_stop(&self) -> bool {
1160 self.inner.load(Ordering::Relaxed)
1161 }
1162}