Skip to main content

foundry_evm/executors/
builder.rs

1use crate::{
2    executors::Executor,
3    inspectors::{InspectorStackBuilder, TempoLabels},
4};
5use alloy_primitives::Address;
6use foundry_evm_core::{
7    backend::Backend,
8    evm::{
9        BlockEnvFor, EthEvmNetwork, EvmEnvFor, FoundryEvmNetwork, SpecFor, TempoEvmNetwork,
10        TxEnvFor,
11    },
12};
13use foundry_evm_networks::NetworkConfigs;
14use revm::context::{Block, Transaction};
15
16#[cfg(feature = "base")]
17use foundry_evm_core::evm::BaseEvmNetwork;
18
19#[cfg(feature = "monad")]
20use foundry_evm_core::{constants::MONAD_CHEATCODE_ADDRESS, evm::MonadEvmNetwork};
21
22#[cfg(feature = "optimism")]
23use foundry_evm_core::evm::OpEvmNetwork;
24
25/// The builder that allows to configure an evm [`Executor`] which a stack of optional
26/// [`revm::Inspector`]s, such as [`Cheatcodes`].
27///
28/// By default, the [`Executor`] will be configured with an empty [`InspectorStack`] and no
29/// network-specific tooling. Command dispatch should use the concrete FEN's inherent `new`
30/// constructor so any required tooling is selected there.
31///
32/// [`Cheatcodes`]: super::Cheatcodes
33/// [`InspectorStack`]: super::InspectorStack
34#[derive(Debug, Clone)]
35#[must_use = "builders do nothing unless you call `build` on them"]
36pub struct ExecutorBuilder<FEN: FoundryEvmNetwork> {
37    /// The configuration used to build an `InspectorStack`.
38    stack: InspectorStackBuilder<BlockEnvFor<FEN>>,
39    /// The gas limit.
40    gas_limit: Option<u64>,
41    /// The spec override. When `None`, the spec from `EvmEnv::cfg_env` is preserved.
42    spec: Option<SpecFor<FEN>>,
43    legacy_assertions: bool,
44}
45
46impl<FEN: FoundryEvmNetwork> Default for ExecutorBuilder<FEN> {
47    #[inline]
48    fn default() -> Self {
49        Self {
50            stack: InspectorStackBuilder::new().extra_cheatcode_addresses(&[]),
51            gas_limit: None,
52            spec: None,
53            legacy_assertions: false,
54        }
55    }
56}
57
58impl<FEN: FoundryEvmNetwork> ExecutorBuilder<FEN> {
59    /// Returns additional cheatcode addresses selected for this executor.
60    #[inline]
61    pub const fn extra_cheatcode_addresses(&self) -> &'static [Address] {
62        self.stack.extra_cheatcode_addresses
63    }
64
65    /// Modify the inspector stack.
66    #[inline]
67    pub fn inspectors(
68        mut self,
69        f: impl FnOnce(
70            InspectorStackBuilder<BlockEnvFor<FEN>>,
71        ) -> InspectorStackBuilder<BlockEnvFor<FEN>>,
72    ) -> Self {
73        self.stack = f(self.stack);
74        self
75    }
76
77    /// Sets the EVM spec to use.
78    #[inline]
79    pub const fn spec_id(mut self, spec: SpecFor<FEN>) -> Self {
80        self.spec = Some(spec);
81        self
82    }
83
84    /// Optionally sets the EVM spec. When `None`, the spec from `EvmEnv::cfg_env` is preserved.
85    #[inline]
86    pub const fn spec_id_opt(self, spec: Option<SpecFor<FEN>>) -> Self {
87        if let Some(spec) = spec { self.spec_id(spec) } else { self }
88    }
89
90    /// Sets the executor gas limit.
91    #[inline]
92    pub const fn gas_limit(mut self, gas_limit: u64) -> Self {
93        self.gas_limit = Some(gas_limit);
94        self
95    }
96
97    /// Sets the `legacy_assertions` flag.
98    #[inline]
99    pub const fn legacy_assertions(mut self, legacy_assertions: bool) -> Self {
100        self.legacy_assertions = legacy_assertions;
101        self
102    }
103
104    /// Builds the executor as configured.
105    #[inline]
106    pub fn build(
107        self,
108        mut evm_env: EvmEnvFor<FEN>,
109        tx_env: TxEnvFor<FEN>,
110        db: Backend<FEN>,
111        // TODO(monad-fen-lifecycle): Remove this argument after the backend's Monad fork-position
112        // migration and the inspector's separate Celo configuration cleanup are complete.
113        networks: NetworkConfigs,
114    ) -> Executor<FEN> {
115        let Self { mut stack, gas_limit, spec, legacy_assertions, .. } = self;
116        stack.networks = networks;
117        if stack.block.is_none() {
118            stack.block = Some(evm_env.block_env.clone());
119        }
120        if stack.gas_price.is_none() {
121            stack.gas_price = Some(tx_env.gas_price());
122        }
123        let gas_limit = gas_limit.unwrap_or(evm_env.block_env.gas_limit());
124        if let Some(spec) = spec {
125            evm_env.cfg_env.set_spec_and_mainnet_gas_params(spec);
126        }
127        Executor::new(db, evm_env, tx_env, stack.build(), networks, gas_limit, legacy_assertions)
128    }
129}
130
131impl ExecutorBuilder<EthEvmNetwork> {
132    /// Creates the default Ethereum executor builder.
133    #[inline]
134    pub fn new() -> Self {
135        Self::default()
136    }
137}
138
139#[cfg(feature = "base")]
140impl ExecutorBuilder<BaseEvmNetwork> {
141    /// Creates the default Base executor builder.
142    #[inline]
143    pub fn new() -> Self {
144        Self::default()
145    }
146}
147
148#[cfg(feature = "optimism")]
149impl ExecutorBuilder<OpEvmNetwork> {
150    /// Creates the default OP executor builder.
151    #[inline]
152    pub fn new() -> Self {
153        Self::default()
154    }
155}
156
157impl ExecutorBuilder<TempoEvmNetwork> {
158    /// Creates a Tempo executor builder with its native label inspector.
159    #[inline]
160    pub fn new() -> Self {
161        Self::default().inspectors(|stack| stack.tempo_labels(TempoLabels::default()))
162    }
163}
164
165#[cfg(feature = "monad")]
166impl ExecutorBuilder<MonadEvmNetwork> {
167    /// Creates a Monad executor builder with MonadVM cheatcode support.
168    #[inline]
169    pub fn new() -> Self {
170        Self::default()
171            .inspectors(|stack| stack.extra_cheatcode_addresses(&[MONAD_CHEATCODE_ADDRESS]))
172    }
173}