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