foundry_evm/executors/invariant/
replay.rs1use super::{call_after_invariant_function, call_invariant_function, execute_tx};
2use crate::executors::{
3 EarlyExit, Executor,
4 invariant::shrink::{shrink_sequence, shrink_sequence_value},
5};
6use alloy_dyn_abi::JsonAbiExt;
7use alloy_primitives::{I256, Log, map::HashMap};
8use eyre::Result;
9use foundry_common::{ContractsByAddress, ContractsByArtifact};
10use foundry_config::InvariantConfig;
11use foundry_evm_core::evm::FoundryEvmNetwork;
12use foundry_evm_coverage::HitMaps;
13use foundry_evm_fuzz::{BaseCounterExample, BasicTxDetails, invariant::InvariantContract};
14use foundry_evm_traces::{TraceKind, TraceMode, Traces, load_contracts};
15use indicatif::ProgressBar;
16use parking_lot::RwLock;
17use std::sync::Arc;
18
19#[expect(clippy::too_many_arguments)]
22pub fn replay_run<FEN: FoundryEvmNetwork>(
23 invariant_contract: &InvariantContract<'_>,
24 mut executor: Executor<FEN>,
25 known_contracts: &ContractsByArtifact,
26 mut ided_contracts: ContractsByAddress,
27 logs: &mut Vec<Log>,
28 traces: &mut Traces,
29 line_coverage: &mut Option<HitMaps>,
30 deprecated_cheatcodes: &mut HashMap<&'static str, Option<&'static str>>,
31 inputs: &[BasicTxDetails],
32 show_solidity: bool,
33) -> Result<Vec<BaseCounterExample>> {
34 if executor.inspector().tracer.is_none() {
36 executor.set_tracing(TraceMode::Call);
37 }
38
39 let mut counterexample_sequence = vec![];
40
41 for tx in inputs {
43 let mut call_result = execute_tx(&mut executor, tx)?;
44 logs.extend(call_result.logs.clone());
45 traces.push((TraceKind::Execution, call_result.traces.clone().unwrap()));
46 HitMaps::merge_opt(line_coverage, call_result.line_coverage.clone());
47
48 executor.commit(&mut call_result);
50
51 ided_contracts
53 .extend(load_contracts(call_result.traces.iter().map(|a| &a.arena), known_contracts));
54
55 counterexample_sequence.push(BaseCounterExample::from_invariant_call(
57 tx,
58 &ided_contracts,
59 call_result.traces,
60 show_solidity,
61 ));
62 }
63
64 let (invariant_result, invariant_success) = call_invariant_function(
70 &executor,
71 invariant_contract.address,
72 invariant_contract.invariant_function.abi_encode_input(&[])?.into(),
73 )?;
74 traces.push((TraceKind::Execution, invariant_result.traces.clone().unwrap()));
75 logs.extend(invariant_result.logs);
76 deprecated_cheatcodes.extend(
77 invariant_result
78 .cheatcodes
79 .as_ref()
80 .map_or_else(Default::default, |cheats| cheats.deprecated.clone()),
81 );
82
83 if invariant_contract.call_after_invariant && invariant_success {
85 let (after_invariant_result, _) =
86 call_after_invariant_function(&executor, invariant_contract.address)?;
87 traces.push((TraceKind::Execution, after_invariant_result.traces.clone().unwrap()));
88 logs.extend(after_invariant_result.logs);
89 }
90
91 Ok(counterexample_sequence)
92}
93
94#[expect(clippy::too_many_arguments)]
99pub fn replay_error<FEN: FoundryEvmNetwork>(
100 config: InvariantConfig,
101 mut executor: Executor<FEN>,
102 calls: &[BasicTxDetails],
103 inner_sequence: Option<Vec<Option<BasicTxDetails>>>,
104 target_value: Option<I256>,
105 invariant_contract: &InvariantContract<'_>,
106 known_contracts: &ContractsByArtifact,
107 ided_contracts: ContractsByAddress,
108 logs: &mut Vec<Log>,
109 traces: &mut Traces,
110 line_coverage: &mut Option<HitMaps>,
111 deprecated_cheatcodes: &mut HashMap<&'static str, Option<&'static str>>,
112 progress: Option<&ProgressBar>,
113 early_exit: &EarlyExit,
114) -> Result<Vec<BaseCounterExample>> {
115 let calls = if let Some(target) = target_value {
116 shrink_sequence_value(
117 &config,
118 invariant_contract,
119 calls,
120 &executor,
121 target,
122 progress,
123 early_exit,
124 )?
125 } else {
126 shrink_sequence(&config, invariant_contract, calls, &executor, progress, early_exit)?
127 };
128
129 if let Some(sequence) = inner_sequence {
130 set_up_inner_replay(&mut executor, &sequence);
131 }
132
133 replay_run(
134 invariant_contract,
135 executor,
136 known_contracts,
137 ided_contracts,
138 logs,
139 traces,
140 line_coverage,
141 deprecated_cheatcodes,
142 &calls,
143 config.show_solidity,
144 )
145}
146
147fn set_up_inner_replay<FEN: FoundryEvmNetwork>(
149 executor: &mut Executor<FEN>,
150 inner_sequence: &[Option<BasicTxDetails>],
151) {
152 if let Some(fuzzer) = &mut executor.inspector_mut().fuzzer
153 && let Some(call_generator) = &mut fuzzer.call_generator
154 {
155 call_generator.last_sequence = Arc::new(RwLock::new(inner_sequence.to_owned()));
156 call_generator.set_replay(true);
157 }
158}