Skip to main content

foundry_evm_core/env/
context.rs

1//! Mutable execution context access.
2
3use super::{EvmEnv, FoundryBlock, FoundryCfg, FoundryChain, FoundryJournal, FoundryTransaction};
4use crate::backend::JournaledState;
5use revm::{
6    Context, Database,
7    context::{Cfg, CfgEnv},
8    context_interface::ContextTr,
9    primitives::hardfork::SpecId,
10};
11use std::fmt::Debug;
12
13/// Extension trait providing mutable field access to block, tx, and cfg environments.
14///
15/// [`ContextTr`] only exposes immutable references for block, tx, and cfg.
16/// Cheatcodes like `vm.warp()`, `vm.roll()`, `vm.chainId()` need to mutate these fields.
17pub trait FoundryContextExt:
18    ContextTr<
19        Block: FoundryBlock + Clone,
20        Tx: FoundryTransaction + Clone,
21        Cfg: FoundryCfg<Spec = Self::Spec>,
22        Journal: FoundryJournal,
23        Chain: FoundryChain<Self::Tx>,
24    >
25{
26    /// Specification id type
27    ///
28    /// Bubbled-up from `ContextTr::Cfg` for convenience and simplified bounds.
29    type Spec: Into<SpecId> + Copy + Debug;
30
31    /// Mutable reference to the block environment.
32    fn block_mut(&mut self) -> &mut Self::Block;
33
34    /// Mutable reference to the transaction environment.
35    fn tx_mut(&mut self) -> &mut Self::Tx;
36
37    /// Mutable reference to the configuration environment.
38    fn cfg_mut(&mut self) -> &mut Self::Cfg;
39
40    /// Reference to the underlying [`CfgEnv`].
41    fn cfg_env(&self) -> &CfgEnv<Self::Spec> {
42        self.cfg().cfg_env()
43    }
44
45    /// Mutable reference to the underlying [`CfgEnv`].
46    fn cfg_env_mut(&mut self) -> &mut CfgEnv<Self::Spec> {
47        self.cfg_mut().cfg_env_mut()
48    }
49
50    /// Mutable reference to the db and the journal inner.
51    fn db_journal_inner_mut(&mut self) -> (&mut Self::Db, &mut JournaledState) {
52        self.journal_mut().db_journal_inner_mut()
53    }
54
55    /// Reference to the journal inner.
56    fn journal_inner(&self) -> &JournaledState {
57        self.journal().journal_inner()
58    }
59
60    /// Sets the spec and refreshes gas params for the concrete EVM family.
61    fn set_spec_and_gas_params(&mut self, spec: Self::Spec) {
62        self.cfg_mut().set_spec_and_gas_params(spec);
63    }
64
65    /// Sets block environment.
66    fn set_block(&mut self, block: Self::Block) {
67        *self.block_mut() = block;
68    }
69
70    /// Sets transaction environment.
71    fn set_tx(&mut self, tx: Self::Tx) {
72        *self.tx_mut() = tx;
73    }
74
75    /// Sets configuration environment.
76    fn set_cfg(&mut self, cfg: Self::Cfg) {
77        *self.cfg_mut() = cfg;
78    }
79
80    /// Sets journal inner.
81    fn set_journal_inner(&mut self, journal_inner: JournaledState) {
82        *self.db_journal_inner_mut().1 = journal_inner;
83    }
84
85    /// Sets EVM environment.
86    fn set_evm(&mut self, evm_env: EvmEnv<Self::Spec, Self::Block>) {
87        *self.cfg_mut() = evm_env.cfg_env.into();
88        *self.block_mut() = evm_env.block_env;
89    }
90
91    /// Cloned transaction environment.
92    fn tx_clone(&self) -> Self::Tx {
93        self.tx().clone()
94    }
95
96    /// Cloned EVM environment (Cfg + Block).
97    fn evm_clone(&self) -> EvmEnv<Self::Spec, Self::Block> {
98        EvmEnv::new(self.cfg().clone().into(), self.block().clone())
99    }
100}
101
102impl<
103    BLOCK: FoundryBlock + Clone,
104    TX: FoundryTransaction + Clone,
105    CFG: FoundryCfg,
106    DB: Database,
107    J: FoundryJournal<Database = DB>,
108    C: FoundryChain<TX>,
109> FoundryContextExt for Context<BLOCK, TX, CFG, DB, J, C>
110{
111    type Spec = <Self::Cfg as Cfg>::Spec;
112
113    fn block_mut(&mut self) -> &mut Self::Block {
114        &mut self.block
115    }
116
117    fn tx_mut(&mut self) -> &mut Self::Tx {
118        &mut self.tx
119    }
120
121    fn cfg_mut(&mut self) -> &mut Self::Cfg {
122        &mut self.cfg
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129    use alloy_evm::{EthEvmFactory, EvmFactory};
130    use alloy_primitives::U256;
131    use foundry_evm_hardforks::TempoHardfork;
132    use revm::{
133        context::{Block, Transaction},
134        database::EmptyDB,
135    };
136    use tempo_evm::TempoEvmFactory;
137
138    #[cfg(feature = "base")]
139    use base_common_evm::{BaseEvmFactory, BaseSpecId, BaseUpgrade};
140
141    #[test]
142    fn eth_evm_foundry_context_ext_implementation() {
143        let mut evm = EthEvmFactory::default().create_evm(EmptyDB::default(), EvmEnv::default());
144        assert_context_mutation(evm.ctx_mut(), SpecId::AMSTERDAM);
145    }
146
147    #[cfg(feature = "base")]
148    #[test]
149    fn base_evm_foundry_context_ext_implementation() {
150        let mut evm = BaseEvmFactory::default().create_evm(EmptyDB::default(), EvmEnv::default());
151        assert_context_mutation(evm.ctx_mut(), BaseSpecId::new(BaseUpgrade::Beryl));
152    }
153
154    #[cfg(feature = "monad")]
155    #[test]
156    fn monad_evm_foundry_context_ext_implementation() {
157        let mut evm = alloy_monad_evm::MonadEvmFactory::default().create_evm(
158            EmptyDB::default(),
159            EvmEnv::new(
160                CfgEnv::new_with_spec(monad_revm::MonadHardfork::MonadNine),
161                Default::default(),
162            ),
163        );
164        assert_context_mutation(evm.ctx_mut(), monad_revm::MonadHardfork::MonadEight);
165        evm.ctx_mut().journal_mut().set_preserve_reserve_balance(true);
166        let mut inner = evm.ctx().journal_inner().clone();
167        inner.depth = 2;
168        evm.ctx_mut().set_journal_inner(inner);
169        assert_eq!(evm.ctx().journal_inner().depth, 2);
170        assert!(evm.ctx().journal().preserves_reserve_balance());
171    }
172
173    #[test]
174    fn tempo_evm_foundry_context_ext_implementation() {
175        let mut evm = TempoEvmFactory::default().create_evm(EmptyDB::default(), EvmEnv::default());
176        assert_context_mutation(evm.ctx_mut(), TempoHardfork::Genesis);
177    }
178
179    #[cfg(feature = "optimism")]
180    mod optimism {
181        use super::*;
182        use alloy_op_evm::{OpEvmFactory, OpTx};
183        use op_revm::OpSpecId;
184
185        #[test]
186        fn op_evm_foundry_context_ext_implementation() {
187            let mut evm =
188                OpEvmFactory::<OpTx>::default().create_evm(EmptyDB::default(), EvmEnv::default());
189            assert_context_mutation(evm.ctx_mut(), OpSpecId::JOVIAN);
190        }
191    }
192
193    fn assert_context_mutation<CTX: FoundryContextExt>(ctx: &mut CTX, spec: CTX::Spec)
194    where
195        CTX::Spec: PartialEq,
196    {
197        ctx.block_mut().set_number(U256::from(123));
198        assert_eq!(ctx.block().number(), U256::from(123));
199        ctx.tx_mut().set_nonce(99);
200        assert_eq!(ctx.tx().nonce(), 99);
201        ctx.cfg_env_mut().spec = spec;
202        assert_eq!(ctx.cfg().spec(), spec);
203
204        let tx = ctx.tx_clone();
205        ctx.tx_mut().set_nonce(0);
206        ctx.set_tx(tx);
207        assert_eq!(ctx.tx().nonce(), 99);
208        ctx.cfg_env_mut().chain_id = 42;
209        let env = ctx.evm_clone();
210        ctx.block_mut().set_number(U256::ZERO);
211        ctx.cfg_env_mut().chain_id = 43;
212        ctx.set_evm(env);
213        assert_eq!(ctx.block().number(), U256::from(123));
214        assert_eq!(ctx.cfg().spec(), spec);
215        assert_eq!(ctx.cfg().chain_id(), 42);
216    }
217}