Skip to main content

foundry_evm/executors/
trace.rs

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, TempoHardfork};
14use foundry_evm_networks::NetworkConfigs;
15use foundry_evm_traces::TraceRequirements;
16use revm::state::Bytecode;
17use std::ops::{Deref, DerefMut};
18
19/// A default executor with tracing enabled
20pub struct TracingExecutor<FEN: FoundryEvmNetwork> {
21    executor: Executor<FEN>,
22}
23
24impl<FEN: FoundryEvmNetwork> TracingExecutor<FEN> {
25    pub fn new(
26        env: (EvmEnvFor<FEN>, TxEnvFor<FEN>),
27        fork: CreateFork,
28        version: Option<EvmVersion>,
29        trace_requirements: TraceRequirements,
30        networks: NetworkConfigs,
31        create2_deployer: Address,
32        state_overrides: Option<StateOverride>,
33    ) -> eyre::Result<Self> {
34        let db = Backend::spawn(Some(fork))?;
35        // configures a bare version of the evm executor: no cheatcode and log_collector inspector
36        // is enabled, tracing will be enabled only for the targeted transaction
37        let mut executor = ExecutorBuilder::default()
38            .inspectors(|stack| {
39                stack.trace_requirements(trace_requirements).create2_deployer(create2_deployer)
40            })
41            .spec_id_opt(version.map(evm_spec_id::<SpecFor<FEN>>))
42            .build(env.0, env.1, db, networks);
43
44        if let Some(state_overrides) = state_overrides {
45            apply_state_overrides(&mut executor, state_overrides)?;
46        }
47
48        Ok(Self { executor })
49    }
50
51    /// Returns the spec id of the executor
52    pub const fn spec_id(&self) -> SpecFor<FEN> {
53        self.executor.spec_id()
54    }
55
56    /// Resolves and applies the execution spec for the effective block environment.
57    pub fn resolve_spec(
58        config: &Config,
59        networks: NetworkConfigs,
60        evm_env: &mut EvmEnvFor<FEN>,
61        evm_version: Option<EvmVersion>,
62    ) -> Option<FoundryHardfork> {
63        Self::resolve_spec_for_chain(
64            config,
65            networks,
66            evm_env.cfg_env.chain_id,
67            None,
68            evm_env,
69            evm_version,
70        )
71    }
72
73    /// Resolves and applies the execution spec using the source chain's hardfork schedule.
74    pub fn resolve_spec_for_chain(
75        config: &Config,
76        networks: NetworkConfigs,
77        source_chain_id: ChainId,
78        endpoint_hardfork: Option<FoundryHardfork>,
79        evm_env: &mut EvmEnvFor<FEN>,
80        evm_version: Option<EvmVersion>,
81    ) -> Option<FoundryHardfork> {
82        let explicit_hardfork =
83            evm_version.and_then(|version| network_hardfork_from_evm_version(networks, version));
84        resolve_execution_spec(
85            config,
86            networks,
87            evm_env,
88            ExecutionSpecContext::historical(source_chain_id, endpoint_hardfork),
89            evm_version.map(evm_spec_id::<SpecFor<FEN>>),
90            explicit_hardfork,
91        )
92    }
93
94    /// Extends trace labels with the precompiles active at the resolved execution hardfork.
95    pub fn extend_precompile_labels(
96        config: &mut Config,
97        networks: NetworkConfigs,
98        resolved_hardfork: Option<FoundryHardfork>,
99    ) {
100        config.labels.extend(networks.precompiles_label(resolved_hardfork));
101    }
102
103    /// uses the fork block number from the config
104    pub async fn get_fork_material(
105        config: &mut Config,
106        mut evm_opts: EvmOpts,
107    ) -> eyre::Result<(
108        EvmEnvFor<FEN>,
109        TxEnvFor<FEN>,
110        CreateFork,
111        Chain,
112        NetworkConfigs,
113        Option<FoundryHardfork>,
114    )> {
115        evm_opts.fork_url = Some(config.get_rpc_url_or_localhost_http()?.into_owned());
116        evm_opts.fork_block_number = config.fork_block_number;
117        evm_opts.infer_network_from_fork().await?;
118        let networks = evm_opts.networks;
119        let (evm_env, tx_env, resolved) =
120            evm_opts.env_resolved::<SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>().await?;
121        let resolved = resolved.context("fork context is missing for tracing executor")?;
122        let fork = evm_opts
123            .get_fork_resolved(config, evm_env.cfg_env.chain_id, Some(&resolved))
124            .context("fork URL is missing for tracing executor")?;
125        let fork_context = resolved.context();
126
127        let chain = fork_context.source_chain_id.into();
128        Ok((evm_env, tx_env, fork, chain, networks, fork_context.hardfork))
129    }
130}
131
132fn apply_state_overrides<FEN: FoundryEvmNetwork>(
133    executor: &mut Executor<FEN>,
134    state_overrides: StateOverride,
135) -> eyre::Result<()> {
136    for (address, overrides) in state_overrides {
137        if let Some(balance) = overrides.balance {
138            executor.set_balance(address, balance)?;
139        }
140        if let Some(nonce) = overrides.nonce {
141            executor.set_account_nonce(address, nonce)?;
142        }
143        if let Some(code) = overrides.code {
144            let bytecode =
145                Bytecode::new_raw_checked(code).wrap_err("invalid bytecode in state override")?;
146            executor.set_code(address, bytecode)?;
147        }
148        if let Some(state) = overrides.state {
149            let state: HashMap<U256, U256> =
150                state.into_iter().map(|(slot, value)| (slot.into(), value.into())).collect();
151            executor.set_storage(address, state)?;
152        }
153        if let Some(state_diff) = overrides.state_diff {
154            for (slot, value) in state_diff {
155                executor.set_storage_slot(address, slot.into(), value.into())?;
156            }
157        }
158    }
159    Ok(())
160}
161
162fn network_hardfork_from_evm_version(
163    networks: NetworkConfigs,
164    evm_version: EvmVersion,
165) -> Option<FoundryHardfork> {
166    if networks.is_tempo() {
167        return Some(FoundryHardfork::Tempo(evm_spec_id::<TempoHardfork>(evm_version)));
168    }
169    #[cfg(feature = "monad")]
170    if networks.is_monad() {
171        return Some(FoundryHardfork::Monad(evm_spec_id::<foundry_evm_hardforks::MonadHardfork>(
172            evm_version,
173        )));
174    }
175    None
176}
177
178impl<FEN: FoundryEvmNetwork> Deref for TracingExecutor<FEN> {
179    type Target = Executor<FEN>;
180
181    fn deref(&self) -> &Self::Target {
182        &self.executor
183    }
184}
185
186impl<FEN: FoundryEvmNetwork> DerefMut for TracingExecutor<FEN> {
187    fn deref_mut(&mut self) -> &mut Self::Target {
188        &mut self.executor
189    }
190}
191
192#[cfg(test)]
193mod tests {
194    use super::*;
195    use alloy_rpc_types::state::AccountOverride;
196    use foundry_evm_core::{FoundryTransaction, evm::EthEvmNetwork};
197    use revm::context::Transaction;
198
199    #[test]
200    fn state_override_nonce_does_not_modify_transaction_nonce() {
201        let sender = Address::repeat_byte(0x11);
202        let mut tx_env = TxEnvFor::<EthEvmNetwork>::default();
203        tx_env.set_caller(sender);
204        tx_env.set_nonce(7);
205        let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
206        let mut evm_env = EvmEnvFor::<EthEvmNetwork>::default();
207        evm_env.cfg_env.disable_nonce_check = true;
208        let mut executor =
209            ExecutorBuilder::default().build(evm_env, tx_env, backend, NetworkConfigs::default());
210        executor.set_gas_limit(1_000_000);
211        executor.set_account_nonce(sender, 7).unwrap();
212
213        let overridden = Address::repeat_byte(0x42);
214        let mut state_overrides = StateOverride::default();
215        state_overrides.insert(sender, AccountOverride { nonce: Some(100), ..Default::default() });
216        state_overrides
217            .insert(overridden, AccountOverride { nonce: Some(200), ..Default::default() });
218
219        apply_state_overrides(&mut executor, state_overrides).unwrap();
220
221        assert_eq!(executor.get_nonce(sender).unwrap(), 100);
222        assert_eq!(executor.get_nonce(overridden).unwrap(), 200);
223        assert_eq!(executor.tx_env().caller(), sender);
224        assert_eq!(executor.tx_env().nonce(), 7);
225
226        let result =
227            executor.transact_raw(sender, overridden, Default::default(), U256::ZERO).unwrap();
228        assert_eq!(result.tx_env.nonce(), 7);
229    }
230}