Skip to main content

anvil/eth/backend/mem/
monad.rs

1//! Monad-specific execution and context helpers for the in-memory backend.
2
3use super::{
4    AnvilInspector, Backend, ClientFork, DatabaseRef, EnvelopeExecutionKind, MonadExecutionContext,
5    storage::BlockchainStorage,
6};
7use crate::eth::{
8    backend::{
9        db::MonadBlockReplayProfile,
10        executor::{
11            AnvilBlockExecutor, ExecutedPoolTransactions, PoolTransactionHooks, PoolTxGasConfig,
12            build_tx_env_for_pending, execute_pool_transactions,
13        },
14        replay::{
15            ExecutedHistoricalReplay, HistoricalReplayTransaction, execute_historical_replay_with,
16        },
17    },
18    error::{BlockchainError, InvalidTransactionError},
19    pool::transactions::PoolTransaction,
20};
21use alloy_consensus::{
22    BlockHeader, Transaction as _, constants::EMPTY_ROOT_HASH, transaction::Recovered,
23};
24use alloy_evm::{
25    Database, Evm, EvmEnv, EvmFactory, RecoveredTx,
26    block::{BlockExecutionError, BlockExecutionResult, BlockExecutor, StateDB},
27};
28use alloy_monad_evm::{MonadContext, MonadEvm, MonadEvmFactory};
29use alloy_network::{BlockResponse, Network};
30use alloy_primitives::{B256, U256};
31use alloy_rpc_types::{AccessList, BlockNumberOrTag as BlockNumber, BlockTransactions};
32use anvil_core::eth::{
33    block::Block,
34    transaction::{MaybeImpersonatedTransaction, PendingTransaction},
35};
36use eyre::{Context, Result};
37use foundry_evm::{
38    backend::DatabaseError,
39    core::{
40        FoundryChain, FromAnyRpcTransaction,
41        evm::{
42            EvmEnvFor, FoundryEvmFactory, MonadBlockParticipants, MonadEvmNetwork,
43            monad_block_participants, monad_context_from_participants, protocol_system_call,
44        },
45    },
46    hardfork::FoundryHardfork,
47    utils::get_blob_params,
48};
49use foundry_primitives::{FoundryReceiptEnvelope, FoundryTxEnvelope};
50use monad_revm::{MonadChainContext, MonadHardfork, instructions::monad_gas_params};
51use revm::{
52    Inspector,
53    context::{Transaction, TxEnv},
54    context_interface::{
55        result::{HaltReason, InvalidTransaction, ResultAndState},
56        transaction::AuthorizationTr,
57    },
58    database_interface::WrapDatabaseRef,
59    primitives::hardfork::SpecId,
60    state::AccountInfo,
61};
62use std::sync::Arc;
63use tracing::debug;
64
65pub(super) struct PreparedExecution {
66    pub(super) context: Option<MonadChainContext>,
67    pub(super) kind: EnvelopeExecutionKind,
68    pub(super) hardfork: MonadHardfork,
69}
70
71pub(super) struct ForkReplay {
72    hardfork: FoundryHardfork,
73    context: Option<MonadChainContext>,
74    participants: MonadBlockParticipants,
75    execution_chain_id: u64,
76    source_chain_id: u64,
77    timestamp: u64,
78    inferred_hardfork: bool,
79}
80
81impl ForkReplay {
82    pub(super) const fn hardfork(&self) -> FoundryHardfork {
83        self.hardfork
84    }
85
86    pub(super) const fn take_context(&mut self) -> Option<MonadChainContext> {
87        self.context.take()
88    }
89
90    pub(super) fn store_metadata<N: Network>(
91        &mut self,
92        storage: &mut BlockchainStorage<N>,
93        block_hash: B256,
94    ) {
95        store_block_metadata(
96            storage,
97            block_hash,
98            std::mem::take(&mut self.participants),
99            self.execution_chain_id,
100            self.hardfork,
101        );
102    }
103}
104
105pub(super) fn normalize_access_list(
106    mut access_list: AccessList,
107    hardfork: MonadHardfork,
108) -> AccessList {
109    if MonadHardfork::MonadTen.is_enabled_in(hardfork) {
110        for item in &mut access_list.0 {
111            item.storage_keys.sort_unstable();
112            item.storage_keys.dedup_by_key(|slot| {
113                monad_revm::page::page_index(U256::from_be_slice(slot.as_slice()))
114            });
115        }
116    }
117    access_list
118}
119
120/// Caches the fork blocks needed to construct the next Monad block's ancestor context.
121pub(super) async fn cache_fork_context(fork: &ClientFork) -> Result<(), BlockchainError> {
122    let block =
123        fork.block_by_hash_full(fork.block_hash()).await?.ok_or(BlockchainError::BlockNotFound)?;
124    let parent_hash = block.header().parent_hash();
125    if !parent_hash.is_zero() {
126        fork.block_by_hash_full(parent_hash).await?.ok_or(BlockchainError::BlockNotFound)?;
127    }
128    Ok(())
129}
130
131/// Adds a candidate transaction to the current Monad block context.
132fn append_transaction(chain: &mut MonadChainContext, tx: &TxEnv) {
133    chain.current_tx_index = chain.current_block_senders.len();
134    chain.current_block_senders.push(tx.caller());
135    chain.current_block_authorities.push(
136        tx.authorization_list().filter_map(|authorization| authorization.authority()).collect(),
137    );
138}
139
140pub(super) fn prepare_transaction<DB: alloy_evm::Database>(
141    evm: &mut MonadEvm<DB, AnvilInspector>,
142    tx: &TxEnv,
143) {
144    append_transaction(&mut evm.ctx_mut().chain, tx);
145}
146
147pub(super) fn resolve_execution_context(
148    context: Option<MonadExecutionContext<'_>>,
149    tx: &TxEnv,
150) -> Option<MonadChainContext> {
151    match context {
152        Some(MonadExecutionContext::Exact(context)) => Some(*context),
153        Some(MonadExecutionContext::Next(context)) => {
154            append_transaction(context, tx);
155            Some(context.clone())
156        }
157        None => None,
158    }
159}
160
161pub(super) fn advance_block_context(context: &mut Option<MonadChainContext>) {
162    let Some(context) = context else { return };
163    let current = context
164        .current_block_senders
165        .iter()
166        .copied()
167        .chain(context.current_block_authorities.iter().flatten().copied())
168        .collect();
169    context.grandparent_senders_and_authorities =
170        std::mem::replace(&mut context.parent_senders_and_authorities, current);
171    context.current_block_senders.clear();
172    context.current_block_authorities.clear();
173    context.current_tx_index = 0;
174}
175
176/// Removes a candidate transaction that failed before block inclusion.
177pub(super) fn rollback_transaction<DB: alloy_evm::Database>(
178    evm: &mut MonadEvm<DB, AnvilInspector>,
179) {
180    let chain = &mut evm.ctx_mut().chain;
181    chain.current_block_senders.pop();
182    chain.current_block_authorities.pop();
183    chain.current_tx_index = chain.current_block_senders.len();
184}
185
186impl<N: Network> Backend<N> {
187    /// Prepares the Monad-specific inputs for replaying a historical transaction prefix.
188    pub(super) async fn prepare_monad_fork_replay(
189        &self,
190        source_chain_id: u64,
191        execution_chain_id: u64,
192        timestamp: u64,
193        parent_hash: B256,
194        transactions: &[HistoricalReplayTransaction],
195    ) -> Result<Option<ForkReplay>> {
196        if !self.is_monad() {
197            return Ok(None);
198        }
199
200        let explicit_hardfork = self.node_config.read().await.hardfork;
201        // The target block's schedule can cross a hardfork boundary after the selected parent.
202        // Explicit overrides still take precedence over timestamp inference.
203        let hardfork = explicit_hardfork
204            .or_else(|| {
205                MonadHardfork::from_chain_and_timestamp(source_chain_id, timestamp)
206                    .map(FoundryHardfork::Monad)
207            })
208            .unwrap_or_else(|| self.hardfork());
209        let context = self.monad_context_for_child_of_block_hash(parent_hash).await?;
210        let participants =
211            monad_block_participants(&self.monad_historical_replay_tx_envs(transactions));
212
213        Ok(Some(ForkReplay {
214            hardfork,
215            context: Some(context),
216            participants,
217            execution_chain_id,
218            source_chain_id,
219            timestamp,
220            inferred_hardfork: explicit_hardfork.is_none(),
221        }))
222    }
223
224    /// Applies the Monad execution rules selected for a completed fork replay.
225    pub(super) fn finalize_monad_fork_replay(&self, replay: &ForkReplay, evm_env: &mut EvmEnv) {
226        let spec_id = SpecId::from(replay.hardfork);
227        evm_env.cfg_env.set_spec_and_mainnet_gas_params(spec_id);
228        self.fees.set_execution_rules(
229            spec_id,
230            self.networks.base_fee_params(replay.timestamp),
231            None,
232        );
233        self.fees.set_blob_params(get_blob_params(replay.source_chain_id, replay.timestamp));
234
235        if replay.inferred_hardfork {
236            *self.hardfork.write() = replay.hardfork;
237            if let Some(fork) = self.fork.read().clone() {
238                fork.config.write().hardfork = Some(replay.hardfork);
239            }
240        }
241    }
242
243    /// Validates Monad-specific transaction type restrictions.
244    pub(super) const fn validate_monad_transaction_type(
245        &self,
246        tx: &FoundryTxEnvelope,
247    ) -> Result<(), InvalidTransactionError> {
248        if self.is_monad() && tx.is_eip4844() {
249            return Err(InvalidTransactionError::MonadBlobTransactionUnsupported);
250        }
251        Ok(())
252    }
253
254    /// Validates Monad's gas-only transaction balance requirement.
255    ///
256    /// Returns whether the transaction was validated as a Monad transaction.
257    pub(super) fn validate_monad_transaction_funds(
258        &self,
259        pending: &PendingTransaction<FoundryTxEnvelope>,
260        account: &AccountInfo,
261        evm_env: &EvmEnv,
262    ) -> Result<bool, InvalidTransactionError> {
263        if !self.is_monad() {
264            return Ok(false);
265        }
266
267        let tx = &pending.transaction;
268        let effective_gas_price = tx.effective_gas_price(Some(evm_env.block_env.basefee));
269        let required = U256::from(tx.gas_limit()) * U256::from(effective_gas_price);
270        if account.balance < required {
271            debug!(target: "backend", "[{:?}] insufficient balance={}, required={} account={:?}", tx.hash(), account.balance, required, *pending.sender());
272            return Err(InvalidTransactionError::InsufficientFunds);
273        }
274        Ok(true)
275    }
276
277    /// Validates a forced Monad protocol transaction selected for mining.
278    ///
279    /// Returns whether the transaction was recognized and fully validated as a protocol call.
280    pub(super) fn validate_monad_mining_pool_transaction_for(
281        &self,
282        pool_tx: &PoolTransaction<FoundryTxEnvelope>,
283        account: &AccountInfo,
284        evm_env: &EvmEnv,
285    ) -> Result<bool, InvalidTransactionError> {
286        if !self.is_monad() || !pool_tx.is_replay {
287            return Ok(false);
288        }
289
290        let tx_env: TxEnv = build_tx_env_for_pending(&pool_tx.pending_transaction, self.cheats());
291        let Some(system_call) = protocol_system_call(&tx_env).map_err(|err| {
292            InvalidTransactionError::Revm(InvalidTransaction::Str(err.to_string().into()))
293        })?
294        else {
295            return Ok(false);
296        };
297        if system_call.chain_id.is_some_and(|chain_id| chain_id != evm_env.cfg_env.chain_id) {
298            return Err(InvalidTransactionError::InvalidChainId);
299        }
300        if system_call.nonce < account.nonce {
301            return Err(InvalidTransactionError::NonceTooLow);
302        }
303        if system_call.nonce > account.nonce {
304            return Err(InvalidTransactionError::NonceTooHigh);
305        }
306        if system_call.nonce == u64::MAX {
307            return Err(InvalidTransactionError::NonceMaxValue);
308        }
309        Ok(true)
310    }
311
312    /// Executes a candidate block through a concrete Monad EVM.
313    #[allow(clippy::too_many_arguments, clippy::type_complexity)]
314    pub(super) fn execute_with_monad_block_executor<DB>(
315        &self,
316        db: DB,
317        evm_env: &EvmEnv,
318        parent_hash: B256,
319        spec_id: SpecId,
320        hardfork: FoundryHardfork,
321        pool_transactions: &[Arc<PoolTransaction<FoundryTxEnvelope>>],
322        gas_config: &PoolTxGasConfig,
323        inspector_tx_config: &crate::mem::inspector::InspectorTxConfig,
324        validator: &dyn Fn(
325            &PoolTransaction<FoundryTxEnvelope>,
326            &AccountInfo,
327        ) -> Result<(), InvalidTransactionError>,
328    ) -> Result<
329        (ExecutedPoolTransactions<FoundryTxEnvelope>, BlockExecutionResult<FoundryReceiptEnvelope>),
330        BlockchainError,
331    >
332    where
333        DB: StateDB<Error = DatabaseError>,
334    {
335        let hardfork = MonadHardfork::from(hardfork);
336        let monad_env = Self::build_monad_evm_env(evm_env, hardfork);
337        let inspector = self.build_mining_inspector();
338        let mut evm =
339            MonadEvmFactory::default().create_evm_with_inspector(db, monad_env, inspector);
340        let transaction_context = self
341            .monad_context_for_child_of(parent_hash)
342            .expect("Monad ancestor context must be available before block execution");
343        evm.ctx_mut().chain = transaction_context;
344        self.inject_precompiles(evm.precompiles_mut(), evm_env);
345
346        let mut executor = AnvilBlockExecutor::new(evm, parent_hash, spec_id, None)
347            .with_max_blob_gas_per_block(gas_config.max_blob_gas_per_block);
348        executor
349            .apply_pre_execution_changes()
350            .map_err(|err| BlockchainError::Internal(err.to_string()))?;
351        let mut hooks = PoolTransactionHooks {
352            before_transaction: prepare_transaction,
353            execute_transaction: |executor: &mut AnvilBlockExecutor<_>,
354                                  tx_env: TxEnv,
355                                  recovered: Recovered<FoundryTxEnvelope>,
356                                  is_replay: bool| {
357                if !is_replay {
358                    return executor.execute_transaction_without_commit((tx_env, recovered));
359                }
360                match protocol_system_call(&tx_env) {
361                    Ok(None) => {
362                        return executor.execute_transaction_without_commit((tx_env, recovered));
363                    }
364                    Ok(Some(_)) => {}
365                    Err(err) => return Err(BlockExecutionError::msg(err)),
366                }
367                executor.execute_transaction_without_commit_with(
368                    (tx_env, recovered),
369                    |evm, tx_env, transaction_hash| {
370                        MonadEvmFactory::default()
371                            .try_transact_system_replay(evm, &tx_env)
372                            .map_err(|err| {
373                                BlockExecutionError::msg(format!(
374                                    "failed to replay Monad transaction {transaction_hash}: {err}"
375                                ))
376                            })?
377                            .ok_or_else(|| {
378                                BlockExecutionError::msg(format!(
379                                    "Monad transaction {transaction_hash} is not a canonical replay \
380                                     envelope"
381                                ))
382                            })
383                    },
384                )
385            },
386            on_execution_error: rollback_transaction,
387        };
388        let pool_result = execute_pool_transactions(
389            &mut executor,
390            pool_transactions,
391            gas_config,
392            inspector_tx_config,
393            self.cheats(),
394            validator,
395            &mut hooks,
396        );
397        let (evm, block_result) =
398            executor.finish().map_err(|err| BlockchainError::Internal(err.to_string()))?;
399        drop(evm);
400        Ok((pool_result, block_result))
401    }
402
403    /// Executes a historical transaction prefix through a concrete Monad EVM.
404    #[allow(clippy::too_many_arguments)]
405    pub(super) fn execute_with_monad_replay_block_executor<DB>(
406        &self,
407        db: DB,
408        evm_env: &EvmEnv,
409        parent_hash: B256,
410        hardfork: FoundryHardfork,
411        transactions: &[HistoricalReplayTransaction],
412        inspector_tx_config: &crate::mem::inspector::InspectorTxConfig,
413        transaction_context: Option<MonadChainContext>,
414    ) -> Result<ExecutedHistoricalReplay>
415    where
416        DB: StateDB<Error = DatabaseError>,
417    {
418        let hardfork = MonadHardfork::from(hardfork);
419        let monad_env = Self::build_monad_evm_env(evm_env, hardfork);
420        let inspector = self.build_mining_inspector();
421        let mut evm =
422            MonadEvmFactory::default().create_evm_with_inspector(db, monad_env, inspector);
423        evm.ctx_mut().chain = transaction_context
424            .ok_or_else(|| eyre::eyre!("Monad replay ancestor context is unavailable"))?;
425        self.inject_precompiles(evm.precompiles_mut(), evm_env);
426
427        let mut executor = AnvilBlockExecutor::new(evm, parent_hash, *evm_env.spec_id(), None)
428            .with_state_changes();
429        executor
430            .apply_pre_execution_changes()
431            .wrap_err("failed to apply replay block-start transitions")?;
432        let (transactions, transaction_infos) = execute_historical_replay_with(
433            &mut executor,
434            transactions,
435            inspector_tx_config,
436            |evm, tx_env, _transaction_hash| {
437                prepare_transaction(evm, &tx_env);
438                let result = match MonadEvmFactory::default()
439                    .try_transact_system_replay(evm, &tx_env)
440                    .map_err(BlockExecutionError::msg)
441                {
442                    Ok(Some(result)) => Ok(result),
443                    Ok(None) => evm.transact(tx_env).map_err(BlockExecutionError::msg),
444                    Err(err) => Err(err),
445                };
446                if result.is_err() {
447                    rollback_transaction(evm);
448                }
449                result
450            },
451        )?;
452        let state_changes = executor.take_state_changes();
453        let (evm, block_result) =
454            executor.finish().wrap_err("failed to finish replay block execution")?;
455        drop(evm);
456        Ok(ExecutedHistoricalReplay {
457            block_result,
458            transactions,
459            transaction_infos,
460            state_changes,
461        })
462    }
463
464    /// Reconstructs a locally mined transaction using its authoritative stored sender.
465    pub(super) fn monad_pending_mined_transaction_from_storage(
466        storage: &BlockchainStorage<N>,
467        transaction: MaybeImpersonatedTransaction<FoundryTxEnvelope>,
468    ) -> Result<PendingTransaction<FoundryTxEnvelope>, BlockchainError> {
469        let transaction_hash = transaction.hash();
470        let mined =
471            storage.transactions.get(&transaction_hash).ok_or(BlockchainError::DataUnavailable)?;
472        Ok(PendingTransaction::with_sender(transaction, mined.info.from))
473    }
474
475    /// Converts retained local transactions using their authoritative mined senders.
476    fn monad_tx_envs_from_storage(
477        &self,
478        storage: &BlockchainStorage<N>,
479        transactions: &[MaybeImpersonatedTransaction<FoundryTxEnvelope>],
480    ) -> Result<Vec<TxEnv>, BlockchainError> {
481        transactions
482            .iter()
483            .map(|transaction| {
484                let pending = Self::monad_pending_mined_transaction_from_storage(
485                    storage,
486                    transaction.clone(),
487                )?;
488                Ok(build_tx_env_for_pending::<FoundryTxEnvelope, TxEnv>(&pending, self.cheats()))
489            })
490            .collect()
491    }
492
493    /// Converts a historical replay prefix using its prepared authoritative senders.
494    pub(super) fn monad_historical_replay_tx_envs(
495        &self,
496        transactions: &[HistoricalReplayTransaction],
497    ) -> Vec<TxEnv> {
498        transactions
499            .iter()
500            .map(|replay| {
501                let pending = PendingTransaction::with_sender(
502                    MaybeImpersonatedTransaction::new(replay.transaction.tx().clone()),
503                    replay.transaction.signer(),
504                );
505                build_tx_env_for_pending::<FoundryTxEnvelope, TxEnv>(&pending, self.cheats())
506            })
507            .collect()
508    }
509
510    /// Returns a block's number, parent hash, and cached participants.
511    fn monad_block_participants_from_storage(
512        &self,
513        storage: &BlockchainStorage<N>,
514        hash: B256,
515    ) -> Result<(u64, B256, MonadBlockParticipants), BlockchainError> {
516        if let Some(block) = storage.blocks.get(&hash) {
517            let participants = match storage.monad_block_participants.get(&hash) {
518                Some(participants) => participants.clone(),
519                None if block.body.transactions.is_empty()
520                    && block.header.transactions_root() != EMPTY_ROOT_HASH =>
521                {
522                    return Err(BlockchainError::DataUnavailable);
523                }
524                None => monad_block_participants(
525                    &self.monad_tx_envs_from_storage(storage, &block.body.transactions)?,
526                ),
527            };
528            return Ok((block.header.number(), block.header.parent_hash, participants));
529        }
530
531        let fork = self.get_fork().ok_or(BlockchainError::BlockNotFound)?;
532        let block = fork
533            .storage
534            .read()
535            .blocks
536            .get(&hash)
537            .cloned()
538            .ok_or(BlockchainError::DataUnavailable)?;
539        let BlockTransactions::Full(transactions) = block.transactions() else {
540            return Err(BlockchainError::DataUnavailable);
541        };
542        let tx_envs = transactions
543            .iter()
544            .map(TxEnv::from_any_rpc_transaction)
545            .collect::<eyre::Result<Vec<_>>>()?;
546        Ok((
547            block.header().number(),
548            block.header().parent_hash(),
549            monad_block_participants(&tx_envs),
550        ))
551    }
552
553    fn monad_block_participants(
554        &self,
555        hash: B256,
556    ) -> Result<(u64, B256, MonadBlockParticipants), BlockchainError> {
557        self.monad_block_participants_from_storage(&self.blockchain.storage.read(), hash)
558    }
559
560    /// Rebuilds participant metadata for locally stored blocks with transaction bodies.
561    pub(super) fn rebuild_monad_block_participant_cache(
562        &self,
563        storage: &mut BlockchainStorage<N>,
564    ) -> Result<(), BlockchainError> {
565        let participants = storage
566            .blocks
567            .iter()
568            .filter(|(hash, block)| {
569                !storage.monad_block_participants.contains_key(*hash)
570                    && (!block.body.transactions.is_empty()
571                        || block.header.transactions_root() == EMPTY_ROOT_HASH)
572            })
573            .map(|(hash, block)| (*hash, block.body.transactions.clone()))
574            .map(|(hash, transactions)| {
575                let tx_envs = self.monad_tx_envs_from_storage(storage, &transactions)?;
576                Ok((hash, monad_block_participants(&tx_envs)))
577            })
578            .collect::<Result<Vec<_>, BlockchainError>>()?;
579
580        for (hash, participants) in participants {
581            if storage.blocks.contains_key(&hash) {
582                storage.monad_block_participants.insert(hash, participants);
583            }
584        }
585        Ok(())
586    }
587
588    /// Builds the initial context for a block whose parent is `parent_hash`.
589    pub(super) fn monad_context_for_child_of(
590        &self,
591        parent_hash: B256,
592    ) -> Result<MonadChainContext, BlockchainError> {
593        self.monad_context_for_child_of_in_storage(&self.blockchain.storage.read(), parent_hash)
594    }
595
596    /// Builds the initial context from staged storage.
597    pub(super) fn monad_context_for_child_of_in_storage(
598        &self,
599        storage: &BlockchainStorage<N>,
600        parent_hash: B256,
601    ) -> Result<MonadChainContext, BlockchainError> {
602        let (_, grandparent_hash, parent) =
603            self.monad_block_participants_from_storage(storage, parent_hash)?;
604        let grandparent = if grandparent_hash.is_zero() {
605            MonadBlockParticipants::default()
606        } else {
607            self.monad_block_participants_from_storage(storage, grandparent_hash)?.2
608        };
609        Ok(monad_context_from_participants(grandparent, parent, &[], 0))
610    }
611
612    async fn monad_context_for_child_of_block(
613        &self,
614        block: alloy_network::AnyRpcBlock,
615        block_hash: B256,
616    ) -> Result<MonadChainContext, BlockchainError> {
617        let parent_hash = block.header().parent_hash();
618        if !parent_hash.is_zero() {
619            self.block_by_hash_full(parent_hash).await?.ok_or(BlockchainError::DataUnavailable)?;
620        }
621        self.monad_context_for_child_of(block_hash)
622    }
623
624    /// Fetches the full blocks required to build context on top of `block_number`.
625    pub(super) async fn monad_context_for_child_of_block_number(
626        &self,
627        block_number: u64,
628    ) -> Result<MonadChainContext, BlockchainError> {
629        let block = self
630            .block_by_number_full(BlockNumber::Number(block_number))
631            .await?
632            .ok_or(BlockchainError::BlockNotFound)?;
633        let block_hash = block.header().hash;
634        self.monad_context_for_child_of_block(block, block_hash).await
635    }
636
637    /// Fetches the full blocks required to build context on top of `block_hash`.
638    pub(super) async fn monad_context_for_child_of_block_hash(
639        &self,
640        block_hash: B256,
641    ) -> Result<MonadChainContext, BlockchainError> {
642        let block =
643            self.block_by_hash_full(block_hash).await?.ok_or(BlockchainError::BlockNotFound)?;
644        self.monad_context_for_child_of_block(block, block_hash).await
645    }
646
647    fn monad_context_for_mined_transactions(
648        &self,
649        block: &Block,
650        current: &[TxEnv],
651        current_tx_index: usize,
652    ) -> Result<MonadChainContext, BlockchainError> {
653        let (_, grandparent_hash, parent) =
654            self.monad_block_participants(block.header.parent_hash)?;
655        let grandparent = if grandparent_hash.is_zero() {
656            MonadBlockParticipants::default()
657        } else {
658            self.monad_block_participants(grandparent_hash)?.2
659        };
660        Ok(monad_context_from_participants(grandparent, parent, current, current_tx_index))
661    }
662
663    fn monad_context_for_mined_block(
664        &self,
665        block: &Block,
666    ) -> Result<MonadChainContext, BlockchainError> {
667        let current = self.monad_tx_envs_from_storage(
668            &self.blockchain.storage.read(),
669            &block.body.transactions,
670        )?;
671        self.monad_context_for_mined_transactions(block, &current, 0)
672    }
673
674    pub(super) fn active_monad_context_for_mined_block(
675        &self,
676        block: &Block,
677    ) -> Result<Option<MonadChainContext>, BlockchainError> {
678        self.is_monad().then(|| self.monad_context_for_mined_block(block)).transpose()
679    }
680
681    fn monad_context_before_mined_transaction(
682        &self,
683        block: &Block,
684        current_tx_index: usize,
685    ) -> Result<MonadChainContext, BlockchainError> {
686        let current = self.monad_tx_envs_from_storage(
687            &self.blockchain.storage.read(),
688            &block.body.transactions,
689        )?;
690        if current_tx_index > current.len() {
691            return Err(BlockchainError::DataUnavailable);
692        }
693        self.monad_context_for_mined_transactions(
694            block,
695            &current[..current_tx_index],
696            current_tx_index,
697        )
698    }
699
700    pub(super) fn active_monad_context_before_mined_transaction(
701        &self,
702        block: &Block,
703        current_tx_index: usize,
704    ) -> Result<Option<MonadChainContext>, BlockchainError> {
705        self.is_monad()
706            .then(|| self.monad_context_before_mined_transaction(block, current_tx_index))
707            .transpose()
708    }
709
710    /// Builds the Monad [`EvmEnv`] (spec and gas params) from a base env.
711    pub(super) fn build_monad_evm_env(
712        evm_env: &EvmEnv,
713        hardfork: MonadHardfork,
714    ) -> EvmEnvFor<MonadEvmNetwork> {
715        EvmEnv::new(
716            evm_env.cfg_env.clone().with_spec_and_gas_params(hardfork, monad_gas_params(hardfork)),
717            evm_env.block_env.clone(),
718        )
719    }
720
721    /// Monad path of [`Backend::transact_call_with_inspector_ref`].
722    pub(super) fn transact_monad_with_inspector_ref<'db, I, DB>(
723        &self,
724        db: &'db DB,
725        evm_env: &EvmEnv,
726        inspector: &mut I,
727        tx_env: TxEnv,
728        execution: PreparedExecution,
729    ) -> Result<ResultAndState<HaltReason>, BlockchainError>
730    where
731        DB: DatabaseRef + ?Sized,
732        I: Inspector<MonadContext<WrapDatabaseRef<&'db DB>>>,
733        WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
734    {
735        let monad_env = Self::build_monad_evm_env(evm_env, execution.hardfork);
736        let factory = MonadEvmFactory::default();
737        let context =
738            execution.context.unwrap_or_else(|| MonadChainContext::for_transaction(&tx_env));
739        let mut evm = factory.create_evm_with_inspector(WrapDatabaseRef(db), monad_env, inspector);
740        evm.ctx_mut().chain = context;
741        self.inject_configured_precompiles(evm.precompiles_mut(), evm_env);
742        match execution.kind {
743            EnvelopeExecutionKind::Transaction => Ok(evm.transact(tx_env)?),
744            EnvelopeExecutionKind::Replay => {
745                if let Some(result) = factory.try_transact_system_replay(&mut evm, &tx_env)? {
746                    Ok(result)
747                } else {
748                    Ok(evm.transact(tx_env)?)
749                }
750            }
751        }
752    }
753}
754
755pub(super) fn store_block_metadata<N: Network>(
756    storage: &mut BlockchainStorage<N>,
757    block_hash: B256,
758    participants: MonadBlockParticipants,
759    execution_chain_id: u64,
760    hardfork: FoundryHardfork,
761) {
762    storage.monad_block_participants.insert(block_hash, participants);
763    storage.monad_block_replay_profiles.insert(
764        block_hash,
765        MonadBlockReplayProfile { execution_chain_id, hardfork: MonadHardfork::from(hardfork) },
766    );
767}