1use crate::executors::{Executor, ExecutorBuilder};
2use alloy_primitives::{Address, ChainId, U256, map::HashMap};
3use alloy_rpc_types::state::StateOverride;
4use eyre::{Context, ContextCompat};
5use foundry_compilers::artifacts::EvmVersion;
6use foundry_config::{Chain, Config, evm_spec_id};
7use foundry_evm_core::{
8 backend::Backend,
9 evm::{EvmEnvFor, FoundryEvmNetwork, SpecFor, TxEnvFor},
10 opts::{EvmOpts, ExecutionSpecContext, resolve_execution_spec},
11};
12use foundry_evm_hardforks::FoundryHardfork;
13use foundry_evm_networks::{
14 NetworkConfigs,
15 celo::transfer::{CELO_TRANSFER_ADDRESS, CELO_TRANSFER_LABEL},
16 resolved_precompile_labels,
17};
18use foundry_evm_traces::{TraceContext, TraceRequirements};
19use revm::state::Bytecode;
20use std::ops::{Deref, DerefMut};
21
22pub struct TracingExecutor<FEN: FoundryEvmNetwork> {
24 executor: Executor<FEN>,
25}
26
27pub struct TracingFork<FEN: FoundryEvmNetwork> {
29 pub evm_env: EvmEnvFor<FEN>,
30 pub tx_env: TxEnvFor<FEN>,
31 fork: Backend<FEN>,
32 context: TraceContext,
33}
34
35impl<FEN: FoundryEvmNetwork> TracingFork<FEN> {
36 pub const fn context(&self) -> TraceContext {
37 self.context
38 }
39
40 pub fn resolve_spec(&mut self, config: &Config, evm_version: Option<EvmVersion>) {
42 let hardfork = TracingExecutor::<FEN>::resolve_spec_for_chain(
43 config,
44 self.context.chain().id(),
45 self.context.hardfork(),
46 &mut self.evm_env,
47 evm_version,
48 );
49 self.context = self.context.with_hardfork(hardfork);
50 }
51
52 pub fn extend_precompile_labels(&self, config: &mut Config) {
54 TracingExecutor::<FEN>::extend_precompile_labels(
55 config,
56 self.context.networks(),
57 self.context.hardfork(),
58 );
59 }
60
61 pub fn into_executor(
63 self,
64 builder: ExecutorBuilder<FEN>,
65 trace_requirements: TraceRequirements,
66 create2_deployer: Address,
67 state_overrides: Option<StateOverride>,
68 ) -> eyre::Result<TracingExecutor<FEN>> {
69 TracingExecutor::new(
70 builder,
71 (self.evm_env, self.tx_env),
72 self.fork,
73 None,
74 trace_requirements,
75 self.context.networks(),
76 create2_deployer,
77 state_overrides,
78 )
79 }
80
81 fn into_parts(
82 self,
83 ) -> (EvmEnvFor<FEN>, TxEnvFor<FEN>, Backend<FEN>, Chain, NetworkConfigs, Option<FoundryHardfork>)
84 {
85 (
86 self.evm_env,
87 self.tx_env,
88 self.fork,
89 self.context.chain(),
90 self.context.networks(),
91 self.context.hardfork(),
92 )
93 }
94}
95
96impl<FEN: FoundryEvmNetwork> TracingExecutor<FEN> {
97 #[allow(clippy::too_many_arguments)]
99 pub fn new(
100 builder: ExecutorBuilder<FEN>,
101 env: (EvmEnvFor<FEN>, TxEnvFor<FEN>),
102 fork: Backend<FEN>,
103 version: Option<EvmVersion>,
104 trace_requirements: TraceRequirements,
105 networks: NetworkConfigs,
106 create2_deployer: Address,
107 state_overrides: Option<StateOverride>,
108 ) -> eyre::Result<Self> {
109 let db = fork;
110 let mut executor = builder
113 .inspectors(|stack| {
114 stack.trace_requirements(trace_requirements).create2_deployer(create2_deployer)
115 })
116 .spec_id_opt(version.map(evm_spec_id::<SpecFor<FEN>>))
117 .build(env.0, env.1, db, networks);
118
119 if let Some(state_overrides) = state_overrides {
120 apply_state_overrides(&mut executor, state_overrides)?;
121 }
122
123 Ok(Self { executor })
124 }
125
126 pub const fn spec_id(&self) -> SpecFor<FEN> {
128 self.executor.spec_id()
129 }
130
131 pub fn resolve_spec_for_chain(
133 config: &Config,
134 source_chain_id: ChainId,
135 endpoint_hardfork: Option<FoundryHardfork>,
136 evm_env: &mut EvmEnvFor<FEN>,
137 evm_version: Option<EvmVersion>,
138 ) -> Option<FoundryHardfork> {
139 resolve_execution_spec(
140 config.evm_version,
141 config.hardfork,
142 evm_env,
143 ExecutionSpecContext::historical(source_chain_id, endpoint_hardfork),
144 evm_version.map(evm_spec_id::<SpecFor<FEN>>),
145 )
146 }
147
148 pub fn extend_precompile_labels(
150 config: &mut Config,
151 networks: NetworkConfigs,
152 resolved_hardfork: Option<FoundryHardfork>,
153 ) {
154 config.labels.extend(resolved_precompile_labels(resolved_hardfork));
155 if networks.is_celo() {
157 config.labels.insert(CELO_TRANSFER_ADDRESS, CELO_TRANSFER_LABEL.to_string());
158 }
159 }
160
161 pub async fn get_fork(
163 config: &mut Config,
164 mut evm_opts: EvmOpts,
165 ) -> eyre::Result<TracingFork<FEN>> {
166 evm_opts.fork_url = Some(config.get_rpc_url_or_localhost_http()?.into_owned());
167 evm_opts.fork_block_number = config.fork_block_number;
168 evm_opts.fork_state_by_number = false;
170 evm_opts.infer_network_from_fork().await?;
171 let networks = evm_opts.networks;
172 let fork = Backend::<FEN>::spawn(evm_opts.get_fork(
173 config,
174 evm_opts.env.chain_id.unwrap_or_default(),
175 None,
176 ))?;
177 let (evm_env, tx_env) = fork.env(&evm_opts).await?;
178 let fork_context =
179 fork.fork()?.context("fork context is missing for tracing executor")?.context();
180
181 let chain = fork_context.source_chain_id.into();
182 Ok(TracingFork {
183 evm_env,
184 tx_env,
185 fork,
186 context: TraceContext::new(chain, networks, fork_context.hardfork),
187 })
188 }
189
190 pub async fn get_fork_material(
192 config: &mut Config,
193 evm_opts: EvmOpts,
194 ) -> eyre::Result<(
195 EvmEnvFor<FEN>,
196 TxEnvFor<FEN>,
197 Backend<FEN>,
198 Chain,
199 NetworkConfigs,
200 Option<FoundryHardfork>,
201 )> {
202 Ok(Self::get_fork(config, evm_opts).await?.into_parts())
203 }
204}
205
206fn apply_state_overrides<FEN: FoundryEvmNetwork>(
207 executor: &mut Executor<FEN>,
208 state_overrides: StateOverride,
209) -> eyre::Result<()> {
210 for (address, overrides) in state_overrides {
211 if let Some(balance) = overrides.balance {
212 executor.set_balance(address, balance)?;
213 }
214 if let Some(nonce) = overrides.nonce {
215 executor.set_account_nonce(address, nonce)?;
216 }
217 if let Some(code) = overrides.code {
218 let bytecode =
219 Bytecode::new_raw_checked(code).wrap_err("invalid bytecode in state override")?;
220 executor.set_code(address, bytecode)?;
221 }
222 if let Some(state) = overrides.state {
223 let state: HashMap<U256, U256> =
224 state.into_iter().map(|(slot, value)| (slot.into(), value.into())).collect();
225 executor.set_storage(address, state)?;
226 }
227 if let Some(state_diff) = overrides.state_diff {
228 for (slot, value) in state_diff {
229 executor.set_storage_slot(address, slot.into(), value.into())?;
230 }
231 }
232 }
233 Ok(())
234}
235
236impl<FEN: FoundryEvmNetwork> Deref for TracingExecutor<FEN> {
237 type Target = Executor<FEN>;
238
239 fn deref(&self) -> &Self::Target {
240 &self.executor
241 }
242}
243
244impl<FEN: FoundryEvmNetwork> DerefMut for TracingExecutor<FEN> {
245 fn deref_mut(&mut self) -> &mut Self::Target {
246 &mut self.executor
247 }
248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253 use alloy_evm::EvmEnv;
254 use alloy_rpc_types::state::AccountOverride;
255 use foundry_evm_core::{FoundryTransaction, evm::EthEvmNetwork};
256 use revm::context::{Transaction, TxEnv};
257
258 #[cfg(feature = "base")]
259 use foundry_evm_core::evm::BaseEvmNetwork;
260 #[cfg(feature = "base")]
261 use foundry_evm_hardforks::{BaseSpecId, BaseUpgrade};
262
263 fn assert_trace_spec_authority<FEN>(
264 networks: NetworkConfigs,
265 configured: FoundryHardfork,
266 evm_version: EvmVersion,
267 expected_spec: SpecFor<FEN>,
268 expected_hardfork: Option<FoundryHardfork>,
269 ) where
270 FEN: FoundryEvmNetwork,
271 SpecFor<FEN>: std::fmt::Debug + PartialEq,
272 {
273 let mut config = Config { networks, hardfork: Some(configured), ..Default::default() };
274 let mut env = EvmEnvFor::<FEN>::default();
275 env.cfg_env.chain_id = 999_999;
276 let hardfork = TracingExecutor::<FEN>::resolve_spec_for_chain(
277 &config,
278 1,
279 Some(configured),
280 &mut env,
281 Some(evm_version),
282 );
283 assert_eq!(env.cfg_env.spec, expected_spec);
284 assert_eq!(hardfork, expected_hardfork);
285 assert_eq!(env.cfg_env.chain_id, 999_999);
286
287 TracingExecutor::<FEN>::extend_precompile_labels(&mut config, networks, hardfork);
288 assert_eq!(config.labels, resolved_precompile_labels(expected_hardfork));
289 let context = TraceContext::new(Chain::from_id(1), networks, hardfork);
290 let decoder = foundry_evm_traces::CallTraceDecoderBuilder::new()
291 .with_networks(context.networks())
292 .with_hardfork(context.hardfork())
293 .build();
294 assert_eq!(decoder.hardfork(), expected_hardfork);
295 }
296
297 #[test]
298 fn trace_spec_ethereum_override_preserves_absent_metadata() {
299 assert_trace_spec_authority::<EthEvmNetwork>(
300 NetworkConfigs::default(),
301 "ethereum:shanghai".parse().unwrap(),
302 EvmVersion::Cancun,
303 revm::primitives::hardfork::SpecId::CANCUN,
304 None,
305 );
306 }
307
308 #[test]
309 fn trace_spec_tempo_override_reports_executed_hardfork() {
310 let spec = evm_spec_id::<foundry_evm_hardforks::TempoHardfork>(EvmVersion::Cancun);
311 assert_trace_spec_authority::<foundry_evm_core::evm::TempoEvmNetwork>(
312 NetworkConfigs::with_tempo(),
313 "tempo:T3".parse().unwrap(),
314 EvmVersion::Cancun,
315 spec,
316 Some(spec.into()),
317 );
318 }
319
320 #[cfg(feature = "base")]
321 #[test]
322 fn trace_spec_base_override_reports_executed_hardfork() {
323 assert_trace_spec_authority::<BaseEvmNetwork>(
324 NetworkConfigs::with_base(),
325 FoundryHardfork::Base(BaseUpgrade::Cobalt),
326 EvmVersion::Cancun,
327 BaseSpecId::new(BaseUpgrade::Ecotone),
328 Some(FoundryHardfork::Base(BaseUpgrade::Ecotone)),
329 );
330 }
331
332 #[test]
333 fn state_override_nonce_does_not_modify_transaction_nonce() {
334 let sender = Address::repeat_byte(0x11);
335 let mut tx_env = TxEnv::default();
336 tx_env.set_caller(sender);
337 tx_env.set_nonce(7);
338 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
339 let mut evm_env = EvmEnv::default();
340 evm_env.cfg_env.disable_nonce_check = true;
341 let mut executor =
342 ExecutorBuilder::default().build(evm_env, tx_env, backend, NetworkConfigs::default());
343 executor.set_gas_limit(1_000_000);
344 executor.set_account_nonce(sender, 7).unwrap();
345
346 let overridden = Address::repeat_byte(0x42);
347 let mut state_overrides = StateOverride::default();
348 state_overrides.insert(sender, AccountOverride { nonce: Some(100), ..Default::default() });
349 state_overrides
350 .insert(overridden, AccountOverride { nonce: Some(200), ..Default::default() });
351
352 apply_state_overrides(&mut executor, state_overrides).unwrap();
353
354 assert_eq!(executor.get_nonce(sender).unwrap(), 100);
355 assert_eq!(executor.get_nonce(overridden).unwrap(), 200);
356 assert_eq!(executor.tx_env().caller(), sender);
357 assert_eq!(executor.tx_env().nonce(), 7);
358
359 let result =
360 executor.transact_raw(sender, overridden, Default::default(), U256::ZERO).unwrap();
361 assert_eq!(result.tx_env.nonce(), 7);
362 }
363}