Skip to main content

foundry_evm_core/evm/
mod.rs

1//! Shared EVM traits, associated types, and execution helpers.
2//!
3//! Each network module owns its network marker and concrete EVM implementations.
4
5use crate::{
6    FoundryBlock, FoundryChain, FoundryContextExt, FoundryInspectorExt, FoundryJournal,
7    FoundryTransaction, FromAnyRpcTransaction,
8    backend::{DatabaseExt, JournaledState},
9    refresh_chain_journal,
10};
11use alloy_consensus::{SignableTransaction, Signed, transaction::SignerRecoverable};
12use alloy_evm::{Evm, EvmEnv, EvmFactory, FromRecoveredTx, precompiles::PrecompilesMap};
13use alloy_network::Network;
14use alloy_primitives::{Address, Signature, U256};
15use alloy_rlp::Decodable;
16use foundry_common::{FoundryReceiptResponse, FoundryTransactionBuilder, fmt::UIfmt};
17use foundry_config::ExecutionSpec;
18use foundry_fork_db::{DatabaseError, ForkBlockEnv};
19use revm::{
20    Database,
21    context::{
22        ContextTr, JournalTr, LocalContextTr,
23        result::{EVMError, HaltReason, ResultAndState},
24    },
25    handler::{EvmTr, FrameResult},
26    inspector::{InspectorEvmTr, InspectorHandler, NoOpInspector},
27    interpreter::{
28        CallInput, CallInputs, CallScheme, CallValue, CreateInputs, FrameInput, GasTracker,
29        InstructionResult, SharedMemory, interpreter::EthInterpreter,
30        interpreter_action::FrameInit,
31    },
32    primitives::hardfork::SpecId,
33    state::{AccountStatus, EvmState},
34};
35use serde::{Deserialize, Serialize};
36use std::{fmt::Debug, ops::DerefMut};
37
38#[cfg(feature = "base")]
39pub mod base;
40pub mod eth;
41#[cfg(feature = "monad")]
42pub mod monad;
43#[cfg(feature = "optimism")]
44pub mod op;
45pub mod tempo;
46
47pub use eth::*;
48pub use tempo::*;
49
50#[cfg(feature = "base")]
51pub use base::*;
52
53#[cfg(feature = "monad")]
54pub use monad::*;
55
56#[cfg(feature = "optimism")]
57pub use op::*;
58
59/// Foundry's compatibility trait associating a [`Network`] with a [`FoundryEvmFactory`].
60pub trait FoundryEvmNetwork: Copy + Debug + Default + 'static {
61    type Network: Network<
62            TxEnvelope: Decodable
63                            + SignerRecoverable
64                            + From<Signed<<Self::Network as Network>::UnsignedTx>>
65                            + for<'d> Deserialize<'d>
66                            + Serialize
67                            + UIfmt,
68            UnsignedTx: SignableTransaction<Signature>,
69            TransactionRequest: FoundryTransactionBuilder<Self::Network>
70                                    + for<'d> Deserialize<'d>
71                                    + Serialize,
72            ReceiptResponse: FoundryReceiptResponse,
73        >;
74    type EvmFactory: FoundryEvmFactory<Tx: FromRecoveredTx<<Self::Network as Network>::TxEnvelope>>;
75}
76
77pub trait FoundryEvmFactory:
78    EvmFactory<
79        Spec: Into<SpecId> + ExecutionSpec + Default + Copy + Unpin + Send + 'static,
80        BlockEnv: FoundryBlock + ForkBlockEnv + Default + Unpin,
81        Tx: Clone + Debug + FoundryTransaction + FromAnyRpcTransaction + Default + Send + Sync,
82        HaltReason: IntoInstructionResult,
83        Precompiles = PrecompilesMap,
84    > + Clone
85    + Debug
86    + Default
87    + 'static
88{
89    /// Chain type for EVM's context created by this factory.
90    type Chain: FoundryChain<Self::Tx>;
91
92    /// Foundry Context abstraction
93    type FoundryContext<'db>: FoundryContextExt<
94            Block = Self::BlockEnv,
95            Tx = Self::Tx,
96            Spec = Self::Spec,
97            Chain = Self::Chain,
98            Journal: FoundryJournal,
99            Db: DatabaseExt<Self>,
100        >
101    where
102        Self: 'db;
103
104    /// The Foundry-wrapped EVM type produced by this factory.
105    type FoundryEvm<'db, I: FoundryInspectorExt<Self::FoundryContext<'db>>>: Evm<
106            DB = &'db mut dyn DatabaseExt<Self>,
107            Tx = Self::Tx,
108            BlockEnv = Self::BlockEnv,
109            Spec = Self::Spec,
110            HaltReason = Self::HaltReason,
111        > + DerefMut<Target = Self::FoundryContext<'db>>
112    where
113        Self: 'db;
114
115    /// Creates a Foundry-wrapped EVM with the given inspector.
116    ///
117    /// Callers carrying execution context must install it through the returned context's
118    /// `chain_mut` before executing. This also preserves OP's block-derived L1 fee information.
119    fn create_foundry_evm_with_inspector<'db, I: FoundryInspectorExt<Self::FoundryContext<'db>>>(
120        &self,
121        db: &'db mut dyn DatabaseExt<Self>,
122        evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
123        inspector: I,
124    ) -> Self::FoundryEvm<'db, I>;
125
126    /// Creates a Foundry-wrapped nested EVM without an inspector.
127    fn create_nested_evm<'db>(
128        &self,
129        db: &'db mut dyn DatabaseExt<Self>,
130        evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
131    ) -> NestedEvmFor<'db, Self> {
132        self.create_nested_evm_with_inspector(db, evm_env, NoOpInspector)
133    }
134
135    /// Creates a Foundry-wrapped nested EVM with the given inspector.
136    /// Install inherited chain state with [`NestedEvm::chain_mut`] before executing or restoring
137    /// journal-derived state.
138    fn create_nested_evm_with_inspector<'db, I>(
139        &self,
140        db: &'db mut dyn DatabaseExt<Self>,
141        evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
142        inspector: I,
143    ) -> NestedEvmFor<'db, Self>
144    where
145        I: FoundryInspectorExt<Self::FoundryContext<'db>> + 'db;
146}
147
148/// Object-safe EVM operations used by nested execution and fork replay.
149///
150/// This abstracts over the concrete EVM type (`FoundryEvm`, future `TempoEvm`, etc.)
151/// so that cheatcode impls can build and run nested EVMs without knowing the concrete type.
152pub trait NestedEvm {
153    /// The spec type.
154    type Spec;
155    /// The block environment type.
156    type Block;
157    /// The transaction environment type.
158    type Tx: FoundryTransaction;
159    /// Chain context identifying the active transaction position.
160    type Chain: FoundryChain<Self::Tx>;
161    /// The Journal type, which may own Monad's reserve-balance-tracker state.
162    type Journal: FoundryJournal;
163    /// Returns a mutable reference to the journal inner state (`JournaledState`).
164    fn journal_inner_mut(&mut self) -> &mut JournaledState;
165
166    /// Returns a mutable reference to the transaction environment.
167    fn tx_mut(&mut self) -> &mut Self::Tx;
168
169    /// Returns a mutable reference to the chain-position context.
170    fn chain_mut(&mut self) -> &mut Self::Chain;
171
172    /// Returns the precompile map.
173    fn precompiles_mut(&mut self) -> &mut PrecompilesMap;
174
175    /// Returns a mutable reference to the Journal.
176    fn journal_mut(&mut self) -> &mut Self::Journal;
177
178    /// Runs a single execution frame (create or call) through the EVM handler loop.
179    fn run_execution(&mut self, frame: FrameInput) -> Result<FrameResult, EVMError<DatabaseError>>;
180
181    /// Executes a full transaction with the given tx env.
182    fn transact_raw(&mut self, tx: Self::Tx) -> eyre::Result<ResultAndState<HaltReason>>;
183
184    /// Replays a transaction, skipping unsupported system envelopes.
185    ///
186    /// `is_system` preserves the RPC envelope classification that conversion to `Self::Tx` may
187    /// discard. Returning `None` must not mutate the EVM, database, or inspector.
188    fn transact_replay(
189        &mut self,
190        tx: Self::Tx,
191        is_system: bool,
192    ) -> eyre::Result<Option<ResultAndState<HaltReason>>> {
193        if is_system {
194            return Ok(None);
195        }
196        self.transact_raw(tx).map(Some)
197    }
198
199    fn to_evm_env(&self) -> EvmEnv<Self::Spec, Self::Block>;
200}
201
202/// Converts a network-specific halt reason into an [`InstructionResult`].
203pub trait IntoInstructionResult {
204    fn into_instruction_result(self) -> InstructionResult;
205}
206
207/// Convenience type aliases for accessing associated types through [`FoundryEvmNetwork`].
208pub type EvmFactoryFor<FEN> = <FEN as FoundryEvmNetwork>::EvmFactory;
209pub type FoundryContextFor<'db, FEN> =
210    <EvmFactoryFor<FEN> as FoundryEvmFactory>::FoundryContext<'db>;
211pub type TxEnvFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::Tx;
212pub type HaltReasonFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::HaltReason;
213pub type SpecFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::Spec;
214pub type BlockEnvFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::BlockEnv;
215pub type PrecompilesFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::Precompiles;
216pub type EvmEnvFor<FEN> = EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>;
217pub type NetworkFor<FEN> = <FEN as FoundryEvmNetwork>::Network;
218pub type TxEnvelopeFor<FEN> = <NetworkFor<FEN> as Network>::TxEnvelope;
219pub type TransactionRequestFor<FEN> = <NetworkFor<FEN> as Network>::TransactionRequest;
220pub type TransactionResponseFor<FEN> = <NetworkFor<FEN> as Network>::TransactionResponse;
221pub type BlockResponseFor<FEN> = <NetworkFor<FEN> as Network>::BlockResponse;
222
223pub type ChainFor<FEN> = <EvmFactoryFor<FEN> as FoundryEvmFactory>::Chain;
224
225/// Boxed nested EVM produced by a Foundry EVM factory.
226pub type NestedEvmFor<'db, F> = Box<
227    dyn NestedEvm<
228            Spec = <F as EvmFactory>::Spec,
229            Block = <F as EvmFactory>::BlockEnv,
230            Tx = <F as EvmFactory>::Tx,
231            Chain = <F as FoundryEvmFactory>::Chain,
232            Journal = <<F as FoundryEvmFactory>::FoundryContext<'db> as ContextTr>::Journal,
233        > + 'db,
234>;
235
236/// Closure type used by `CheatcodesExecutor` methods that run nested EVM operations.
237pub type NestedEvmClosure<'a, F> = &'a mut dyn for<'j> FnMut(
238    &mut dyn NestedEvm<
239        Spec = <F as EvmFactory>::Spec,
240        Block = <F as EvmFactory>::BlockEnv,
241        Tx = <F as EvmFactory>::Tx,
242        Chain = <F as FoundryEvmFactory>::Chain,
243        Journal = <<F as FoundryEvmFactory>::FoundryContext<'j> as ContextTr>::Journal,
244    >,
245)
246    -> Result<(), EVMError<DatabaseError>>;
247
248/// Nested EVM closure for a Foundry EVM network.
249pub type NestedEvmClosureFor<'a, FEN> = NestedEvmClosure<'a, EvmFactoryFor<FEN>>;
250
251/// Runs a child operation with the parent's environment, journal, and native chain state.
252///
253/// Publishes child environment, journal, and chain changes only when the operation returns `Ok`.
254/// This does not roll back database or inspector effects: callers retain their existing ownership
255/// of those effects. The outer transaction environment is not replaced.
256///
257/// Both inspector adapters use this operation so inheritance and write-back remain paired. The
258/// existing Monad journal bridge is retained here until native journal lifecycle ownership
259/// migrates.
260pub fn with_inherited_evm<F, I>(
261    ecx: &mut F::FoundryContext<'_>,
262    inspector: I,
263    f: NestedEvmClosure<'_, F>,
264) -> Result<(), EVMError<DatabaseError>>
265where
266    F: FoundryEvmFactory,
267    I: for<'db> FoundryInspectorExt<F::FoundryContext<'db>>,
268{
269    let evm_env = ecx.evm_clone();
270    let chain_context = ecx.chain().clone();
271    #[cfg(feature = "monad")]
272    let mut reserve_balance = FoundryJournal::capture_reserve_balance(ecx.journal());
273    let (evm_env, journaled_state, chain_context) = {
274        let (db, journaled_state) = ecx.db_journal_inner_mut();
275        let journaled_state = journaled_state.clone();
276        let mut evm = F::default().create_nested_evm_with_inspector(db, evm_env, inspector);
277        *evm.chain_mut() = chain_context;
278        *evm.journal_inner_mut() = journaled_state;
279        #[cfg(feature = "monad")]
280        {
281            FoundryJournal::restore_reserve_balance(evm.journal_mut(), reserve_balance);
282            refresh_nested_chain_journal(&mut *evm);
283        }
284        f(&mut *evm)?;
285        #[cfg(feature = "monad")]
286        {
287            reserve_balance = FoundryJournal::capture_reserve_balance(evm.journal_mut());
288        }
289        (evm.to_evm_env(), evm.journal_inner_mut().clone(), evm.chain_mut().clone())
290    };
291    ecx.set_journal_inner(journaled_state);
292    ecx.set_evm(evm_env);
293    *ecx.chain_mut() = chain_context;
294    #[cfg(feature = "monad")]
295    FoundryJournal::restore_reserve_balance(ecx.journal_mut(), reserve_balance);
296    refresh_chain_journal(ecx);
297    Ok(())
298}
299
300/// Prepares account state for execution across a synthetic transaction boundary.
301///
302/// Preserve account flags, including local creation, while making accounts outside the protocol
303/// warm-address set cold. All storage starts cold with its current value as the child's original
304/// value. The parent's state is unchanged.
305pub fn prepare_child_state(journal: &JournaledState) -> EvmState {
306    let mut state = journal.state.clone();
307    for (address, account) in &mut state {
308        if journal.warm_addresses.is_cold(address) {
309            account.mark_cold();
310        }
311        for slot in account.storage.values_mut() {
312            slot.is_cold = true;
313            slot.original_value = slot.present_value;
314        }
315    }
316    state
317}
318
319/// Merges a child's returned account state into its suspended parent.
320///
321/// Preserve parent warmth and original storage values, import child account flags and current
322/// values, and retain untouched parent accounts and slots. Newly loaded accounts and slots keep
323/// their child metadata. This operates on the EVM's returned state, not on an unfiltered write set;
324/// the caller retains responsibility for execution errors and family-specific reconciliation.
325pub fn merge_child_state(parent: &mut EvmState, child: EvmState) {
326    for (address, mut account) in child {
327        let Some(parent_account) = parent.get_mut(&address) else {
328            parent.insert(address, account);
329            continue;
330        };
331        if account.status.contains(AccountStatus::Cold)
332            && !parent_account.status.contains(AccountStatus::Cold)
333        {
334            account.status -= AccountStatus::Cold;
335        }
336        parent_account.info = account.info;
337        parent_account.status |= account.status;
338        for (key, slot) in account.storage {
339            let Some(parent_slot) = parent_account.storage.get_mut(&key) else {
340                parent_account.storage.insert(key, slot);
341                continue;
342            };
343            parent_slot.present_value = slot.present_value;
344            parent_slot.is_cold &= slot.is_cold;
345        }
346    }
347}
348
349/// Runs a nested frame with inspection and settles its gas into the parent frame.
350pub(crate) fn run_inspected_frame<H>(
351    evm: &mut H::Evm,
352    mut handler: H,
353    frame_input: FrameInput,
354) -> Result<FrameResult, H::Error>
355where
356    H: InspectorHandler<IT = EthInterpreter>,
357    H::Evm: InspectorEvmTr,
358{
359    let memory =
360        SharedMemory::new_with_buffer(evm.ctx_ref().local().shared_memory_buffer().clone());
361    let first_frame_input = FrameInit { depth: 0, memory, frame_input };
362    let mut frame_result = handler.inspect_run_exec_loop(evm, first_frame_input)?;
363    let mut parent_gas = GasTracker::new(
364        frame_result.gas().limit(),
365        frame_result.gas().remaining(),
366        frame_result.gas().reservoir(),
367    );
368    handler.last_frame_result(evm, &mut frame_result, &mut parent_gas)?;
369    Ok(frame_result)
370}
371
372/// Get the call inputs for the CREATE2 factory.
373pub fn get_create2_factory_call_inputs<T: JournalTr>(
374    salt: U256,
375    inputs: &CreateInputs,
376    deployer: Address,
377    journal: &mut T,
378) -> Result<CallInputs, <T::Database as Database>::Error> {
379    let calldata = [&salt.to_be_bytes::<32>()[..], &inputs.init_code()[..]].concat();
380    let account = journal.load_account_with_code(deployer)?;
381    Ok(CallInputs {
382        caller: inputs.caller(),
383        bytecode_address: deployer,
384        known_bytecode: (account.info.code_hash, account.info.code.clone().unwrap_or_default()),
385        target_address: deployer,
386        scheme: CallScheme::Call,
387        value: CallValue::Transfer(inputs.value()),
388        input: CallInput::Bytes(calldata.into()),
389        gas_limit: inputs.gas_limit(),
390        reservoir: inputs.reservoir(),
391        is_static: false,
392        return_memory_offset: 0..0,
393        charged_new_account_state_gas: false,
394    })
395}
396
397#[cfg(test)]
398mod tests {
399    use super::*;
400    use crate::backend::Backend;
401    use alloy_evm::EthEvmFactory;
402    use revm::{
403        context::Transaction,
404        state::{Account, AccountInfo, EvmStorageSlot, TransactionId},
405    };
406
407    #[cfg(feature = "monad")]
408    use alloy_monad_evm::MonadEvmFactory;
409    #[cfg(feature = "monad")]
410    use monad_revm::{MonadHardfork, MonadJournalTr, reserve_balance::tracker::ReserveBalanceInit};
411    #[cfg(feature = "monad")]
412    use revm::context::{BlockEnv, CfgEnv};
413
414    #[test]
415    fn inherited_journal_publishes_only_after_success() {
416        let address = Address::with_last_byte(0x42);
417        for succeeds in [false, true] {
418            let mut db = Backend::<EthEvmNetwork>::spawn(None).unwrap();
419            let mut parent = EthEvmFactory::default().create_foundry_evm_with_inspector(
420                &mut db,
421                EvmEnvFor::<EthEvmNetwork>::default(),
422                NoOpInspector,
423            );
424            parent.journaled_state.inner.depth = 3;
425            parent
426                .journaled_state
427                .inner
428                .state
429                .insert(address, Account::from(AccountInfo::from_balance(U256::from(7))));
430            let caller = parent.tx().caller();
431            let result =
432                with_inherited_evm::<EthEvmFactory, _>(&mut parent, NoOpInspector, &mut |child| {
433                    assert_eq!(child.journal_inner_mut().depth, 3);
434                    assert_eq!(
435                        child.journal_inner_mut().state[&address].info.balance,
436                        U256::from(7)
437                    );
438                    child.journal_inner_mut().depth = 4;
439                    child.journal_inner_mut().state.get_mut(&address).unwrap().info.balance =
440                        U256::from(9);
441                    child.tx_mut().caller = address;
442                    if succeeds { Ok(()) } else { Err(EVMError::Custom("abort child".into())) }
443                });
444            assert_eq!(result.is_ok(), succeeds);
445            assert_eq!(parent.journaled_state.inner.depth, if succeeds { 4 } else { 3 });
446            assert_eq!(
447                parent.journaled_state.inner.state[&address].info.balance,
448                U256::from(if succeeds { 9 } else { 7 })
449            );
450            assert_eq!(parent.tx().caller(), caller);
451        }
452    }
453
454    #[cfg(feature = "monad")]
455    #[test]
456    fn inherited_monad_tracker_and_chain_publish_together() {
457        let sender = Address::with_last_byte(0x42);
458        for succeeds in [false, true] {
459            let mut db = Backend::<MonadEvmNetwork>::spawn(None).unwrap();
460            let mut parent = MonadEvmFactory::default().create_foundry_evm_with_inspector(
461                &mut db,
462                EvmEnv::new(CfgEnv::new_with_spec(MonadHardfork::MonadNine), BlockEnv::default()),
463                NoOpInspector,
464            );
465            let account = Account::from(AccountInfo::from_balance(U256::from(12)));
466            let chain = parent.chain().clone();
467            parent.journaled_state.reserve_balance_mut().init(ReserveBalanceInit {
468                chain: &chain,
469                spec: MonadHardfork::MonadNine,
470                sender,
471                effective_gas_price: 0,
472                gas_limit: 0,
473                sender_is_delegated: false,
474                sender_account: Some(&account),
475            });
476            parent.journaled_state.inner.state.insert(sender, account);
477            let tracker = parent.journaled_state.reserve_balance().clone();
478            let result = with_inherited_evm::<MonadEvmFactory, _>(
479                &mut parent,
480                NoOpInspector,
481                &mut |child| {
482                    assert_eq!(child.journal_mut().reserve_balance(), &tracker);
483                    child.chain_mut().parent_senders_and_authorities.insert(sender);
484                    child.journal_inner_mut().state.get_mut(&sender).unwrap().info.balance =
485                        U256::from(9);
486                    if succeeds { Ok(()) } else { Err(EVMError::Custom("abort child".into())) }
487                },
488            );
489            assert_eq!(result.is_ok(), succeeds);
490            if succeeds {
491                assert!(parent.chain().parent_senders_and_authorities.contains(&sender));
492                assert!(parent.journaled_state.reserve_balance().has_violation());
493            } else {
494                assert_eq!(parent.chain(), &chain);
495                assert_eq!(parent.journaled_state.reserve_balance(), &tracker);
496                assert_eq!(
497                    parent.journaled_state.inner.state[&sender].info.balance,
498                    U256::from(12)
499                );
500            }
501        }
502    }
503
504    #[test]
505    fn preparation_preserves_creation_and_protocol_warmth() {
506        let address = Address::with_last_byte(0x42);
507        let protocol_address = Address::with_last_byte(0x43);
508        let key = U256::ONE;
509        let mut account = Account::from(AccountInfo::default());
510        account.mark_created_locally();
511        account.mark_touch();
512        account.storage.insert(
513            key,
514            EvmStorageSlot::new_changed(U256::from(3), U256::from(7), TransactionId::ZERO),
515        );
516        let mut journal = JournaledState::default();
517        journal.state.insert(address, account.clone());
518        journal.state.insert(protocol_address, account);
519        journal.warm_addresses.set_coinbase(protocol_address);
520        let before = journal.state.clone();
521
522        let child = prepare_child_state(&journal);
523
524        assert_eq!(journal.state, before);
525        assert!(child[&address].is_created_locally());
526        assert!(child[&address].is_touched());
527        assert!(child[&address].status.contains(AccountStatus::Cold));
528        assert!(!child[&protocol_address].status.contains(AccountStatus::Cold));
529        for account in child.values() {
530            assert_eq!(account.storage[&key].original_value, U256::from(7));
531            assert_eq!(account.storage[&key].present_value, U256::from(7));
532            assert!(account.storage[&key].is_cold);
533        }
534    }
535
536    #[test]
537    fn settlement_preserves_parent_original_values_and_combines_warmth() {
538        let address = Address::with_last_byte(0x42);
539        let key = U256::ONE;
540        for parent_cold in [false, true] {
541            for child_cold in [false, true] {
542                let mut account = Account::from(AccountInfo::default());
543                account.status.set(AccountStatus::Cold, parent_cold);
544                account.mark_created_locally();
545                let mut slot =
546                    EvmStorageSlot::new_changed(U256::from(3), U256::from(7), TransactionId::ZERO);
547                slot.is_cold = parent_cold;
548                account.storage.insert(key, slot);
549                let mut parent = EvmState::from_iter([(address, account)]);
550                let mut account = Account::from(AccountInfo::from_balance(U256::from(9)));
551                account.status.set(AccountStatus::Cold, child_cold);
552                account.mark_touch();
553                let mut slot =
554                    EvmStorageSlot::new_changed(U256::from(7), U256::from(11), TransactionId::ZERO);
555                slot.is_cold = child_cold;
556                account.storage.insert(key, slot);
557
558                merge_child_state(&mut parent, EvmState::from_iter([(address, account)]));
559
560                let account = &parent[&address];
561                assert!(account.is_created_locally());
562                assert!(account.is_touched());
563                assert_eq!(account.info.balance, U256::from(9));
564                // Account flags are unioned; a cold parent retains its flag until journal access.
565                assert_eq!(account.status.contains(AccountStatus::Cold), parent_cold);
566                assert_eq!(account.storage[&key].original_value, U256::from(3));
567                assert_eq!(account.storage[&key].present_value, U256::from(11));
568                assert_eq!(account.storage[&key].is_cold, parent_cold && child_cold);
569            }
570        }
571    }
572}