Skip to main content

anvil/eth/backend/mem/
mod.rs

1//! In-memory blockchain backend.
2use self::state::trie_storage;
3
4use crate::{
5    ForkChoice, NodeConfig, PrecompileFactory,
6    config::PruneStateHistoryConfig,
7    eth::{
8        backend::{
9            cheats::{CheatEcrecover, CheatsManager},
10            db::{AnvilCacheDB, Db, MaybeFullDatabase, SerializableState, StateDb},
11            executor::{
12                AnvilBlockExecutor, ExecutedPoolTransactions, PoolTxGasConfig,
13                execute_pool_transactions,
14            },
15            fork::ClientFork,
16            genesis::GenesisConfig,
17            mem::{
18                state::{storage_root, trie_accounts},
19                storage::MinedTransactionReceipt,
20            },
21            notifications::{NewBlockNotification, NewBlockNotifications},
22            tempo::AnvilStorageProvider,
23            time::{TimeManager, utc_from_secs},
24            validate::TransactionValidator,
25        },
26        error::{BlockchainError, ErrDetail, InvalidTransactionError},
27        fees::{FeeDetails, FeeManager, MIN_SUGGESTED_PRIORITY_FEE},
28        macros::node_info,
29        pool::transactions::PoolTransaction,
30        sign::build_impersonated,
31    },
32    mem::{
33        inspector::{AnvilInspector, InspectorTxConfig},
34        storage::{BlockchainStorage, InMemoryBlockStates, MinedBlockOutcome},
35    },
36};
37use alloy_chains::NamedChain;
38use alloy_consensus::{
39    Blob, BlockHeader, EnvKzgSettings, Header, Signed, Transaction as TransactionTrait,
40    TrieAccount, TxEnvelope, TxReceipt, Typed2718,
41    constants::EMPTY_WITHDRAWALS,
42    proofs::{calculate_receipt_root, calculate_transaction_root},
43    transaction::Recovered,
44};
45use alloy_eips::{
46    BlockNumHash, Encodable2718, eip2935, eip4844::kzg_to_versioned_hash,
47    eip7685::EMPTY_REQUESTS_HASH, eip7840::BlobParams, eip7910::SystemContract,
48};
49use alloy_evm::{
50    Database, EthEvmFactory, Evm, EvmEnv, EvmFactory, FromTxWithEncoded,
51    block::{BlockExecutionResult, BlockExecutor, StateDB},
52    eth::EthEvmContext,
53    overrides::{OverrideBlockHashes, apply_state_overrides},
54    precompiles::{DynPrecompile, Precompile, PrecompilesMap},
55};
56use alloy_network::{
57    AnyHeader, AnyRpcBlock, AnyRpcHeader, AnyRpcTransaction, AnyTxEnvelope, AnyTxType, Network,
58    NetworkTransactionBuilder, ReceiptResponse, UnknownTxEnvelope, UnknownTypedTransaction,
59};
60#[cfg(feature = "optimism")]
61use alloy_op_evm::{OpEvmContext, OpEvmFactory, OpTx};
62use alloy_primitives::{
63    Address, B256, Bloom, Bytes, TxHash, TxKind, U64, U256, hex, keccak256, logs_bloom,
64    map::{AddressMap, HashMap, HashSet},
65};
66use alloy_rpc_types::{
67    AccessList, Block as AlloyBlock, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions,
68    EIP1186AccountProofResponse as AccountProof, EIP1186StorageProof as StorageProof, Filter,
69    Header as AlloyHeader, Index, Log, Transaction, TransactionReceipt,
70    anvil::Forking,
71    request::TransactionRequest,
72    serde_helpers::JsonStorageKey,
73    simulate::{SimBlock, SimCallResult, SimulatePayload, SimulatedBlock},
74    state::EvmOverrides,
75    trace::{
76        filter::TraceFilter,
77        geth::{
78            FourByteFrame, GethDebugBuiltInTracerType, GethDebugTracerType,
79            GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, NoopFrame,
80            TraceResult,
81        },
82        parity::{LocalizedTransactionTrace, TraceResultsWithTransactionHash, TraceType},
83    },
84};
85use alloy_serde::{OtherFields, WithOtherFields};
86use alloy_trie::{HashBuilder, Nibbles, proof::ProofRetainer};
87use anvil_core::eth::{
88    block::{Block, BlockInfo, create_block},
89    transaction::{MaybeImpersonatedTransaction, PendingTransaction, TransactionInfo},
90};
91use anvil_rpc::error::RpcError;
92use chrono::Datelike;
93use eyre::{Context, Result};
94use flate2::{Compression, read::GzDecoder, write::GzEncoder};
95use foundry_evm::{
96    backend::{DatabaseError, DatabaseResult, RevertStateSnapshotAction},
97    constants::DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE,
98    core::precompiles::EC_RECOVER,
99    decode::RevertDecoder,
100    hardfork::FoundryHardfork,
101    inspectors::AccessListInspector,
102    traces::{
103        CallTraceDecoder, FourByteInspector, GethTraceBuilder, TracingInspector,
104        TracingInspectorConfig,
105    },
106    utils::{
107        block_env_from_header, get_blob_base_fee_update_fraction,
108        get_blob_base_fee_update_fraction_by_spec_id, get_blob_params_by_spec_id,
109    },
110};
111use foundry_evm_networks::NetworkConfigs;
112#[cfg(feature = "optimism")]
113use foundry_primitives::get_deposit_tx_parts;
114use foundry_primitives::{
115    FoundryNetwork, FoundryReceiptEnvelope, FoundryTransactionRequest, FoundryTxEnvelope,
116    FoundryTxReceipt,
117};
118use futures::channel::mpsc::{UnboundedSender, unbounded};
119#[cfg(feature = "optimism")]
120use op_alloy_consensus::{DEPOSIT_TX_TYPE_ID, OpTransaction as OpTransactionTrait};
121#[cfg(feature = "optimism")]
122use op_revm::{OpTransaction, transaction::deposit::DepositTransactionParts};
123
124/// Side-channel container for OP-specific deposit info produced by
125/// [`Backend::build_call_env`] and consumed by the OP transact path.
126///
127/// When the `optimism` feature is enabled, this is an alias for
128/// `op_revm::DepositTransactionParts`. When disabled, it is a zero-sized
129/// stand-in so the eth/tempo dispatch chain still type-checks.
130#[cfg(feature = "optimism")]
131type OpCallDepositInfo = DepositTransactionParts;
132#[cfg(not(feature = "optimism"))]
133#[derive(Default, Clone, Debug)]
134struct OpCallDepositInfo;
135
136/// Marker trait that abstracts over the per-network inspector trait bounds
137/// required by the in-memory backend. The OP bound is only included when the
138/// `optimism` feature is enabled.
139#[cfg(feature = "optimism")]
140pub trait BackendInspector<DB: Database>:
141    Inspector<EthEvmContext<DB>> + Inspector<OpEvmContext<DB>> + Inspector<TempoContext<DB>>
142{
143}
144#[cfg(feature = "optimism")]
145impl<DB: Database, T> BackendInspector<DB> for T where
146    T: Inspector<EthEvmContext<DB>> + Inspector<OpEvmContext<DB>> + Inspector<TempoContext<DB>>
147{
148}
149#[cfg(not(feature = "optimism"))]
150pub trait BackendInspector<DB: Database>:
151    Inspector<EthEvmContext<DB>> + Inspector<TempoContext<DB>>
152{
153}
154#[cfg(not(feature = "optimism"))]
155impl<DB: Database, T> BackendInspector<DB> for T where
156    T: Inspector<EthEvmContext<DB>> + Inspector<TempoContext<DB>>
157{
158}
159use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
160use revm::{
161    DatabaseCommit, Inspector,
162    context::{Block as RevmBlock, BlockEnv, Cfg, TxEnv},
163    context_interface::{
164        block::BlobExcessGasAndPrice,
165        result::{ExecutionResult, HaltReason, Output, ResultAndState},
166    },
167    database::{CacheDB, DbAccount, WrapDatabaseRef},
168    interpreter::InstructionResult,
169    precompile::{PrecompileSpecId, Precompiles},
170    primitives::{KECCAK_EMPTY, hardfork::SpecId},
171    state::AccountInfo,
172};
173use std::{
174    collections::BTreeMap,
175    fmt::{self, Debug},
176    io::{Read, Write},
177    ops::{Mul, Not},
178    path::PathBuf,
179    sync::Arc,
180    time::Duration,
181};
182use storage::{Blockchain, DEFAULT_HISTORY_LIMIT, MinedTransaction};
183use tempo_chainspec::hardfork::TempoHardfork;
184use tempo_evm::evm::TempoEvmFactory;
185use tempo_precompiles::{
186    storage::StorageCtx,
187    tip_fee_manager::{IFeeManager, TipFeeManager},
188    tip20::{ISSUER_ROLE, ITIP20, TIP20Token},
189};
190use tempo_primitives::TEMPO_TX_TYPE_ID;
191use tempo_revm::{
192    TempoBatchCallEnv, TempoBlockEnv, TempoHaltReason, TempoTxEnv, evm::TempoContext,
193    gas_params::tempo_gas_params,
194};
195use tokio::sync::RwLock as AsyncRwLock;
196
197pub mod cache;
198pub mod fork_db;
199pub mod in_memory_db;
200pub mod inspector;
201#[cfg(feature = "optimism")]
202pub mod optimism;
203pub mod state;
204pub mod storage;
205
206/// Helper trait that combines revm::DatabaseRef with Debug.
207/// This is needed because alloy-evm requires Debug on Database implementations.
208/// With trait upcasting now stable, we can now upcast from this trait to revm::DatabaseRef.
209pub trait DatabaseRef: revm::DatabaseRef<Error = DatabaseError> + Debug {}
210impl<T> DatabaseRef for T where T: revm::DatabaseRef<Error = DatabaseError> + Debug {}
211impl DatabaseRef for dyn crate::eth::backend::db::Db {}
212
213// Gas per transaction not creating a contract.
214pub const MIN_TRANSACTION_GAS: u128 = 21000;
215// Gas per transaction creating a contract.
216pub const MIN_CREATE_GAS: u128 = 53000;
217
218pub type State = foundry_evm::utils::StateChangeset;
219
220/// A block request, which includes the Pool Transactions if it's Pending
221pub enum BlockRequest<T> {
222    Pending(Vec<Arc<PoolTransaction<T>>>),
223    Number(u64),
224}
225
226impl<T> fmt::Debug for BlockRequest<T> {
227    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228        match self {
229            Self::Pending(txs) => f.debug_tuple("Pending").field(&txs.len()).finish(),
230            Self::Number(n) => f.debug_tuple("Number").field(n).finish(),
231        }
232    }
233}
234
235impl<T> BlockRequest<T> {
236    pub const fn block_number(&self) -> BlockNumber {
237        match *self {
238            Self::Pending(_) => BlockNumber::Pending,
239            Self::Number(n) => BlockNumber::Number(n),
240        }
241    }
242}
243
244/// Gives access to the [revm::Database]
245pub struct Backend<N: Network> {
246    /// Access to [`revm::Database`] abstraction.
247    ///
248    /// This will be used in combination with [`alloy_evm::Evm`] and is responsible for feeding
249    /// data to the evm during its execution.
250    ///
251    /// At time of writing, there are two different types of `Db`:
252    ///   - [`MemDb`](crate::mem::in_memory_db::MemDb): everything is stored in memory
253    ///   - [`ForkDb`](crate::mem::fork_db::ForkedDatabase): forks off a remote client, missing
254    ///     data is retrieved via RPC-calls
255    ///
256    /// In order to commit changes to the [`revm::Database`], the [`alloy_evm::Evm`] requires
257    /// mutable access, which requires a write-lock from this `db`. In forking mode, the time
258    /// during which the write-lock is active depends on whether the `ForkDb` can provide all
259    /// requested data from memory or whether it has to retrieve it via RPC calls first. This
260    /// means that it potentially blocks for some time, even taking into account the rate
261    /// limits of RPC endpoints. Therefore the `Db` is guarded by a `tokio::sync::RwLock` here
262    /// so calls that need to read from it, while it's currently written to, don't block. E.g.
263    /// a new block is currently mined and a new [`Self::set_storage_at()`] request is being
264    /// executed.
265    db: Arc<AsyncRwLock<Box<dyn Db>>>,
266    /// stores all block related data in memory.
267    blockchain: Blockchain<N>,
268    /// Historic states of previous blocks.
269    states: Arc<RwLock<InMemoryBlockStates>>,
270    /// EVM environment data of the chain (block env, cfg env).
271    evm_env: Arc<RwLock<EvmEnv>>,
272    /// Network configuration (optimism, custom precompiles, etc.)
273    networks: NetworkConfigs,
274    /// The active hardfork.
275    hardfork: FoundryHardfork,
276    /// This is set if this is currently forked off another client.
277    fork: Arc<RwLock<Option<ClientFork>>>,
278    /// Provides time related info, like timestamp.
279    time: TimeManager,
280    /// Contains state of custom overrides.
281    cheats: CheatsManager,
282    /// Contains fee data.
283    fees: FeeManager,
284    /// Initialised genesis.
285    genesis: GenesisConfig,
286    /// Listeners for new blocks that get notified when a new block was imported.
287    new_block_listeners: Arc<Mutex<Vec<UnboundedSender<NewBlockNotification>>>>,
288    /// Keeps track of active state snapshots at a specific block.
289    active_state_snapshots: Arc<Mutex<HashMap<U256, (u64, B256)>>>,
290    enable_steps_tracing: bool,
291    print_logs: bool,
292    print_traces: bool,
293    /// Recorder used for decoding traces, used together with print_traces
294    call_trace_decoder: Arc<CallTraceDecoder>,
295    /// How to keep history state
296    prune_state_history_config: PruneStateHistoryConfig,
297    /// max number of blocks with transactions in memory
298    transaction_block_keeper: Option<usize>,
299    pub(crate) node_config: Arc<AsyncRwLock<NodeConfig>>,
300    /// Slots in an epoch
301    slots_in_an_epoch: u64,
302    /// Precompiles to inject to the EVM.
303    precompile_factory: Option<Arc<dyn PrecompileFactory>>,
304    /// Prevent race conditions during mining
305    mining: Arc<tokio::sync::Mutex<()>>,
306    /// Disable pool balance checks
307    disable_pool_balance_checks: bool,
308}
309
310impl<N: Network> Clone for Backend<N> {
311    fn clone(&self) -> Self {
312        Self {
313            db: self.db.clone(),
314            blockchain: self.blockchain.clone(),
315            states: self.states.clone(),
316            evm_env: self.evm_env.clone(),
317            networks: self.networks,
318            hardfork: self.hardfork,
319            fork: self.fork.clone(),
320            time: self.time.clone(),
321            cheats: self.cheats.clone(),
322            fees: self.fees.clone(),
323            genesis: self.genesis.clone(),
324            new_block_listeners: self.new_block_listeners.clone(),
325            active_state_snapshots: self.active_state_snapshots.clone(),
326            enable_steps_tracing: self.enable_steps_tracing,
327            print_logs: self.print_logs,
328            print_traces: self.print_traces,
329            call_trace_decoder: self.call_trace_decoder.clone(),
330            prune_state_history_config: self.prune_state_history_config,
331            transaction_block_keeper: self.transaction_block_keeper,
332            node_config: self.node_config.clone(),
333            slots_in_an_epoch: self.slots_in_an_epoch,
334            precompile_factory: self.precompile_factory.clone(),
335            mining: self.mining.clone(),
336            disable_pool_balance_checks: self.disable_pool_balance_checks,
337        }
338    }
339}
340
341impl<N: Network> fmt::Debug for Backend<N> {
342    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
343        f.debug_struct("Backend").finish_non_exhaustive()
344    }
345}
346
347// Methods that are generic over any Network.
348impl<N: Network> Backend<N> {
349    /// Sets the account to impersonate
350    ///
351    /// Returns `true` if the account is already impersonated
352    pub fn impersonate(&self, addr: Address) -> bool {
353        if self.cheats.impersonated_accounts().contains(&addr) {
354            return true;
355        }
356        // Ensure EIP-3607 is disabled
357        self.evm_env.write().cfg_env.disable_eip3607 = true;
358        self.cheats.impersonate(addr)
359    }
360
361    /// Removes the account that from the impersonated set
362    ///
363    /// If the impersonated `addr` is a contract then we also reset the code here
364    pub fn stop_impersonating(&self, addr: Address) {
365        self.cheats.stop_impersonating(&addr);
366    }
367
368    /// If set to true will make every account impersonated
369    pub fn auto_impersonate_account(&self, enabled: bool) {
370        self.cheats.set_auto_impersonate_account(enabled);
371    }
372
373    /// Returns the configured fork, if any
374    pub fn get_fork(&self) -> Option<ClientFork> {
375        self.fork.read().clone()
376    }
377
378    /// Returns the database
379    pub fn get_db(&self) -> &Arc<AsyncRwLock<Box<dyn Db>>> {
380        &self.db
381    }
382
383    /// Returns the `AccountInfo` from the database
384    pub async fn get_account(&self, address: Address) -> DatabaseResult<AccountInfo> {
385        Ok(self.db.read().await.basic_ref(address)?.unwrap_or_default())
386    }
387
388    /// Whether we're forked off some remote client
389    pub fn is_fork(&self) -> bool {
390        self.fork.read().is_some()
391    }
392
393    /// Writes the CREATE2 deployer code directly to the database at the address provided.
394    pub async fn set_create2_deployer(&self, address: Address) -> DatabaseResult<()> {
395        self.set_code(address, Bytes::from_static(DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE)).await?;
396        Ok(())
397    }
398
399    /// Updates memory limits that should be more strict when auto-mine is enabled
400    pub(crate) fn update_interval_mine_block_time(&self, block_time: Duration) {
401        self.states.write().update_interval_mine_block_time(block_time)
402    }
403
404    /// Returns the `TimeManager` responsible for timestamps
405    pub const fn time(&self) -> &TimeManager {
406        &self.time
407    }
408
409    /// Returns the `CheatsManager` responsible for executing cheatcodes
410    pub const fn cheats(&self) -> &CheatsManager {
411        &self.cheats
412    }
413
414    /// Whether to skip blob validation
415    pub fn skip_blob_validation(&self, impersonator: Option<Address>) -> bool {
416        self.cheats().auto_impersonate_accounts()
417            || impersonator
418                .is_some_and(|addr| self.cheats().impersonated_accounts().contains(&addr))
419    }
420
421    /// Returns the `FeeManager` that manages fee/pricings
422    pub const fn fees(&self) -> &FeeManager {
423        &self.fees
424    }
425
426    /// The EVM environment data of the blockchain
427    pub const fn evm_env(&self) -> &Arc<RwLock<EvmEnv>> {
428        &self.evm_env
429    }
430
431    /// Returns the current best hash of the chain
432    pub fn best_hash(&self) -> B256 {
433        self.blockchain.storage.read().best_hash
434    }
435
436    /// Returns the current best number of the chain
437    pub fn best_number(&self) -> u64 {
438        self.blockchain.storage.read().best_number
439    }
440
441    /// Sets the block number
442    pub fn set_block_number(&self, number: u64) {
443        self.evm_env.write().block_env.number = U256::from(number);
444    }
445
446    /// Returns the client coinbase address.
447    pub fn coinbase(&self) -> Address {
448        self.evm_env.read().block_env.beneficiary
449    }
450
451    /// Returns the client coinbase address.
452    pub fn chain_id(&self) -> U256 {
453        U256::from(self.evm_env.read().cfg_env.chain_id)
454    }
455
456    pub fn set_chain_id(&self, chain_id: u64) {
457        self.evm_env.write().cfg_env.chain_id = chain_id;
458    }
459
460    /// Returns the genesis data for the Beacon API.
461    pub const fn genesis_time(&self) -> u64 {
462        self.genesis.timestamp
463    }
464
465    /// Returns the configured genesis block number.
466    pub const fn genesis_number(&self) -> u64 {
467        self.genesis.number
468    }
469
470    /// Returns balance of the given account.
471    pub async fn current_balance(&self, address: Address) -> DatabaseResult<U256> {
472        Ok(self.get_account(address).await?.balance)
473    }
474
475    /// Returns balance of the given account.
476    pub async fn current_nonce(&self, address: Address) -> DatabaseResult<u64> {
477        Ok(self.get_account(address).await?.nonce)
478    }
479
480    /// Sets the coinbase address
481    pub fn set_coinbase(&self, address: Address) {
482        self.evm_env.write().block_env.beneficiary = address;
483    }
484
485    /// Sets the nonce of the given address
486    pub async fn set_nonce(&self, address: Address, nonce: U256) -> DatabaseResult<()> {
487        self.db.write().await.set_nonce(address, nonce.try_into().unwrap_or(u64::MAX))
488    }
489
490    /// Sets the balance of the given address
491    pub async fn set_balance(&self, address: Address, balance: U256) -> DatabaseResult<()> {
492        self.db.write().await.set_balance(address, balance)
493    }
494
495    /// Sets the code of the given address
496    pub async fn set_code(&self, address: Address, code: Bytes) -> DatabaseResult<()> {
497        self.db.write().await.set_code(address, code)
498    }
499
500    /// Sets the value for the given slot of the given address
501    pub async fn set_storage_at(
502        &self,
503        address: Address,
504        slot: U256,
505        val: B256,
506    ) -> DatabaseResult<()> {
507        self.db.write().await.set_storage_at(address, slot.into(), val)
508    }
509
510    /// Returns the configured specid
511    pub fn spec_id(&self) -> SpecId {
512        *self.evm_env.read().spec_id()
513    }
514
515    /// Returns true for post London
516    pub fn is_eip1559(&self) -> bool {
517        (self.spec_id() as u8) >= (SpecId::LONDON as u8)
518    }
519
520    /// Returns true for post Merge
521    pub fn is_eip3675(&self) -> bool {
522        (self.spec_id() as u8) >= (SpecId::MERGE as u8)
523    }
524
525    /// Returns true for post Berlin
526    pub fn is_eip2930(&self) -> bool {
527        (self.spec_id() as u8) >= (SpecId::BERLIN as u8)
528    }
529
530    /// Returns true for post Cancun
531    pub fn is_eip4844(&self) -> bool {
532        (self.spec_id() as u8) >= (SpecId::CANCUN as u8)
533    }
534
535    /// Returns true for post Prague
536    pub fn is_eip7702(&self) -> bool {
537        (self.spec_id() as u8) >= (SpecId::PRAGUE as u8)
538    }
539
540    /// Returns true if op-stack deposits are active
541    #[cfg(feature = "optimism")]
542    pub const fn is_optimism(&self) -> bool {
543        self.networks.is_optimism()
544    }
545
546    /// Returns true if op-stack deposits are active.
547    ///
548    /// Always `false` when built without the `optimism` feature.
549    #[cfg(not(feature = "optimism"))]
550    pub const fn is_optimism(&self) -> bool {
551        false
552    }
553
554    /// Returns true if Tempo network mode is active
555    pub const fn is_tempo(&self) -> bool {
556        self.networks.is_tempo()
557    }
558
559    /// Returns the active hardfork.
560    pub fn hardfork(&self) -> FoundryHardfork {
561        if let Some(hardfork) =
562            self.fork.read().as_ref().and_then(|fork| fork.config.read().hardfork)
563        {
564            return hardfork;
565        }
566        self.hardfork
567    }
568
569    /// Returns the active Tempo hardfork.
570    pub fn tempo_hardfork(&self) -> TempoHardfork {
571        TempoHardfork::from(self.hardfork())
572    }
573
574    /// Returns the precompiles for the current spec.
575    pub fn precompiles(&self) -> BTreeMap<String, Address> {
576        let spec_id = self.spec_id();
577        let precompiles = Precompiles::new(PrecompileSpecId::from_spec_id(spec_id));
578
579        let mut precompiles_map = BTreeMap::<String, Address>::default();
580        for (address, precompile) in precompiles.inner() {
581            precompiles_map.insert(precompile.id().name().to_string(), *address);
582        }
583
584        // Extend with configured network precompiles.
585        precompiles_map.extend(self.networks.precompiles());
586
587        if let Some(factory) = &self.precompile_factory {
588            for (address, precompile) in factory.precompiles() {
589                precompiles_map.insert(precompile.precompile_id().to_string(), address);
590            }
591        }
592
593        precompiles_map
594    }
595
596    /// Returns the system contracts for the current spec.
597    pub fn system_contracts(&self) -> BTreeMap<SystemContract, Address> {
598        let mut system_contracts = BTreeMap::<SystemContract, Address>::default();
599
600        let spec_id = self.spec_id();
601
602        if spec_id >= SpecId::CANCUN {
603            system_contracts.extend(SystemContract::cancun());
604        }
605
606        if spec_id >= SpecId::PRAGUE {
607            system_contracts.extend(SystemContract::prague(None));
608        }
609
610        system_contracts
611    }
612
613    /// Returns [`BlobParams`] corresponding to the current spec.
614    pub fn blob_params(&self) -> BlobParams {
615        get_blob_params_by_spec_id(self.spec_id())
616    }
617
618    /// Returns an error if EIP1559 is not active (pre Berlin)
619    pub fn ensure_eip1559_active(&self) -> Result<(), BlockchainError> {
620        if self.is_eip1559() {
621            return Ok(());
622        }
623        Err(BlockchainError::EIP1559TransactionUnsupportedAtHardfork)
624    }
625
626    /// Returns an error if EIP1559 is not active (pre muirGlacier)
627    pub fn ensure_eip2930_active(&self) -> Result<(), BlockchainError> {
628        if self.is_eip2930() {
629            return Ok(());
630        }
631        Err(BlockchainError::EIP2930TransactionUnsupportedAtHardfork)
632    }
633
634    pub fn ensure_eip4844_active(&self) -> Result<(), BlockchainError> {
635        if self.is_eip4844() {
636            return Ok(());
637        }
638        Err(BlockchainError::EIP4844TransactionUnsupportedAtHardfork)
639    }
640
641    pub fn ensure_eip7702_active(&self) -> Result<(), BlockchainError> {
642        if self.is_eip7702() {
643            return Ok(());
644        }
645        Err(BlockchainError::EIP7702TransactionUnsupportedAtHardfork)
646    }
647
648    /// Returns an error if op-stack deposits are not active
649    #[cfg(feature = "optimism")]
650    pub const fn ensure_op_deposits_active(&self) -> Result<(), BlockchainError> {
651        if self.is_optimism() {
652            return Ok(());
653        }
654        Err(BlockchainError::DepositTransactionUnsupported)
655    }
656
657    /// Returns an error if Tempo transactions are not active
658    pub const fn ensure_tempo_active(&self) -> Result<(), BlockchainError> {
659        if self.is_tempo() {
660            return Ok(());
661        }
662        Err(BlockchainError::TempoTransactionUnsupported)
663    }
664
665    /// Builds the [`InspectorTxConfig`] from the backend's current settings.
666    fn inspector_tx_config(&self) -> InspectorTxConfig {
667        InspectorTxConfig {
668            print_traces: self.print_traces,
669            print_logs: self.print_logs,
670            enable_steps_tracing: self.enable_steps_tracing,
671            call_trace_decoder: self.call_trace_decoder.clone(),
672        }
673    }
674
675    /// Builds the [`PoolTxGasConfig`] from the given EVM environment.
676    fn pool_tx_gas_config(&self, evm_env: &EvmEnv) -> PoolTxGasConfig {
677        let spec_id = *evm_env.spec_id();
678        let is_cancun = spec_id >= SpecId::CANCUN;
679        let blob_params = self.blob_params();
680        PoolTxGasConfig {
681            disable_block_gas_limit: evm_env.cfg_env.disable_block_gas_limit,
682            tx_gas_limit_cap: evm_env.cfg_env.tx_gas_limit_cap,
683            tx_gas_limit_cap_resolved: evm_env.cfg_env.tx_gas_limit_cap(),
684            max_blob_gas_per_block: blob_params.max_blob_gas_per_block(),
685            is_cancun,
686        }
687    }
688
689    /// Returns the block gas limit
690    pub fn gas_limit(&self) -> u64 {
691        self.evm_env.read().block_env.gas_limit
692    }
693
694    /// Sets the block gas limit
695    pub fn set_gas_limit(&self, gas_limit: u64) {
696        self.evm_env.write().block_env.gas_limit = gas_limit;
697    }
698
699    /// Returns the current base fee
700    pub fn base_fee(&self) -> u64 {
701        self.fees.base_fee()
702    }
703
704    /// Returns whether the minimum suggested priority fee is enforced
705    pub const fn is_min_priority_fee_enforced(&self) -> bool {
706        self.fees.is_min_priority_fee_enforced()
707    }
708
709    pub fn excess_blob_gas_and_price(&self) -> Option<BlobExcessGasAndPrice> {
710        self.fees.excess_blob_gas_and_price()
711    }
712
713    /// Sets the current basefee
714    pub fn set_base_fee(&self, basefee: u64) {
715        self.fees.set_base_fee(basefee)
716    }
717
718    /// Sets the gas price
719    pub fn set_gas_price(&self, price: u128) {
720        self.fees.set_gas_price(price)
721    }
722
723    pub fn elasticity(&self) -> f64 {
724        self.fees.elasticity()
725    }
726
727    /// Returns the total difficulty of the chain until this block
728    ///
729    /// Note: this will always be `0` in memory mode
730    /// In forking mode this will always be the total difficulty of the forked block
731    pub fn total_difficulty(&self) -> U256 {
732        self.blockchain.storage.read().total_difficulty
733    }
734
735    /// Creates a new `evm_snapshot` at the current height.
736    ///
737    /// Returns the id of the snapshot created.
738    pub async fn create_state_snapshot(&self) -> U256 {
739        let num = self.best_number();
740        let hash = self.best_hash();
741        let id = self.db.write().await.snapshot_state();
742        trace!(target: "backend", "creating snapshot {} at {}", id, num);
743        self.active_state_snapshots.lock().insert(id, (num, hash));
744        id
745    }
746
747    pub fn list_state_snapshots(&self) -> BTreeMap<U256, (u64, B256)> {
748        self.active_state_snapshots.lock().clone().into_iter().collect()
749    }
750
751    /// Returns the environment for the next block
752    fn next_evm_env(&self) -> EvmEnv {
753        let mut evm_env = self.evm_env.read().clone();
754        // increase block number for this block
755        evm_env.block_env.number = evm_env.block_env.number.saturating_add(U256::from(1));
756        evm_env.block_env.basefee = self.base_fee();
757        evm_env.block_env.blob_excess_gas_and_price = self.excess_blob_gas_and_price();
758        evm_env.block_env.timestamp = U256::from(self.time.current_call_timestamp());
759        evm_env
760    }
761
762    /// Builds [`Inspector`] with the configured options.
763    fn build_inspector(&self) -> AnvilInspector {
764        let mut inspector = AnvilInspector::default();
765
766        if self.print_logs {
767            inspector = inspector.with_log_collector();
768        }
769        if self.print_traces {
770            inspector = inspector.with_trace_printer();
771        }
772
773        inspector
774    }
775
776    /// Builds an inspector configured for block mining (tracing always enabled).
777    fn build_mining_inspector(&self) -> AnvilInspector {
778        let mut inspector = AnvilInspector::default().with_tracing();
779        if self.enable_steps_tracing {
780            inspector = inspector.with_steps_tracing();
781        }
782        if self.print_logs {
783            inspector = inspector.with_log_collector();
784        }
785        if self.print_traces {
786            inspector = inspector.with_trace_printer();
787        }
788        inspector
789    }
790
791    /// Returns a new block event stream that yields Notifications when a new block was added
792    pub fn new_block_notifications(&self) -> NewBlockNotifications {
793        let (tx, rx) = unbounded();
794        self.new_block_listeners.lock().push(tx);
795        trace!(target: "backed", "added new block listener");
796        rx
797    }
798
799    /// Notifies all `new_block_listeners` about the new block
800    fn notify_on_new_block(&self, header: Header, hash: B256) {
801        // cleanup closed notification streams first, if the channel is closed we can remove the
802        // sender half for the set
803        self.new_block_listeners.lock().retain(|tx| !tx.is_closed());
804
805        let notification = NewBlockNotification { hash, header: Arc::new(header) };
806
807        self.new_block_listeners
808            .lock()
809            .retain(|tx| tx.unbounded_send(notification.clone()).is_ok());
810    }
811
812    /// Returns the block number for the given block id
813    pub fn convert_block_number(&self, block: Option<BlockNumber>) -> u64 {
814        let current = self.best_number();
815        match block.unwrap_or(BlockNumber::Latest) {
816            BlockNumber::Latest | BlockNumber::Pending => current,
817            BlockNumber::Earliest => 0,
818            BlockNumber::Number(num) => num,
819            BlockNumber::Safe => current.saturating_sub(self.slots_in_an_epoch),
820            BlockNumber::Finalized => current.saturating_sub(self.slots_in_an_epoch * 2),
821        }
822    }
823
824    /// Returns the block and its hash for the given id
825    fn get_block_with_hash(&self, id: impl Into<BlockId>) -> Option<(Block, B256)> {
826        let hash = self.blockchain.hash(id.into(), self.slots_in_an_epoch)?;
827        let block = self.get_block_by_hash(hash)?;
828        Some((block, hash))
829    }
830
831    pub fn get_block(&self, id: impl Into<BlockId>) -> Option<Block> {
832        self.get_block_with_hash(id).map(|(block, _)| block)
833    }
834
835    pub fn get_block_by_hash(&self, hash: B256) -> Option<Block> {
836        self.blockchain.get_block_by_hash(&hash)
837    }
838
839    /// Returns the traces for the given transaction
840    pub(crate) fn mined_parity_trace_transaction(
841        &self,
842        hash: B256,
843    ) -> Option<Vec<LocalizedTransactionTrace>> {
844        self.blockchain.storage.read().transactions.get(&hash).map(|tx| tx.parity_traces())
845    }
846
847    /// Returns the traces for the given block
848    pub(crate) fn mined_parity_trace_block(
849        &self,
850        block: u64,
851    ) -> Option<Vec<LocalizedTransactionTrace>> {
852        let block = self.get_block(block)?;
853        let mut traces = vec![];
854        let storage = self.blockchain.storage.read();
855        for tx in block.body.transactions {
856            if let Some(mined_tx) = storage.transactions.get(&tx.hash()) {
857                traces.extend(mined_tx.parity_traces());
858            }
859        }
860        Some(traces)
861    }
862
863    /// Returns the mined transaction for the given hash
864    pub(crate) fn mined_transaction(&self, hash: B256) -> Option<MinedTransaction<N>> {
865        self.blockchain.storage.read().transactions.get(&hash).cloned()
866    }
867
868    /// Overrides the given signature to impersonate the specified address during ecrecover.
869    pub async fn impersonate_signature(
870        &self,
871        signature: Bytes,
872        address: Address,
873    ) -> Result<(), BlockchainError> {
874        self.cheats.add_recover_override(signature, address);
875        Ok(())
876    }
877
878    /// Returns code by its hash
879    pub async fn debug_code_by_hash(
880        &self,
881        code_hash: B256,
882        block_id: Option<BlockId>,
883    ) -> Result<Option<Bytes>, BlockchainError> {
884        if let Ok(code) = self.db.read().await.code_by_hash_ref(code_hash) {
885            return Ok(Some(code.original_bytes()));
886        }
887        if let Some(fork) = self.get_fork() {
888            return Ok(fork.debug_code_by_hash(code_hash, block_id).await?);
889        }
890
891        Ok(None)
892    }
893
894    /// Returns the value associated with a key from the database
895    /// Currently only supports bytecode lookups.
896    ///
897    /// Based on Reth implementation: <https://github.com/paradigmxyz/reth/blob/66cfa9ed1a8c4bc2424aacf6fb2c1e67a78ee9a2/crates/rpc/rpc/src/debug.rs#L1146-L1178>
898    ///
899    /// Key should be: 0x63 (1-byte prefix) + 32 bytes (code_hash)
900    /// Total key length must be 33 bytes.
901    pub async fn debug_db_get(&self, key: String) -> Result<Option<Bytes>, BlockchainError> {
902        let key_bytes = if key.starts_with("0x") {
903            hex::decode(&key)
904                .map_err(|_| BlockchainError::Message("Invalid hex key".to_string()))?
905        } else {
906            key.into_bytes()
907        };
908
909        // Validate key length: must be 33 bytes (1 byte prefix + 32 bytes code hash)
910        if key_bytes.len() != 33 {
911            return Err(BlockchainError::Message(format!(
912                "Invalid key length: expected 33 bytes, got {}",
913                key_bytes.len()
914            )));
915        }
916
917        // Check for bytecode prefix (0x63 = 'c' in ASCII)
918        if key_bytes[0] != 0x63 {
919            return Err(BlockchainError::Message(
920                "Key prefix must be 0x63 for code hash lookups".to_string(),
921            ));
922        }
923
924        let code_hash = B256::from_slice(&key_bytes[1..33]);
925
926        // Use the existing debug_code_by_hash method to retrieve the bytecode
927        self.debug_code_by_hash(code_hash, None).await
928    }
929
930    fn mined_block_by_hash(&self, hash: B256) -> Option<AnyRpcBlock> {
931        let block = self.blockchain.get_block_by_hash(&hash)?;
932        Some(self.convert_block_with_hash(block, Some(hash)))
933    }
934
935    pub(crate) async fn mined_transactions_by_block_number(
936        &self,
937        number: BlockNumber,
938    ) -> Option<Vec<AnyRpcTransaction>> {
939        if let Some(block) = self.get_block(number) {
940            return self.mined_transactions_in_block(&block);
941        }
942        None
943    }
944
945    /// Returns all transactions given a block
946    pub(crate) fn mined_transactions_in_block(
947        &self,
948        block: &Block,
949    ) -> Option<Vec<AnyRpcTransaction>> {
950        let mut transactions = Vec::with_capacity(block.body.transactions.len());
951        let base_fee = block.header.base_fee_per_gas();
952        let storage = self.blockchain.storage.read();
953        for hash in block.body.transactions.iter().map(|tx| tx.hash()) {
954            let info = storage.transactions.get(&hash)?.info.clone();
955            let tx = block.body.transactions.get(info.transaction_index as usize)?.clone();
956
957            let tx = transaction_build(Some(hash), tx, Some(block), Some(info), base_fee);
958            transactions.push(tx);
959        }
960        Some(transactions)
961    }
962
963    pub fn mined_block_by_number(&self, number: BlockNumber) -> Option<AnyRpcBlock> {
964        let (block, hash) = self.get_block_with_hash(number)?;
965        let mut block = self.convert_block_with_hash(block, Some(hash));
966        block.transactions.convert_to_hashes();
967        Some(block)
968    }
969
970    pub fn get_full_block(&self, id: impl Into<BlockId>) -> Option<AnyRpcBlock> {
971        let (block, hash) = self.get_block_with_hash(id)?;
972        let transactions = self.mined_transactions_in_block(&block)?;
973        let mut block = self.convert_block_with_hash(block, Some(hash));
974        block.inner.transactions = BlockTransactions::Full(transactions);
975        Some(block)
976    }
977
978    /// Takes a block as it's stored internally and returns the eth api conform block format.
979    pub fn convert_block(&self, block: Block) -> AnyRpcBlock {
980        self.convert_block_with_hash(block, None)
981    }
982
983    /// Takes a block as it's stored internally and returns the eth api conform block format.
984    /// If `known_hash` is provided, it will be used instead of computing `hash_slow()`.
985    pub fn convert_block_with_hash(&self, block: Block, known_hash: Option<B256>) -> AnyRpcBlock {
986        let size = U256::from(alloy_rlp::encode(&block).len() as u32);
987
988        let header = block.header.clone();
989        let transactions = block.body.transactions;
990
991        let hash = known_hash.unwrap_or_else(|| header.hash_slow());
992        let Header { number, withdrawals_root, .. } = header;
993
994        let block = AlloyBlock {
995            header: AlloyHeader {
996                inner: AnyHeader::from(header),
997                hash,
998                total_difficulty: Some(self.total_difficulty()),
999                size: Some(size),
1000            },
1001            transactions: alloy_rpc_types::BlockTransactions::Hashes(
1002                transactions.into_iter().map(|tx| tx.hash()).collect(),
1003            ),
1004            uncles: vec![],
1005            withdrawals: withdrawals_root.map(|_| Default::default()),
1006        };
1007
1008        let mut block = WithOtherFields::new(block);
1009
1010        // If Arbitrum, apply chain specifics to converted block.
1011        if is_arbitrum(self.chain_id().to::<u64>()) {
1012            // Set `l1BlockNumber` field.
1013            block.other.insert("l1BlockNumber".to_string(), number.into());
1014        }
1015
1016        // Add Tempo-specific header fields for compatibility with TempoNetwork provider.
1017        if self.is_tempo() {
1018            let timestamp = block.header.timestamp();
1019            let gas_limit = block.header.gas_limit();
1020            block.other.insert(
1021                "timestampMillis".to_string(),
1022                serde_json::Value::String(format!("0x{:x}", timestamp.saturating_mul(1000))),
1023            );
1024            block.other.insert(
1025                "mainBlockGeneralGasLimit".to_string(),
1026                serde_json::Value::String(format!("0x{gas_limit:x}")),
1027            );
1028            block
1029                .other
1030                .insert("sharedGasLimit".to_string(), serde_json::Value::String("0x0".to_string()));
1031            block.other.insert(
1032                "timestampMillisPart".to_string(),
1033                serde_json::Value::String("0x0".to_string()),
1034            );
1035        }
1036
1037        AnyRpcBlock::from(block)
1038    }
1039
1040    pub async fn block_by_hash(&self, hash: B256) -> Result<Option<AnyRpcBlock>, BlockchainError> {
1041        trace!(target: "backend", "get block by hash {:?}", hash);
1042        if let tx @ Some(_) = self.mined_block_by_hash(hash) {
1043            return Ok(tx);
1044        }
1045
1046        if let Some(fork) = self.get_fork() {
1047            return Ok(fork.block_by_hash(hash).await?);
1048        }
1049
1050        Ok(None)
1051    }
1052
1053    pub async fn block_by_hash_full(
1054        &self,
1055        hash: B256,
1056    ) -> Result<Option<AnyRpcBlock>, BlockchainError> {
1057        trace!(target: "backend", "get block by hash {:?}", hash);
1058        if let tx @ Some(_) = self.get_full_block(hash) {
1059            return Ok(tx);
1060        }
1061
1062        if let Some(fork) = self.get_fork() {
1063            return Ok(fork.block_by_hash_full(hash).await?);
1064        }
1065
1066        Ok(None)
1067    }
1068
1069    pub async fn block_by_number(
1070        &self,
1071        number: BlockNumber,
1072    ) -> Result<Option<AnyRpcBlock>, BlockchainError> {
1073        trace!(target: "backend", "get block by number {:?}", number);
1074        if let tx @ Some(_) = self.mined_block_by_number(number) {
1075            return Ok(tx);
1076        }
1077
1078        if let Some(fork) = self.get_fork() {
1079            let number = self.convert_block_number(Some(number));
1080            if fork.predates_fork_inclusive(number) {
1081                return Ok(fork.block_by_number(number).await?);
1082            }
1083        }
1084
1085        Ok(None)
1086    }
1087
1088    pub async fn block_by_number_full(
1089        &self,
1090        number: BlockNumber,
1091    ) -> Result<Option<AnyRpcBlock>, BlockchainError> {
1092        trace!(target: "backend", "get block by number {:?}", number);
1093        if let tx @ Some(_) = self.get_full_block(number) {
1094            return Ok(tx);
1095        }
1096
1097        if let Some(fork) = self.get_fork() {
1098            let number = self.convert_block_number(Some(number));
1099            if fork.predates_fork_inclusive(number) {
1100                return Ok(fork.block_by_number_full(number).await?);
1101            }
1102        }
1103
1104        Ok(None)
1105    }
1106
1107    /// Converts the `BlockNumber` into a numeric value
1108    ///
1109    /// # Errors
1110    ///
1111    /// returns an error if the requested number is larger than the current height
1112    pub async fn ensure_block_number<T: Into<BlockId>>(
1113        &self,
1114        block_id: Option<T>,
1115    ) -> Result<u64, BlockchainError> {
1116        let current = self.best_number();
1117        let requested =
1118            match block_id.map(Into::into).unwrap_or(BlockId::Number(BlockNumber::Latest)) {
1119                BlockId::Hash(hash) => {
1120                    self.block_by_hash(hash.block_hash)
1121                        .await?
1122                        .ok_or(BlockchainError::BlockNotFound)?
1123                        .header
1124                        .number
1125                }
1126                BlockId::Number(num) => match num {
1127                    BlockNumber::Latest | BlockNumber::Pending => current,
1128                    BlockNumber::Earliest => U64::ZERO.to::<u64>(),
1129                    BlockNumber::Number(num) => num,
1130                    BlockNumber::Safe => current.saturating_sub(self.slots_in_an_epoch),
1131                    BlockNumber::Finalized => current.saturating_sub(self.slots_in_an_epoch * 2),
1132                },
1133            };
1134
1135        if requested > current {
1136            Err(BlockchainError::BlockOutOfRange(current, requested))
1137        } else {
1138            Ok(requested)
1139        }
1140    }
1141
1142    /// Injects all configured precompiles into the given precompile map.
1143    ///
1144    /// This applies three layers:
1145    /// 1. Network-specific precompiles (e.g. Tempo, OP)
1146    /// 2. User-provided precompiles via [`PrecompileFactory`]
1147    /// 3. Cheatcode ecrecover overrides (if active)
1148    fn inject_precompiles(&self, precompiles: &mut PrecompilesMap) {
1149        self.networks.inject_precompiles(precompiles);
1150
1151        if let Some(factory) = &self.precompile_factory {
1152            precompiles.extend_precompiles(factory.precompiles());
1153        }
1154
1155        let cheats = Arc::new(self.cheats.clone());
1156        if cheats.has_recover_overrides() {
1157            let cheat_ecrecover = CheatEcrecover::new(Arc::clone(&cheats));
1158            precompiles.apply_precompile(&EC_RECOVER, move |_| {
1159                Some(DynPrecompile::new_stateful(
1160                    cheat_ecrecover.precompile_id().clone(),
1161                    move |input| cheat_ecrecover.call(input),
1162                ))
1163            });
1164        }
1165    }
1166
1167    /// Creates a concrete EVM, injects precompiles, transacts, and returns the result mapped
1168    /// to [`HaltReason`] so all call sites share a single halt-reason type.
1169    fn transact_with_inspector_ref<'db, I, DB>(
1170        &self,
1171        db: &'db DB,
1172        evm_env: &EvmEnv,
1173        inspector: &mut I,
1174        tx_env: TxEnv,
1175        op_deposit: OpCallDepositInfo,
1176    ) -> Result<ResultAndState<HaltReason>, BlockchainError>
1177    where
1178        DB: DatabaseRef + ?Sized,
1179        I: BackendInspector<WrapDatabaseRef<&'db DB>>,
1180        WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
1181    {
1182        #[cfg(feature = "optimism")]
1183        if self.is_optimism() {
1184            let op_tx = OpTransaction { base: tx_env, deposit: op_deposit, ..Default::default() };
1185            return self.transact_op_with_inspector_ref(db, evm_env, inspector, op_tx);
1186        }
1187        // `op_deposit` only matters on the OP path; eth/tempo ignore it.
1188        let _ = op_deposit;
1189        if self.is_tempo() {
1190            self.transact_tempo_with_inspector_ref(db, evm_env, inspector, TempoTxEnv::from(tx_env))
1191        } else {
1192            self.transact_eth_with_inspector_ref(db, evm_env, inspector, tx_env)
1193        }
1194    }
1195
1196    /// Eth path of [`Backend::transact_with_inspector_ref`].
1197    ///
1198    /// Creates an Ethereum EVM, injects precompiles, and transacts with a
1199    /// plain [`TxEnv`].
1200    fn transact_eth_with_inspector_ref<'db, I, DB>(
1201        &self,
1202        db: &'db DB,
1203        evm_env: &EvmEnv,
1204        inspector: &mut I,
1205        tx_env: TxEnv,
1206    ) -> Result<ResultAndState<HaltReason>, BlockchainError>
1207    where
1208        DB: DatabaseRef + ?Sized,
1209        I: Inspector<EthEvmContext<WrapDatabaseRef<&'db DB>>>,
1210        WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
1211    {
1212        let mut evm = EthEvmFactory::default().create_evm_with_inspector(
1213            WrapDatabaseRef(db),
1214            evm_env.clone(),
1215            inspector,
1216        );
1217        self.inject_precompiles(evm.precompiles_mut());
1218        Ok(evm.transact(tx_env)?)
1219    }
1220
1221    /// Builds the appropriate tx env from a [`FoundryTxEnvelope`], executes via the correct
1222    /// EVM backend (Op/Tempo/Eth), and returns both the result and the base [`TxEnv`].
1223    fn transact_envelope_with_inspector_ref<'db, I, DB>(
1224        &self,
1225        db: &'db DB,
1226        evm_env: &EvmEnv,
1227        inspector: &mut I,
1228        tx: &FoundryTxEnvelope,
1229        sender: Address,
1230    ) -> Result<(ResultAndState<HaltReason>, TxEnv), BlockchainError>
1231    where
1232        DB: DatabaseRef + ?Sized,
1233        I: BackendInspector<WrapDatabaseRef<&'db DB>>,
1234        WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
1235    {
1236        if tx.is_tempo() {
1237            let tx_env: TempoTxEnv =
1238                FromTxWithEncoded::from_encoded_tx(tx, sender, tx.encoded_2718().into());
1239            let base = tx_env.inner.clone();
1240            let result = self.transact_tempo_with_inspector_ref(db, evm_env, inspector, tx_env)?;
1241            return Ok((result, base));
1242        }
1243        #[cfg(feature = "optimism")]
1244        if self.is_optimism() {
1245            let op_tx: OpTransaction<TxEnv> =
1246                FromTxWithEncoded::from_encoded_tx(tx, sender, tx.encoded_2718().into());
1247            let base = op_tx.base.clone();
1248            let result = self.transact_op_with_inspector_ref(db, evm_env, inspector, op_tx)?;
1249            return Ok((result, base));
1250        }
1251        let tx_env: TxEnv =
1252            FromTxWithEncoded::from_encoded_tx(tx, sender, tx.encoded_2718().into());
1253        let base = tx_env.clone();
1254        let result = self.transact_eth_with_inspector_ref(db, evm_env, inspector, tx_env)?;
1255        Ok((result, base))
1256    }
1257
1258    /// Creates a Tempo EVM, injects precompiles, and transacts with a native [`TempoTxEnv`].
1259    fn transact_tempo_with_inspector_ref<'db, I, DB>(
1260        &self,
1261        db: &'db DB,
1262        evm_env: &EvmEnv,
1263        inspector: &mut I,
1264        tx_env: TempoTxEnv,
1265    ) -> Result<ResultAndState<HaltReason>, BlockchainError>
1266    where
1267        DB: DatabaseRef + ?Sized,
1268        I: Inspector<TempoContext<WrapDatabaseRef<&'db DB>>>,
1269        WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
1270    {
1271        let hardfork = self.tempo_hardfork();
1272        let tempo_env = EvmEnv::new(
1273            evm_env.cfg_env.clone().with_spec_and_gas_params(hardfork, tempo_gas_params(hardfork)),
1274            TempoBlockEnv { inner: evm_env.block_env.clone(), timestamp_millis_part: 0 },
1275        );
1276        let mut evm = TempoEvmFactory::default().create_evm_with_inspector(
1277            WrapDatabaseRef(db),
1278            tempo_env,
1279            inspector,
1280        );
1281        self.inject_precompiles(evm.precompiles_mut());
1282        let result = evm.transact(tx_env)?;
1283        Ok(ResultAndState {
1284            result: result.result.map_haltreason(|h| match h {
1285                TempoHaltReason::Ethereum(eth) => eth,
1286                _ => HaltReason::PrecompileError,
1287            }),
1288            state: result.state,
1289        })
1290    }
1291
1292    /// Creates a concrete EVM + [`AnvilBlockExecutor`], runs pre-execution changes, and
1293    /// executes pool transactions. Returns the execution results and drops the EVM.
1294    #[allow(clippy::too_many_arguments, clippy::type_complexity)]
1295    fn execute_with_block_executor<DB>(
1296        &self,
1297        db: DB,
1298        evm_env: &EvmEnv,
1299        parent_hash: B256,
1300        spec_id: SpecId,
1301        pool_transactions: &[Arc<PoolTransaction<FoundryTxEnvelope>>],
1302        gas_config: &PoolTxGasConfig,
1303        inspector_tx_config: &InspectorTxConfig,
1304        validator: &dyn Fn(
1305            &PendingTransaction<FoundryTxEnvelope>,
1306            &AccountInfo,
1307        ) -> Result<(), InvalidTransactionError>,
1308    ) -> (ExecutedPoolTransactions<FoundryTxEnvelope>, BlockExecutionResult<FoundryReceiptEnvelope>)
1309    where
1310        DB: StateDB<Error = DatabaseError>,
1311    {
1312        let inspector = self.build_mining_inspector();
1313
1314        macro_rules! run {
1315            ($evm:expr) => {{
1316                self.inject_precompiles($evm.precompiles_mut());
1317                let mut executor = AnvilBlockExecutor::new($evm, parent_hash, spec_id);
1318                executor.apply_pre_execution_changes().expect("pre-execution changes failed");
1319                let pool_result = execute_pool_transactions(
1320                    &mut executor,
1321                    pool_transactions,
1322                    gas_config,
1323                    inspector_tx_config,
1324                    self.cheats(),
1325                    validator,
1326                );
1327                let (evm, block_result) = executor.finish().expect("executor finish failed");
1328                drop(evm);
1329                (pool_result, block_result)
1330            }};
1331        }
1332
1333        #[cfg(feature = "optimism")]
1334        if self.is_optimism() {
1335            let op_env = EvmEnv::new(
1336                evm_env.cfg_env.clone().with_spec_and_mainnet_gas_params(self.hardfork.into()),
1337                evm_env.block_env.clone(),
1338            );
1339            let mut evm =
1340                OpEvmFactory::<OpTx>::default().create_evm_with_inspector(db, op_env, inspector);
1341            return run!(evm);
1342        }
1343
1344        if self.is_tempo() {
1345            let hardfork = self.tempo_hardfork();
1346            let tempo_env = EvmEnv::new(
1347                evm_env
1348                    .cfg_env
1349                    .clone()
1350                    .with_spec_and_gas_params(hardfork, tempo_gas_params(hardfork)),
1351                TempoBlockEnv { inner: evm_env.block_env.clone(), timestamp_millis_part: 0 },
1352            );
1353            let mut evm =
1354                TempoEvmFactory::default().create_evm_with_inspector(db, tempo_env, inspector);
1355            run!(evm)
1356        } else {
1357            let mut evm =
1358                EthEvmFactory::default().create_evm_with_inspector(db, evm_env.clone(), inspector);
1359            run!(evm)
1360        }
1361    }
1362
1363    /// ## EVM settings
1364    ///
1365    /// This modifies certain EVM settings to mirror geth's `SkipAccountChecks` when transacting requests, see also: <https://github.com/ethereum/go-ethereum/blob/380688c636a654becc8f114438c2a5d93d2db032/core/state_transition.go#L145-L148>:
1366    ///
1367    ///  - `disable_eip3607` is set to `true`
1368    ///  - `disable_base_fee` is set to `true`
1369    ///  - `tx_gas_limit_cap` is set to `Some(u64::MAX)` indicating no gas limit cap
1370    ///  - `nonce` check is skipped
1371    fn build_call_env(
1372        &self,
1373        request: WithOtherFields<TransactionRequest>,
1374        fee_details: FeeDetails,
1375        block_env: BlockEnv,
1376    ) -> (EvmEnv, TxEnv, OpCallDepositInfo) {
1377        let tx_type = request.minimal_tx_type() as u8;
1378
1379        let WithOtherFields::<TransactionRequest> {
1380            inner:
1381                TransactionRequest {
1382                    from,
1383                    to,
1384                    gas,
1385                    value,
1386                    input,
1387                    access_list,
1388                    blob_versioned_hashes,
1389                    authorization_list,
1390                    nonce,
1391                    sidecar: _,
1392                    chain_id,
1393                    .. // Rest of the gas fees related fields are taken from `fee_details`
1394                },
1395            other,
1396        } = request;
1397
1398        let FeeDetails {
1399            gas_price,
1400            max_fee_per_gas,
1401            max_priority_fee_per_gas,
1402            max_fee_per_blob_gas,
1403        } = fee_details;
1404
1405        let gas_limit = gas.unwrap_or(block_env.gas_limit);
1406        let mut evm_env = self.evm_env.read().clone();
1407        evm_env.block_env = block_env;
1408        // we want to disable this in eth_call, since this is common practice used by other node
1409        // impls and providers <https://github.com/foundry-rs/foundry/issues/4388>
1410        evm_env.cfg_env.disable_block_gas_limit = true;
1411        evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX);
1412
1413        // The basefee should be ignored for calls against state for
1414        // - eth_call
1415        // - eth_estimateGas
1416        // - eth_createAccessList
1417        // - tracing
1418        evm_env.cfg_env.disable_base_fee = true;
1419
1420        // Disable nonce check in revm
1421        evm_env.cfg_env.disable_nonce_check = true;
1422
1423        let gas_price = gas_price.or(max_fee_per_gas).unwrap_or_else(|| {
1424            self.fees().raw_gas_price().saturating_add(MIN_SUGGESTED_PRIORITY_FEE)
1425        });
1426        let caller = from.unwrap_or_default();
1427        let to = to.as_ref().and_then(TxKind::to);
1428        let blob_hashes = blob_versioned_hashes.unwrap_or_default();
1429        let mut tx_env = TxEnv {
1430            caller,
1431            gas_limit,
1432            gas_price,
1433            gas_priority_fee: max_priority_fee_per_gas,
1434            max_fee_per_blob_gas: max_fee_per_blob_gas
1435                .or_else(|| {
1436                    if blob_hashes.is_empty() { Some(0) } else { evm_env.block_env.blob_gasprice() }
1437                })
1438                .unwrap_or_default(),
1439            kind: match to {
1440                Some(addr) => TxKind::Call(*addr),
1441                None => TxKind::Create,
1442            },
1443            tx_type,
1444            value: value.unwrap_or_default(),
1445            data: input.into_input().unwrap_or_default(),
1446            chain_id: Some(chain_id.unwrap_or(self.chain_id().to::<u64>())),
1447            access_list: access_list.unwrap_or_default(),
1448            blob_hashes,
1449            ..Default::default()
1450        };
1451        tx_env.set_signed_authorization(authorization_list.unwrap_or_default());
1452
1453        if let Some(nonce) = nonce {
1454            tx_env.nonce = nonce;
1455        }
1456
1457        if evm_env.block_env.basefee == 0 {
1458            // this is an edge case because the evm fails if `tx.effective_gas_price < base_fee`
1459            // 0 is only possible if it's manually set
1460            evm_env.cfg_env.disable_base_fee = true;
1461        }
1462
1463        // Deposit transaction? (only valid when op-stack deposits are active)
1464        #[cfg(feature = "optimism")]
1465        let op_deposit = if self.ensure_op_deposits_active().is_ok()
1466            && let Ok(deposit) = get_deposit_tx_parts(&other)
1467        {
1468            deposit
1469        } else {
1470            OpCallDepositInfo::default()
1471        };
1472        #[cfg(not(feature = "optimism"))]
1473        let op_deposit = {
1474            // `other` carries OP-only deposit fields; consumed only when feature is enabled.
1475            let _ = &other;
1476            OpCallDepositInfo
1477        };
1478
1479        (evm_env, tx_env, op_deposit)
1480    }
1481
1482    pub fn call_with_state(
1483        &self,
1484        state: &dyn DatabaseRef,
1485        request: WithOtherFields<TransactionRequest>,
1486        fee_details: FeeDetails,
1487        block_env: BlockEnv,
1488    ) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
1489        let mut inspector = self.build_inspector();
1490
1491        // Extract Tempo-specific fields before `build_call_env` consumes `other`.
1492        let tempo_overrides = self.is_tempo().then(|| {
1493            let fee_token =
1494                request.other.get_deserialized::<Address>("feeToken").and_then(|r| r.ok());
1495            let nonce_key = request
1496                .other
1497                .get_deserialized::<U256>("nonceKey")
1498                .and_then(|r| r.ok())
1499                .unwrap_or_default();
1500            let valid_before = request
1501                .other
1502                .get_deserialized::<U256>("validBefore")
1503                .and_then(|r| r.ok())
1504                .map(|v| v.saturating_to::<u64>());
1505            let valid_after = request
1506                .other
1507                .get_deserialized::<U256>("validAfter")
1508                .and_then(|r| r.ok())
1509                .map(|v| v.saturating_to::<u64>());
1510            (fee_token, nonce_key, valid_before, valid_after)
1511        });
1512
1513        let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block_env);
1514
1515        let ResultAndState { result, state } =
1516            if let Some((fee_token, nonce_key, valid_before, valid_after)) = tempo_overrides {
1517                use tempo_primitives::transaction::Call;
1518
1519                let base = tx_env;
1520                let mut tempo_tx = TempoTxEnv::from(base.clone());
1521                tempo_tx.fee_token = fee_token;
1522
1523                if !nonce_key.is_zero() || valid_before.is_some() || valid_after.is_some() {
1524                    // For gas estimation we don't have a signed tx, so generate a
1525                    // unique hash for expiring-nonce replay protection.  The nonce
1526                    // manager needs a non-zero hash; the actual value doesn't matter
1527                    // because the state is discarded after estimation.
1528                    let estimation_hash = keccak256(base.data.as_ref());
1529                    // T1B+ uses `TempoTxEnv::unique_tx_identifier` (sender-scoped) as
1530                    // the expiring-nonce replay hash; pre-T1B uses `tx_hash`.
1531                    // Set both so the synthetic env works across hardforks.
1532                    tempo_tx.unique_tx_identifier = Some(estimation_hash);
1533                    tempo_tx.tempo_tx_env = Some(Box::new(TempoBatchCallEnv {
1534                        nonce_key,
1535                        valid_before,
1536                        valid_after,
1537                        aa_calls: vec![Call { to: base.kind, value: base.value, input: base.data }],
1538                        tx_hash: estimation_hash,
1539                        expiring_nonce_idx: Some(0),
1540                        ..Default::default()
1541                    }));
1542                }
1543                self.transact_tempo_with_inspector_ref(state, &evm_env, &mut inspector, tempo_tx)?
1544            } else {
1545                self.transact_with_inspector_ref(
1546                    state,
1547                    &evm_env,
1548                    &mut inspector,
1549                    tx_env,
1550                    op_deposit,
1551                )?
1552            };
1553
1554        let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result);
1555        inspector.print_logs();
1556
1557        if self.print_traces {
1558            inspector.into_print_traces(self.call_trace_decoder.clone());
1559        }
1560
1561        Ok((exit_reason, out, gas_used as u128, state))
1562    }
1563
1564    pub fn build_access_list_with_state(
1565        &self,
1566        state: &dyn DatabaseRef,
1567        request: WithOtherFields<TransactionRequest>,
1568        fee_details: FeeDetails,
1569        block_env: BlockEnv,
1570    ) -> Result<(InstructionResult, Option<Output>, u64, AccessList), BlockchainError> {
1571        let mut inspector =
1572            AccessListInspector::new(request.access_list.clone().unwrap_or_default());
1573
1574        let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block_env);
1575        let ResultAndState { result, state: _ } =
1576            self.transact_with_inspector_ref(state, &evm_env, &mut inspector, tx_env, op_deposit)?;
1577        let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result);
1578        let access_list = inspector.access_list();
1579        Ok((exit_reason, out, gas_used, access_list))
1580    }
1581
1582    pub fn get_code_with_state(
1583        &self,
1584        state: &dyn DatabaseRef,
1585        address: Address,
1586    ) -> Result<Bytes, BlockchainError> {
1587        trace!(target: "backend", "get code for {:?}", address);
1588        let account = state.basic_ref(address)?.unwrap_or_default();
1589        if account.code_hash == KECCAK_EMPTY {
1590            // if the code hash is `KECCAK_EMPTY`, we check no further
1591            return Ok(Default::default());
1592        }
1593        let code = if let Some(code) = account.code {
1594            code
1595        } else {
1596            state.code_by_hash_ref(account.code_hash)?
1597        };
1598        Ok(code.bytes()[..code.len()].to_vec().into())
1599    }
1600
1601    pub fn get_balance_with_state<D>(
1602        &self,
1603        state: D,
1604        address: Address,
1605    ) -> Result<U256, BlockchainError>
1606    where
1607        D: DatabaseRef,
1608    {
1609        trace!(target: "backend", "get balance for {:?}", address);
1610        Ok(state.basic_ref(address)?.unwrap_or_default().balance)
1611    }
1612
1613    pub async fn transaction_by_block_number_and_index(
1614        &self,
1615        number: BlockNumber,
1616        index: Index,
1617    ) -> Result<Option<AnyRpcTransaction>, BlockchainError> {
1618        if let Some(block) = self.mined_block_by_number(number) {
1619            return Ok(self.mined_transaction_by_block_hash_and_index(block.header.hash, index));
1620        }
1621
1622        if let Some(fork) = self.get_fork() {
1623            let number = self.convert_block_number(Some(number));
1624            if fork.predates_fork(number) {
1625                return Ok(fork
1626                    .transaction_by_block_number_and_index(number, index.into())
1627                    .await?);
1628            }
1629        }
1630
1631        Ok(None)
1632    }
1633
1634    pub async fn transaction_by_block_hash_and_index(
1635        &self,
1636        hash: B256,
1637        index: Index,
1638    ) -> Result<Option<AnyRpcTransaction>, BlockchainError> {
1639        if let tx @ Some(_) = self.mined_transaction_by_block_hash_and_index(hash, index) {
1640            return Ok(tx);
1641        }
1642
1643        if let Some(fork) = self.get_fork() {
1644            return Ok(fork.transaction_by_block_hash_and_index(hash, index.into()).await?);
1645        }
1646
1647        Ok(None)
1648    }
1649
1650    pub fn mined_transaction_by_block_hash_and_index(
1651        &self,
1652        block_hash: B256,
1653        index: Index,
1654    ) -> Option<AnyRpcTransaction> {
1655        let (info, block, tx) = {
1656            let storage = self.blockchain.storage.read();
1657            let block = storage.blocks.get(&block_hash).cloned()?;
1658            let index: usize = index.into();
1659            let tx = block.body.transactions.get(index)?.clone();
1660            let info = storage.transactions.get(&tx.hash())?.info.clone();
1661            (info, block, tx)
1662        };
1663
1664        Some(transaction_build(
1665            Some(info.transaction_hash),
1666            tx,
1667            Some(&block),
1668            Some(info),
1669            block.header.base_fee_per_gas(),
1670        ))
1671    }
1672
1673    pub async fn transaction_by_hash(
1674        &self,
1675        hash: B256,
1676    ) -> Result<Option<AnyRpcTransaction>, BlockchainError> {
1677        trace!(target: "backend", "transaction_by_hash={:?}", hash);
1678        if let tx @ Some(_) = self.mined_transaction_by_hash(hash) {
1679            return Ok(tx);
1680        }
1681
1682        if let Some(fork) = self.get_fork() {
1683            return fork
1684                .transaction_by_hash(hash)
1685                .await
1686                .map_err(BlockchainError::AlloyForkProvider);
1687        }
1688
1689        Ok(None)
1690    }
1691
1692    pub fn mined_transaction_by_hash(&self, hash: B256) -> Option<AnyRpcTransaction> {
1693        let (info, block) = {
1694            let storage = self.blockchain.storage.read();
1695            let MinedTransaction { info, block_hash, .. } =
1696                storage.transactions.get(&hash)?.clone();
1697            let block = storage.blocks.get(&block_hash).cloned()?;
1698            (info, block)
1699        };
1700        let tx = block.body.transactions.get(info.transaction_index as usize)?.clone();
1701
1702        Some(transaction_build(
1703            Some(info.transaction_hash),
1704            tx,
1705            Some(&block),
1706            Some(info),
1707            block.header.base_fee_per_gas(),
1708        ))
1709    }
1710
1711    /// Returns the traces for the given transaction
1712    pub async fn trace_transaction(
1713        &self,
1714        hash: B256,
1715    ) -> Result<Vec<LocalizedTransactionTrace>, BlockchainError> {
1716        if let Some(traces) = self.mined_parity_trace_transaction(hash) {
1717            return Ok(traces);
1718        }
1719
1720        if let Some(fork) = self.get_fork() {
1721            return Ok(fork.trace_transaction(hash).await?);
1722        }
1723
1724        Ok(vec![])
1725    }
1726
1727    /// Returns the traces for the given block
1728    pub async fn trace_block(
1729        &self,
1730        block: BlockNumber,
1731    ) -> Result<Vec<LocalizedTransactionTrace>, BlockchainError> {
1732        let number = self.convert_block_number(Some(block));
1733        if let Some(traces) = self.mined_parity_trace_block(number) {
1734            return Ok(traces);
1735        }
1736
1737        if let Some(fork) = self.get_fork()
1738            && fork.predates_fork(number)
1739        {
1740            return Ok(fork.trace_block(number).await?);
1741        }
1742
1743        Ok(vec![])
1744    }
1745
1746    /// Replays all transactions in a block and returns the requested traces for each transaction
1747    pub async fn trace_replay_block_transactions(
1748        &self,
1749        block: BlockNumber,
1750        trace_types: HashSet<TraceType>,
1751    ) -> Result<Vec<TraceResultsWithTransactionHash>, BlockchainError> {
1752        let block_number = self.convert_block_number(Some(block));
1753
1754        // Try mined blocks first
1755        if let Some(results) =
1756            self.mined_parity_trace_replay_block_transactions(block_number, &trace_types)
1757        {
1758            return Ok(results);
1759        }
1760
1761        // Fallback to fork if block predates fork
1762        if let Some(fork) = self.get_fork()
1763            && fork.predates_fork(block_number)
1764        {
1765            return Ok(fork.trace_replay_block_transactions(block_number, trace_types).await?);
1766        }
1767
1768        Ok(vec![])
1769    }
1770
1771    /// Returns the trace results for all transactions in a mined block by replaying them
1772    fn mined_parity_trace_replay_block_transactions(
1773        &self,
1774        block_number: u64,
1775        trace_types: &HashSet<TraceType>,
1776    ) -> Option<Vec<TraceResultsWithTransactionHash>> {
1777        let block = self.get_block(block_number)?;
1778
1779        // Execute this in the context of the parent state
1780        let parent_hash = block.header.parent_hash;
1781        let trace_config = TracingInspectorConfig::from_parity_config(trace_types);
1782
1783        let read_guard = self.states.upgradable_read();
1784        if let Some(state) = read_guard.get_state(&parent_hash) {
1785            self.replay_block_transactions_with_inspector(&block, state, trace_config, trace_types)
1786        } else {
1787            let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
1788            let state = write_guard.get_on_disk_state(&parent_hash)?;
1789            self.replay_block_transactions_with_inspector(&block, state, trace_config, trace_types)
1790        }
1791    }
1792
1793    /// Replays all transactions in a block with the tracing inspector to generate TraceResults
1794    fn replay_block_transactions_with_inspector(
1795        &self,
1796        block: &Block,
1797        parent_state: &StateDb,
1798        trace_config: TracingInspectorConfig,
1799        trace_types: &HashSet<TraceType>,
1800    ) -> Option<Vec<TraceResultsWithTransactionHash>> {
1801        let mut cache_db = CacheDB::new(Box::new(parent_state));
1802        let mut results = Vec::new();
1803
1804        // Configure the block environment
1805        let mut evm_env = self.evm_env.read().clone();
1806        evm_env.block_env = block_env_from_header(&block.header);
1807
1808        // Execute each transaction in the block with tracing
1809        for tx_envelope in &block.body.transactions {
1810            let tx_hash = tx_envelope.hash();
1811
1812            // Create a fresh inspector for this transaction
1813            let mut inspector = TracingInspector::new(trace_config);
1814
1815            // Prepare transaction environment and execute
1816            let pending_tx =
1817                PendingTransaction::from_maybe_impersonated(tx_envelope.clone()).ok()?;
1818            let (result, _) = self
1819                .transact_envelope_with_inspector_ref(
1820                    &cache_db,
1821                    &evm_env,
1822                    &mut inspector,
1823                    pending_tx.transaction.as_ref(),
1824                    *pending_tx.sender(),
1825                )
1826                .ok()?;
1827
1828            // Build TraceResults from the inspector and execution result
1829            let full_trace = inspector
1830                .into_parity_builder()
1831                .into_trace_results_with_state(&result, trace_types, &cache_db)
1832                .ok()?;
1833
1834            results.push(TraceResultsWithTransactionHash { transaction_hash: tx_hash, full_trace });
1835
1836            // Commit the state changes for the next transaction
1837            cache_db.commit(result.state);
1838        }
1839
1840        Some(results)
1841    }
1842
1843    // Returns the traces matching a given filter
1844    pub async fn trace_filter(
1845        &self,
1846        filter: TraceFilter,
1847    ) -> Result<Vec<LocalizedTransactionTrace>, BlockchainError> {
1848        let matcher = filter.matcher();
1849        let start = filter.from_block.unwrap_or(0);
1850        let end = filter.to_block.unwrap_or_else(|| self.best_number());
1851
1852        if start > end {
1853            return Err(BlockchainError::RpcError(RpcError::invalid_params(
1854                "invalid block range, ensure that to block is greater than from block".to_string(),
1855            )));
1856        }
1857
1858        let dist = end - start;
1859        if dist > 300 {
1860            return Err(BlockchainError::RpcError(RpcError::invalid_params(
1861                "block range too large, currently limited to 300".to_string(),
1862            )));
1863        }
1864
1865        // Accumulate tasks for block range
1866        let mut trace_tasks = vec![];
1867        for num in start..=end {
1868            trace_tasks.push(self.trace_block(num.into()));
1869        }
1870
1871        // Execute tasks and filter traces
1872        let traces = futures::future::try_join_all(trace_tasks).await?;
1873        let filtered_traces =
1874            traces.into_iter().flatten().filter(|trace| matcher.matches(&trace.trace));
1875
1876        // Apply after and count
1877        let filtered_traces: Vec<_> = if let Some(after) = filter.after {
1878            filtered_traces.skip(after as usize).collect()
1879        } else {
1880            filtered_traces.collect()
1881        };
1882
1883        let filtered_traces: Vec<_> = if let Some(count) = filter.count {
1884            filtered_traces.into_iter().take(count as usize).collect()
1885        } else {
1886            filtered_traces
1887        };
1888
1889        Ok(filtered_traces)
1890    }
1891
1892    pub fn get_blobs_by_block_id(
1893        &self,
1894        id: impl Into<BlockId>,
1895        versioned_hashes: Vec<B256>,
1896    ) -> Result<Option<Vec<alloy_consensus::Blob>>> {
1897        Ok(self.get_block(id).map(|block| {
1898            block
1899                .body
1900                .transactions
1901                .iter()
1902                .filter_map(|tx| tx.as_ref().sidecar())
1903                .flat_map(|sidecar| {
1904                    sidecar.sidecar.blobs().iter().zip(sidecar.sidecar.commitments().iter())
1905                })
1906                .filter(|(_, commitment)| {
1907                    // Filter blobs by versioned_hashes if provided
1908                    versioned_hashes.is_empty()
1909                        || versioned_hashes.contains(&kzg_to_versioned_hash(commitment.as_slice()))
1910                })
1911                .map(|(blob, _)| *blob)
1912                .collect()
1913        }))
1914    }
1915
1916    #[allow(clippy::large_stack_frames)]
1917    pub fn get_blob_by_versioned_hash(&self, hash: B256) -> Result<Option<Blob>> {
1918        let storage = self.blockchain.storage.read();
1919        for block in storage.blocks.values() {
1920            for tx in &block.body.transactions {
1921                let typed_tx = tx.as_ref();
1922                if let Some(sidecar) = typed_tx.sidecar() {
1923                    for versioned_hash in sidecar.sidecar.versioned_hashes() {
1924                        if versioned_hash == hash
1925                            && let Some(index) =
1926                                sidecar.sidecar.commitments().iter().position(|commitment| {
1927                                    kzg_to_versioned_hash(commitment.as_slice()) == *hash
1928                                })
1929                            && let Some(blob) = sidecar.sidecar.blobs().get(index)
1930                        {
1931                            return Ok(Some(*blob));
1932                        }
1933                    }
1934                }
1935            }
1936        }
1937        Ok(None)
1938    }
1939
1940    /// Initialises the balance of the given accounts
1941    #[expect(clippy::too_many_arguments)]
1942    pub async fn with_genesis(
1943        db: Arc<AsyncRwLock<Box<dyn Db>>>,
1944        env: Arc<RwLock<EvmEnv>>,
1945        networks: NetworkConfigs,
1946        genesis: GenesisConfig,
1947        fees: FeeManager,
1948        fork: Arc<RwLock<Option<ClientFork>>>,
1949        enable_steps_tracing: bool,
1950        print_logs: bool,
1951        print_traces: bool,
1952        call_trace_decoder: Arc<CallTraceDecoder>,
1953        prune_state_history_config: PruneStateHistoryConfig,
1954        max_persisted_states: Option<usize>,
1955        transaction_block_keeper: Option<usize>,
1956        automine_block_time: Option<Duration>,
1957        cache_path: Option<PathBuf>,
1958        node_config: Arc<AsyncRwLock<NodeConfig>>,
1959    ) -> Result<Self> {
1960        // if this is a fork then adjust the blockchain storage
1961        let blockchain = if let Some(fork) = fork.read().as_ref() {
1962            trace!(target: "backend", "using forked blockchain at {}", fork.block_number());
1963            Blockchain::forked(fork.block_number(), fork.block_hash(), fork.total_difficulty())
1964        } else {
1965            Blockchain::new(
1966                &env.read(),
1967                fees.is_eip1559().then(|| fees.base_fee()),
1968                genesis.timestamp,
1969                genesis.number,
1970            )
1971        };
1972
1973        // Sync EVM block.number with genesis for non-fork mode.
1974        // Fork mode syncs in setup_fork_db_config() instead.
1975        if fork.read().is_none() {
1976            env.write().block_env.number = U256::from(genesis.number);
1977        }
1978
1979        let start_timestamp = if let Some(fork) = fork.read().as_ref() {
1980            fork.timestamp()
1981        } else {
1982            genesis.timestamp
1983        };
1984
1985        let mut states = if prune_state_history_config.is_config_enabled() {
1986            // if prune state history is enabled, configure the state cache only for memory
1987            prune_state_history_config
1988                .max_memory_history
1989                .map(|limit| InMemoryBlockStates::new(limit, 0))
1990                .unwrap_or_default()
1991                .memory_only()
1992        } else if max_persisted_states.is_some() {
1993            max_persisted_states
1994                .map(|limit| InMemoryBlockStates::new(DEFAULT_HISTORY_LIMIT, limit))
1995                .unwrap_or_default()
1996        } else {
1997            Default::default()
1998        };
1999
2000        if let Some(cache_path) = cache_path {
2001            states = states.disk_path(cache_path);
2002        }
2003
2004        let (slots_in_an_epoch, precompile_factory, disable_pool_balance_checks, hardfork) = {
2005            let cfg = node_config.read().await;
2006            (
2007                cfg.slots_in_an_epoch,
2008                cfg.precompile_factory.clone(),
2009                cfg.disable_pool_balance_checks,
2010                cfg.get_hardfork(),
2011            )
2012        };
2013
2014        let backend = Self {
2015            db,
2016            blockchain,
2017            states: Arc::new(RwLock::new(states)),
2018            evm_env: env,
2019            networks,
2020            hardfork,
2021            fork,
2022            time: TimeManager::new(start_timestamp),
2023            cheats: Default::default(),
2024            new_block_listeners: Default::default(),
2025            fees,
2026            genesis,
2027            active_state_snapshots: Arc::new(Mutex::new(Default::default())),
2028            enable_steps_tracing,
2029            print_logs,
2030            print_traces,
2031            call_trace_decoder,
2032            prune_state_history_config,
2033            transaction_block_keeper,
2034            node_config,
2035            slots_in_an_epoch,
2036            precompile_factory,
2037            mining: Arc::new(tokio::sync::Mutex::new(())),
2038            disable_pool_balance_checks,
2039        };
2040
2041        if let Some(interval_block_time) = automine_block_time {
2042            backend.update_interval_mine_block_time(interval_block_time);
2043        }
2044
2045        // Note: this can only fail in forking mode, in which case we can't recover
2046        backend.apply_genesis().await.wrap_err("failed to create genesis")?;
2047        Ok(backend)
2048    }
2049
2050    /// Applies the configured genesis settings
2051    ///
2052    /// This will fund, create the genesis accounts
2053    async fn apply_genesis(&self) -> Result<(), DatabaseError> {
2054        trace!(target: "backend", "setting genesis balances");
2055
2056        if self.fork.read().is_some() {
2057            // fetch all account first
2058            let mut genesis_accounts_futures = Vec::with_capacity(self.genesis.accounts.len());
2059            for address in self.genesis.accounts.iter().copied() {
2060                let db = Arc::clone(&self.db);
2061
2062                // The forking Database backend can handle concurrent requests, we can fetch all dev
2063                // accounts concurrently by spawning the job to a new task
2064                genesis_accounts_futures.push(tokio::task::spawn(async move {
2065                    let db = db.read().await;
2066                    let info = db.basic_ref(address)?.unwrap_or_default();
2067                    Ok::<_, DatabaseError>((address, info))
2068                }));
2069            }
2070
2071            let genesis_accounts = futures::future::join_all(genesis_accounts_futures).await;
2072
2073            let mut db = self.db.write().await;
2074
2075            for res in genesis_accounts {
2076                let (address, mut info) = res.unwrap()?;
2077                info.balance = self.genesis.balance;
2078                db.insert_account(address, info.clone());
2079            }
2080        } else {
2081            let mut db = self.db.write().await;
2082            for (account, info) in self.genesis.account_infos() {
2083                db.insert_account(account, info);
2084            }
2085
2086            // insert the new genesis hash to the database so it's available for the next block in
2087            // the evm
2088            db.insert_block_hash(U256::from(self.best_number()), self.best_hash());
2089
2090            // Deploy EIP-2935 blockhash history storage contract if Prague is active.
2091            if self.spec_id() >= SpecId::PRAGUE {
2092                db.set_code(
2093                    eip2935::HISTORY_STORAGE_ADDRESS,
2094                    eip2935::HISTORY_STORAGE_CODE.clone(),
2095                )?;
2096            }
2097        }
2098
2099        let db = self.db.write().await;
2100        // apply the genesis.json alloc
2101        self.genesis.apply_genesis_json_alloc(db)?;
2102
2103        // Initialize Tempo precompiles and fee tokens when in Tempo mode (not in fork mode).
2104        // In fork mode, precompiles are inherited from the forked origin.
2105        if self.networks.is_tempo() && !self.is_fork() {
2106            let chain_id = self.evm_env.read().cfg_env.chain_id;
2107            let timestamp = self.genesis.timestamp;
2108            let test_accounts: Vec<Address> = self.genesis.accounts.clone();
2109            let hardfork = self.tempo_hardfork();
2110            let mut db = self.db.write().await;
2111            crate::eth::backend::tempo::initialize_tempo_precompiles(
2112                &mut **db,
2113                chain_id,
2114                timestamp,
2115                &test_accounts,
2116                hardfork,
2117            )
2118            .map_err(|e| {
2119                tracing::error!(target: "backend", "failed to initialize Tempo precompiles: {e}");
2120                DatabaseError::AnyRequest(Arc::new(eyre::eyre!("{e}")))
2121            })?;
2122            trace!(target: "backend", "initialized Tempo precompiles and fee tokens for {} accounts", test_accounts.len());
2123        }
2124
2125        trace!(target: "backend", "set genesis balances");
2126
2127        Ok(())
2128    }
2129
2130    /// Resets the fork to a fresh state
2131    pub async fn reset_fork(&self, forking: Forking) -> Result<(), BlockchainError> {
2132        if !self.is_fork() {
2133            if let Some(eth_rpc_url) = forking.json_rpc_url.clone() {
2134                let mut evm_env = self.evm_env.read().clone();
2135
2136                let (db, config) = {
2137                    let mut node_config = self.node_config.write().await;
2138
2139                    // we want to force the correct base fee for the next block during
2140                    // `setup_fork_db_config`
2141                    node_config.base_fee.take();
2142                    node_config.fork_urls = vec![eth_rpc_url.clone()];
2143
2144                    node_config.setup_fork_db_config(eth_rpc_url, &mut evm_env, &self.fees).await?
2145                };
2146
2147                *self.db.write().await = Box::new(db);
2148
2149                let fork = ClientFork::new(config, Arc::clone(&self.db));
2150
2151                *self.evm_env.write() = evm_env;
2152                *self.fork.write() = Some(fork);
2153            } else {
2154                return Err(RpcError::invalid_params(
2155                    "Forking not enabled and RPC URL not provided to start forking",
2156                )
2157                .into());
2158            }
2159        }
2160
2161        if let Some(fork) = self.get_fork() {
2162            let block_number =
2163                forking.block_number.map(BlockNumber::from).unwrap_or(BlockNumber::Latest);
2164            // reset the fork entirely and reapply the genesis config
2165            let reset_urls =
2166                forking.json_rpc_url.as_ref().map(|url| vec![url.clone()]).unwrap_or_default();
2167            fork.reset(reset_urls, block_number).await?;
2168            let fork_block_number = fork.block_number();
2169            let fork_block = fork
2170                .block_by_number(fork_block_number)
2171                .await?
2172                .ok_or(BlockchainError::BlockNotFound)?;
2173            // update all settings related to the forked block
2174            {
2175                if let Some(fork_url) = forking.json_rpc_url {
2176                    self.reset_block_number(fork_url, fork_block_number).await?;
2177                } else {
2178                    // If rpc url is unspecified, then update the fork with the new block number and
2179                    // existing rpc url, this updates the cache path
2180                    {
2181                        let maybe_fork_url =
2182                            { self.node_config.read().await.fork_urls.first().cloned() };
2183                        if let Some(fork_url) = maybe_fork_url {
2184                            self.reset_block_number(fork_url, fork_block_number).await?;
2185                        }
2186                    }
2187
2188                    let gas_limit = self.node_config.read().await.fork_gas_limit(&fork_block);
2189                    let mut env = self.evm_env.write();
2190
2191                    env.cfg_env.chain_id = fork.chain_id();
2192                    env.block_env = BlockEnv {
2193                        number: U256::from(fork_block_number),
2194                        timestamp: U256::from(fork_block.header.timestamp()),
2195                        gas_limit,
2196                        difficulty: fork_block.header.difficulty(),
2197                        prevrandao: Some(fork_block.header.mix_hash().unwrap_or_default()),
2198                        // Keep previous `beneficiary` and `basefee` value
2199                        beneficiary: env.block_env.beneficiary,
2200                        basefee: env.block_env.basefee,
2201                        ..env.block_env.clone()
2202                    };
2203
2204                    // this is the base fee of the current block, but we need the base fee of
2205                    // the next block
2206                    let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
2207                        fork_block.header.gas_used(),
2208                        gas_limit,
2209                        fork_block.header.base_fee_per_gas().unwrap_or_default(),
2210                    );
2211
2212                    self.fees.set_base_fee(next_block_base_fee);
2213                }
2214
2215                // reset the time to the timestamp of the forked block
2216                self.time.reset(fork_block.header.timestamp());
2217
2218                // also reset the total difficulty
2219                self.blockchain.storage.write().total_difficulty = fork.total_difficulty();
2220            }
2221            // reset storage
2222            *self.blockchain.storage.write() = BlockchainStorage::forked(
2223                fork.block_number(),
2224                fork.block_hash(),
2225                fork.total_difficulty(),
2226            );
2227            self.states.write().clear();
2228            self.db.write().await.clear();
2229
2230            self.apply_genesis().await?;
2231
2232            trace!(target: "backend", "reset fork");
2233
2234            Ok(())
2235        } else {
2236            Err(RpcError::invalid_params("Forking not enabled").into())
2237        }
2238    }
2239
2240    /// Resets the backend to a fresh in-memory state, clearing all existing data
2241    pub async fn reset_to_in_mem(&self) -> Result<(), BlockchainError> {
2242        // Clear the fork if any exists
2243        *self.fork.write() = None;
2244
2245        let genesis_timestamp = self.genesis.timestamp;
2246        let genesis_number = self.genesis.number;
2247
2248        // Reset environment to genesis state
2249        {
2250            let mut env = self.evm_env.write();
2251            env.block_env.number = U256::from(genesis_number);
2252            env.block_env.timestamp = U256::from(genesis_timestamp);
2253            // Reset other block env fields to their defaults
2254            env.block_env.basefee = self.fees.base_fee();
2255            env.block_env.prevrandao = Some(B256::ZERO);
2256        }
2257
2258        // Clear all storage and reinitialize with genesis
2259        let base_fee = self.fees.is_eip1559().then(|| self.fees.base_fee());
2260        *self.blockchain.storage.write() = BlockchainStorage::new(
2261            &self.evm_env.read(),
2262            base_fee,
2263            genesis_timestamp,
2264            genesis_number,
2265        );
2266        self.states.write().clear();
2267
2268        // Clear the database
2269        self.db.write().await.clear();
2270
2271        // Reset time manager
2272        self.time.reset(genesis_timestamp);
2273
2274        // Reset fees to initial state
2275        if self.fees.is_eip1559() {
2276            self.fees.set_base_fee(crate::eth::fees::INITIAL_BASE_FEE);
2277        }
2278
2279        self.fees.set_gas_price(crate::eth::fees::INITIAL_GAS_PRICE);
2280
2281        // Reapply genesis configuration
2282        self.apply_genesis().await?;
2283
2284        trace!(target: "backend", "reset to fresh in-memory state");
2285
2286        Ok(())
2287    }
2288
2289    async fn reset_block_number(
2290        &self,
2291        fork_url: String,
2292        fork_block_number: u64,
2293    ) -> Result<(), BlockchainError> {
2294        let mut node_config = self.node_config.write().await;
2295        node_config.fork_choice = Some(ForkChoice::Block(fork_block_number as i128));
2296        // Update fork_urls so setup_fork_db_config uses the correct URL set
2297        node_config.fork_urls = vec![fork_url.clone()];
2298
2299        let mut evm_env = self.evm_env.read().clone();
2300        let (forked_db, client_fork_config) =
2301            node_config.setup_fork_db_config(fork_url, &mut evm_env, &self.fees).await?;
2302
2303        *self.db.write().await = Box::new(forked_db);
2304        let fork = ClientFork::new(client_fork_config, Arc::clone(&self.db));
2305        *self.fork.write() = Some(fork);
2306        *self.evm_env.write() = evm_env;
2307
2308        Ok(())
2309    }
2310
2311    /// Reverts the state to the state snapshot identified by the given `id`.
2312    pub async fn revert_state_snapshot(&self, id: U256) -> Result<bool, BlockchainError> {
2313        let block = { self.active_state_snapshots.lock().remove(&id) };
2314        if let Some((num, hash)) = block {
2315            let best_block_hash = {
2316                // revert the storage that's newer than the snapshot
2317                let current_height = self.best_number();
2318                let mut storage = self.blockchain.storage.write();
2319
2320                for n in ((num + 1)..=current_height).rev() {
2321                    trace!(target: "backend", "reverting block {}", n);
2322                    if let Some(hash) = storage.hashes.remove(&n)
2323                        && let Some(block) = storage.blocks.remove(&hash)
2324                    {
2325                        for tx in block.body.transactions {
2326                            let _ = storage.transactions.remove(&tx.hash());
2327                        }
2328                    }
2329                }
2330
2331                storage.best_number = num;
2332                storage.best_hash = hash;
2333                hash
2334            };
2335            let block =
2336                self.block_by_hash(best_block_hash).await?.ok_or(BlockchainError::BlockNotFound)?;
2337
2338            let reset_time = block.header.timestamp();
2339            self.time.reset(reset_time);
2340
2341            let mut env = self.evm_env.write();
2342            env.block_env = BlockEnv {
2343                number: U256::from(num),
2344                timestamp: U256::from(block.header.timestamp()),
2345                difficulty: block.header.difficulty(),
2346                // ensures prevrandao is set
2347                prevrandao: Some(block.header.mix_hash().unwrap_or_default()),
2348                gas_limit: block.header.gas_limit(),
2349                // Keep previous `beneficiary` and `basefee` value
2350                beneficiary: env.block_env.beneficiary,
2351                basefee: env.block_env.basefee,
2352                ..Default::default()
2353            }
2354        }
2355        Ok(self.db.write().await.revert_state(id, RevertStateSnapshotAction::RevertRemove))
2356    }
2357
2358    /// executes the transactions without writing to the underlying database
2359    pub async fn inspect_tx(
2360        &self,
2361        tx: Arc<PoolTransaction<FoundryTxEnvelope>>,
2362    ) -> Result<
2363        (InstructionResult, Option<Output>, u64, State, Vec<revm::primitives::Log>),
2364        BlockchainError,
2365    > {
2366        let evm_env = self.next_evm_env();
2367        let db = self.db.read().await;
2368        let mut inspector = self.build_inspector();
2369        let (ResultAndState { result, state }, _) = self.transact_envelope_with_inspector_ref(
2370            &**db,
2371            &evm_env,
2372            &mut inspector,
2373            tx.pending_transaction.transaction.as_ref(),
2374            *tx.pending_transaction.sender(),
2375        )?;
2376        let (exit_reason, gas_used, out, logs) = unpack_execution_result(result);
2377
2378        inspector.print_logs();
2379
2380        if self.print_traces {
2381            inspector.print_traces(self.call_trace_decoder.clone());
2382        }
2383
2384        Ok((exit_reason, out, gas_used, state, logs))
2385    }
2386}
2387
2388impl<N: Network> Backend<N>
2389where
2390    N::ReceiptEnvelope: TxReceipt<Log = alloy_primitives::Log>,
2391{
2392    /// Returns all `Log`s mined by the node that were emitted in the `block` and match the `Filter`
2393    fn mined_logs_for_block(&self, filter: Filter, block: Block, block_hash: B256) -> Vec<Log> {
2394        let mut all_logs = Vec::new();
2395        let mut block_log_index = 0u32;
2396
2397        let storage = self.blockchain.storage.read();
2398
2399        for tx in block.body.transactions {
2400            let Some(tx) = storage.transactions.get(&tx.hash()) else {
2401                continue;
2402            };
2403
2404            let logs = tx.receipt.logs();
2405            let transaction_hash = tx.info.transaction_hash;
2406
2407            for log in logs {
2408                if filter.matches(log) {
2409                    all_logs.push(Log {
2410                        inner: log.clone(),
2411                        block_hash: Some(block_hash),
2412                        block_number: Some(block.header.number()),
2413                        block_timestamp: Some(block.header.timestamp()),
2414                        transaction_hash: Some(transaction_hash),
2415                        transaction_index: Some(tx.info.transaction_index),
2416                        log_index: Some(block_log_index as u64),
2417                        removed: false,
2418                    });
2419                }
2420                block_log_index += 1;
2421            }
2422        }
2423        all_logs
2424    }
2425
2426    /// Returns the logs of the block that match the filter
2427    async fn logs_for_block(
2428        &self,
2429        filter: Filter,
2430        hash: B256,
2431    ) -> Result<Vec<Log>, BlockchainError> {
2432        if let Some(block) = self.blockchain.get_block_by_hash(&hash) {
2433            return Ok(self.mined_logs_for_block(filter, block, hash));
2434        }
2435
2436        if let Some(fork) = self.get_fork() {
2437            return Ok(fork.logs(&filter).await?);
2438        }
2439
2440        Err(BlockchainError::UnknownBlock)
2441    }
2442
2443    /// Returns the logs that match the filter in the given range of blocks
2444    async fn logs_for_range(
2445        &self,
2446        filter: &Filter,
2447        mut from: u64,
2448        to: u64,
2449    ) -> Result<Vec<Log>, BlockchainError> {
2450        let mut all_logs = Vec::new();
2451
2452        // get the range that predates the fork if any
2453        if let Some(fork) = self.get_fork() {
2454            let to_on_fork = if fork.predates_fork(to) {
2455                to
2456            } else {
2457                // adjust the ranges
2458                fork.block_number()
2459            };
2460
2461            if fork.predates_fork_inclusive(from) {
2462                // this data is only available on the forked client
2463                let filter = filter.clone().from_block(from).to_block(to_on_fork);
2464                all_logs = fork.logs(&filter).await?;
2465
2466                // update the range
2467                from = fork.block_number() + 1;
2468            }
2469        }
2470
2471        for number in from..=to {
2472            if let Some((block, hash)) = self.get_block_with_hash(number) {
2473                all_logs.extend(self.mined_logs_for_block(filter.clone(), block, hash));
2474            }
2475        }
2476
2477        Ok(all_logs)
2478    }
2479
2480    /// Returns the logs according to the filter
2481    pub async fn logs(&self, filter: Filter) -> Result<Vec<Log>, BlockchainError> {
2482        trace!(target: "backend", "get logs [{:?}]", filter);
2483        if let Some(hash) = filter.get_block_hash() {
2484            self.logs_for_block(filter, hash).await
2485        } else {
2486            let best = self.best_number();
2487            let to_block =
2488                self.convert_block_number(filter.block_option.get_to_block().copied()).min(best);
2489            let from_block =
2490                self.convert_block_number(filter.block_option.get_from_block().copied());
2491            if from_block > best {
2492                return Err(BlockchainError::BlockOutOfRange(best, from_block));
2493            }
2494
2495            self.logs_for_range(&filter, from_block, to_block).await
2496        }
2497    }
2498
2499    /// Returns all receipts of the block
2500    pub fn mined_receipts(&self, hash: B256) -> Option<Vec<N::ReceiptEnvelope>> {
2501        let block = self.mined_block_by_hash(hash)?;
2502        let mut receipts = Vec::new();
2503        let storage = self.blockchain.storage.read();
2504        for tx in block.transactions.hashes() {
2505            let receipt = storage.transactions.get(&tx)?.receipt.clone();
2506            receipts.push(receipt);
2507        }
2508        Some(receipts)
2509    }
2510}
2511
2512// Mining methods — generic over N: Network, with Foundry-associated-type bounds for now.
2513impl<N: Network> Backend<N>
2514where
2515    Self: TransactionValidator<FoundryTxEnvelope>,
2516    N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
2517{
2518    /// Mines a new block and stores it.
2519    ///
2520    /// this will execute all transaction in the order they come in and return all the markers they
2521    /// provide.
2522    pub async fn mine_block(
2523        &self,
2524        pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
2525    ) -> MinedBlockOutcome<FoundryTxEnvelope> {
2526        self.do_mine_block(pool_transactions).await
2527    }
2528
2529    /// Builds a [`BlockInfo`] from the EVM environment, execution results, and transactions.
2530    fn build_block_info(
2531        evm_env: &EvmEnv,
2532        parent_hash: B256,
2533        number: u64,
2534        state_root: B256,
2535        block_result: BlockExecutionResult<FoundryReceiptEnvelope>,
2536        transactions: Vec<MaybeImpersonatedTransaction<FoundryTxEnvelope>>,
2537        transaction_infos: Vec<TransactionInfo>,
2538    ) -> BlockInfo<N> {
2539        let spec_id = *evm_env.spec_id();
2540        let is_shanghai = spec_id >= SpecId::SHANGHAI;
2541        let is_cancun = spec_id >= SpecId::CANCUN;
2542        let is_prague = spec_id >= SpecId::PRAGUE;
2543
2544        let receipts_root = calculate_receipt_root(&block_result.receipts);
2545        let cumulative_blob_gas_used = is_cancun.then_some(block_result.blob_gas_used);
2546        let bloom = block_result.receipts.iter().fold(Bloom::default(), |mut b, r| {
2547            b.accrue_bloom(r.logs_bloom());
2548            b
2549        });
2550
2551        let header = Header {
2552            parent_hash,
2553            ommers_hash: Default::default(),
2554            beneficiary: evm_env.block_env.beneficiary,
2555            state_root,
2556            transactions_root: Default::default(),
2557            receipts_root,
2558            logs_bloom: bloom,
2559            difficulty: evm_env.block_env.difficulty,
2560            number,
2561            gas_limit: evm_env.block_env.gas_limit,
2562            gas_used: block_result.gas_used,
2563            timestamp: evm_env.block_env.timestamp.saturating_to(),
2564            extra_data: Default::default(),
2565            mix_hash: evm_env.block_env.prevrandao.unwrap_or_default(),
2566            nonce: Default::default(),
2567            base_fee_per_gas: (spec_id >= SpecId::LONDON).then_some(evm_env.block_env.basefee),
2568            parent_beacon_block_root: is_cancun.then_some(Default::default()),
2569            blob_gas_used: cumulative_blob_gas_used,
2570            excess_blob_gas: if is_cancun { evm_env.block_env.blob_excess_gas() } else { None },
2571            withdrawals_root: is_shanghai.then_some(EMPTY_WITHDRAWALS),
2572            requests_hash: is_prague.then_some(EMPTY_REQUESTS_HASH),
2573            block_access_list_hash: None,
2574            slot_number: None,
2575        };
2576
2577        let block = create_block(header, transactions);
2578        BlockInfo { block, transactions: transaction_infos, receipts: block_result.receipts }
2579    }
2580
2581    async fn do_mine_block(
2582        &self,
2583        pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
2584    ) -> MinedBlockOutcome<FoundryTxEnvelope> {
2585        let _mining_guard = self.mining.lock().await;
2586        trace!(target: "backend", "creating new block with {} transactions", pool_transactions.len());
2587
2588        let (outcome, header, block_hash) = {
2589            let current_base_fee = self.base_fee();
2590            let current_excess_blob_gas_and_price = self.excess_blob_gas_and_price();
2591
2592            let mut evm_env = self.evm_env.read().clone();
2593
2594            if evm_env.block_env.basefee == 0 {
2595                // this is an edge case because the evm fails if `tx.effective_gas_price < base_fee`
2596                // 0 is only possible if it's manually set
2597                evm_env.cfg_env.disable_base_fee = true;
2598            }
2599
2600            let block_number = self.blockchain.storage.read().best_number.saturating_add(1);
2601
2602            // increase block number for this block
2603            if is_arbitrum(evm_env.cfg_env.chain_id) {
2604                // Temporary set `env.block.number` to `block_number` for Arbitrum chains.
2605                evm_env.block_env.number = U256::from(block_number);
2606            } else {
2607                evm_env.block_env.number = evm_env.block_env.number.saturating_add(U256::from(1));
2608            }
2609
2610            evm_env.block_env.basefee = current_base_fee;
2611            evm_env.block_env.blob_excess_gas_and_price = current_excess_blob_gas_and_price;
2612
2613            let best_hash = self.blockchain.storage.read().best_hash;
2614
2615            let mut input = Vec::with_capacity(40);
2616            input.extend_from_slice(best_hash.as_slice());
2617            input.extend_from_slice(&block_number.to_le_bytes());
2618            evm_env.block_env.prevrandao = Some(keccak256(&input));
2619
2620            if self.prune_state_history_config.is_state_history_supported() {
2621                let db = self.db.read().await.current_state();
2622                // store current state before executing all transactions
2623                self.states.write().insert(best_hash, db);
2624            }
2625
2626            let (block_info, included, invalid, not_yet_valid, block_hash) = {
2627                let mut db = self.db.write().await;
2628
2629                // finally set the next block timestamp, this is done just before execution, because
2630                // there can be concurrent requests that can delay acquiring the db lock and we want
2631                // to ensure the timestamp is as close as possible to the actual execution.
2632                evm_env.block_env.timestamp = U256::from(self.time.next_timestamp());
2633
2634                let spec_id = *evm_env.spec_id();
2635
2636                let inspector_tx_config = self.inspector_tx_config();
2637                let gas_config = self.pool_tx_gas_config(&evm_env);
2638
2639                let (pool_result, block_result) = self.execute_with_block_executor(
2640                    &mut **db,
2641                    &evm_env,
2642                    best_hash,
2643                    spec_id,
2644                    &pool_transactions,
2645                    &gas_config,
2646                    &inspector_tx_config,
2647                    &|pending, account| {
2648                        self.validate_pool_transaction_for(pending, account, &evm_env)
2649                    },
2650                );
2651
2652                let included = pool_result.included;
2653                let invalid = pool_result.invalid;
2654                let not_yet_valid = pool_result.not_yet_valid;
2655
2656                let state_root = db.maybe_state_root().unwrap_or_default();
2657                let block_info = Self::build_block_info(
2658                    &evm_env,
2659                    best_hash,
2660                    block_number,
2661                    state_root,
2662                    block_result,
2663                    pool_result.txs,
2664                    pool_result.tx_info,
2665                );
2666
2667                // update the new blockhash in the db itself
2668                let block_hash = block_info.block.header.hash_slow();
2669                db.insert_block_hash(U256::from(block_info.block.header.number()), block_hash);
2670
2671                (block_info, included, invalid, not_yet_valid, block_hash)
2672            };
2673
2674            // create the new block with the current timestamp
2675            let BlockInfo { block, transactions, receipts } = block_info;
2676
2677            let header = block.header.clone();
2678
2679            trace!(
2680                target: "backend",
2681                "Mined block {} with {} tx {:?}",
2682                block_number,
2683                transactions.len(),
2684                transactions.iter().map(|tx| tx.transaction_hash).collect::<Vec<_>>()
2685            );
2686            let mut storage = self.blockchain.storage.write();
2687            // update block metadata
2688            storage.best_number = block_number;
2689            storage.best_hash = block_hash;
2690            // Difficulty is removed and not used after Paris (aka TheMerge). Value is replaced with
2691            // prevrandao. https://github.com/bluealloy/revm/blob/1839b3fce8eaeebb85025576f2519b80615aca1e/crates/interpreter/src/instructions/host_env.rs#L27
2692            if !self.is_eip3675() {
2693                storage.total_difficulty =
2694                    storage.total_difficulty.saturating_add(header.difficulty);
2695            }
2696
2697            storage.blocks.insert(block_hash, block);
2698            storage.hashes.insert(block_number, block_hash);
2699
2700            node_info!("");
2701            // insert all transactions
2702            for (info, receipt) in transactions.into_iter().zip(receipts) {
2703                // log some tx info
2704                node_info!("    Transaction: {:?}", info.transaction_hash);
2705                if let Some(contract) = &info.contract_address {
2706                    node_info!("    Contract created: {contract}");
2707                }
2708                node_info!("    Gas used: {}", receipt.cumulative_gas_used());
2709                if !info.exit.is_ok() {
2710                    let r = RevertDecoder::new().decode(
2711                        info.out.as_ref().map(|b| &b[..]).unwrap_or_default(),
2712                        Some(info.exit),
2713                    );
2714                    node_info!("    Error: reverted with: {r}");
2715                }
2716                node_info!("");
2717
2718                let mined_tx = MinedTransaction { info, receipt, block_hash, block_number };
2719                storage.transactions.insert(mined_tx.info.transaction_hash, mined_tx);
2720            }
2721
2722            // remove old transactions that exceed the transaction block keeper
2723            if let Some(transaction_block_keeper) = self.transaction_block_keeper
2724                && storage.blocks.len() > transaction_block_keeper
2725            {
2726                let to_clear = block_number
2727                    .saturating_sub(transaction_block_keeper.try_into().unwrap_or(u64::MAX));
2728                storage.remove_block_transactions_by_number(to_clear)
2729            }
2730
2731            // we intentionally set the difficulty to `0` for newer blocks
2732            evm_env.block_env.difficulty = U256::from(0);
2733
2734            // update env with new values
2735            *self.evm_env.write() = evm_env;
2736
2737            let timestamp = utc_from_secs(header.timestamp);
2738
2739            node_info!("    Block Number: {}", block_number);
2740            node_info!("    Block Hash: {:?}", block_hash);
2741            if timestamp.year() > 9999 {
2742                // rf2822 panics with more than 4 digits
2743                node_info!("    Block Time: {:?}\n", timestamp.to_rfc3339());
2744            } else {
2745                node_info!("    Block Time: {:?}\n", timestamp.to_rfc2822());
2746            }
2747
2748            let outcome = MinedBlockOutcome { block_number, included, invalid, not_yet_valid };
2749
2750            (outcome, header, block_hash)
2751        };
2752        let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
2753            header.gas_used,
2754            header.gas_limit,
2755            header.base_fee_per_gas.unwrap_or_default(),
2756        );
2757        let next_block_excess_blob_gas = self.fees.get_next_block_blob_excess_gas(
2758            header.excess_blob_gas.unwrap_or_default(),
2759            header.blob_gas_used.unwrap_or_default(),
2760        );
2761
2762        // update next base fee
2763        self.fees.set_base_fee(next_block_base_fee);
2764
2765        self.fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(
2766            next_block_excess_blob_gas,
2767            get_blob_base_fee_update_fraction_by_spec_id(*self.evm_env.read().spec_id()),
2768        ));
2769
2770        // notify all listeners
2771        self.notify_on_new_block(header, block_hash);
2772
2773        outcome
2774    }
2775
2776    /// Reorg the chain to a common height and execute blocks to build new chain.
2777    ///
2778    /// The state of the chain is rewound using `rewind` to the common block, including the db,
2779    /// storage, and env.
2780    ///
2781    /// Finally, `do_mine_block` is called to create the new chain.
2782    pub async fn reorg(
2783        &self,
2784        depth: u64,
2785        tx_pairs: HashMap<u64, Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>>,
2786        common_block: Block,
2787    ) -> Result<(), BlockchainError> {
2788        self.rollback(common_block).await?;
2789        // Create the new reorged chain, filling the blocks with transactions if supplied
2790        for i in 0..depth {
2791            let to_be_mined = tx_pairs.get(&i).cloned().unwrap_or_else(Vec::new);
2792            let outcome = self.do_mine_block(to_be_mined).await;
2793            node_info!(
2794                "    Mined reorg block number {}. With {} valid txs and with invalid {} txs",
2795                outcome.block_number,
2796                outcome.included.len(),
2797                outcome.invalid.len()
2798            );
2799        }
2800
2801        Ok(())
2802    }
2803
2804    /// Creates the pending block
2805    ///
2806    /// This will execute all transaction in the order they come but will not mine the block
2807    pub async fn pending_block(
2808        &self,
2809        pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
2810    ) -> BlockInfo<N> {
2811        self.with_pending_block(pool_transactions, |_, block| block).await
2812    }
2813
2814    /// Creates the pending block
2815    ///
2816    /// This will execute all transaction in the order they come but will not mine the block
2817    pub async fn with_pending_block<F, T>(
2818        &self,
2819        pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
2820        f: F,
2821    ) -> T
2822    where
2823        F: FnOnce(Box<dyn MaybeFullDatabase + '_>, BlockInfo<N>) -> T,
2824    {
2825        let db = self.db.read().await;
2826        let evm_env = self.next_evm_env();
2827
2828        let mut cache_db = AnvilCacheDB::new(&*db);
2829
2830        let parent_hash = self.blockchain.storage.read().best_hash;
2831
2832        let spec_id = *evm_env.spec_id();
2833
2834        let inspector_tx_config = self.inspector_tx_config();
2835        let gas_config = self.pool_tx_gas_config(&evm_env);
2836
2837        let (pool_result, block_result) = self.execute_with_block_executor(
2838            &mut cache_db,
2839            &evm_env,
2840            parent_hash,
2841            spec_id,
2842            &pool_transactions,
2843            &gas_config,
2844            &inspector_tx_config,
2845            &|pending, account| self.validate_pool_transaction_for(pending, account, &evm_env),
2846        );
2847
2848        // Extract inner CacheDB (which implements MaybeFullDatabase)
2849        let cache_db = cache_db.0;
2850
2851        let state_root = cache_db.maybe_state_root().unwrap_or_default();
2852        let block_number = evm_env.block_env.number.saturating_to();
2853        let block_info = Self::build_block_info(
2854            &evm_env,
2855            parent_hash,
2856            block_number,
2857            state_root,
2858            block_result,
2859            pool_result.txs,
2860            pool_result.tx_info,
2861        );
2862
2863        f(Box::new(cache_db), block_info)
2864    }
2865
2866    /// Returns the ERC20/TIP20 token balance for an account.
2867    ///
2868    /// Calls `balanceOf(address)` on the token contract. Returns `U256::ZERO` if
2869    /// the call fails (e.g. the token contract doesn't exist).
2870    pub async fn get_fee_token_balance(
2871        &self,
2872        token: Address,
2873        account: Address,
2874    ) -> Result<U256, BlockchainError> {
2875        // balanceOf(address) selector: 0x70a08231
2876        let mut calldata = vec![0x70, 0xa0, 0x82, 0x31];
2877        // ABI-encode the address (left-padded to 32 bytes)
2878        calldata.extend_from_slice(&[0u8; 12]);
2879        calldata.extend_from_slice(account.as_slice());
2880
2881        let request = WithOtherFields::new(TransactionRequest {
2882            from: Some(Address::ZERO),
2883            to: Some(TxKind::Call(token)),
2884            input: calldata.into(),
2885            ..Default::default()
2886        });
2887
2888        let fee_details = FeeDetails::zero();
2889        let (exit, out, _, _) = self.call(request, fee_details, None, Default::default()).await?;
2890
2891        // Check if call succeeded
2892        if exit != InstructionResult::Return && exit != InstructionResult::Stop {
2893            // Return zero balance if call failed (token might not exist)
2894            return Ok(U256::ZERO);
2895        }
2896
2897        // Decode U256 from output
2898        match out {
2899            Some(Output::Call(data)) if data.len() >= 32 => Ok(U256::from_be_slice(&data[..32])),
2900            _ => Ok(U256::ZERO),
2901        }
2902    }
2903
2904    /// Executes the [TransactionRequest] without writing to the DB
2905    ///
2906    /// # Errors
2907    ///
2908    /// Returns an error if the `block_number` is greater than the current height
2909    pub async fn call(
2910        &self,
2911        request: WithOtherFields<TransactionRequest>,
2912        fee_details: FeeDetails,
2913        block_request: Option<BlockRequest<FoundryTxEnvelope>>,
2914        overrides: EvmOverrides,
2915    ) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
2916        self.with_database_at(block_request, |state, mut block| {
2917            let block_number = block.number;
2918            let (exit, out, gas, state) = {
2919                let mut cache_db = CacheDB::new(state);
2920                if let Some(state_overrides) = overrides.state {
2921                    apply_state_overrides(state_overrides.into_iter().collect(), &mut cache_db)?;
2922                }
2923                if let Some(block_overrides) = overrides.block {
2924                    cache_db.apply_block_overrides(*block_overrides, &mut block);
2925                }
2926                self.call_with_state(&cache_db, request, fee_details, block)
2927            }?;
2928            trace!(target: "backend", "call return {:?} out: {:?} gas {} on block {}", exit, out, gas, block_number);
2929            Ok((exit, out, gas, state))
2930        }).await?
2931    }
2932
2933    pub async fn call_with_tracing(
2934        &self,
2935        request: WithOtherFields<TransactionRequest>,
2936        fee_details: FeeDetails,
2937        block_request: Option<BlockRequest<FoundryTxEnvelope>>,
2938        opts: GethDebugTracingCallOptions,
2939    ) -> Result<GethTrace, BlockchainError> {
2940        let GethDebugTracingCallOptions {
2941            tracing_options, block_overrides, state_overrides, ..
2942        } = opts;
2943        let GethDebugTracingOptions { config, tracer, tracer_config, .. } = tracing_options;
2944
2945        self.with_database_at(block_request, |state, mut block| {
2946            let block_number = block.number;
2947
2948            let mut cache_db = CacheDB::new(state);
2949            if let Some(state_overrides) = state_overrides {
2950                apply_state_overrides(state_overrides, &mut cache_db)?;
2951            }
2952            if let Some(block_overrides) = block_overrides {
2953                cache_db.apply_block_overrides(block_overrides, &mut block);
2954            }
2955
2956            if let Some(tracer) = tracer {
2957                return match tracer {
2958                    GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
2959                        GethDebugBuiltInTracerType::CallTracer => {
2960                            let call_config = tracer_config
2961                                .into_call_config()
2962                                .map_err(|e| RpcError::invalid_params(e.to_string()))?;
2963
2964                            let mut inspector = self.build_inspector().with_tracing_config(
2965                                TracingInspectorConfig::from_geth_call_config(&call_config),
2966                            );
2967
2968                            let (evm_env, tx_env, op_deposit) =
2969                                self.build_call_env(request, fee_details, block);
2970                            let ResultAndState { result, state: _ } = self
2971                                .transact_with_inspector_ref(
2972                                    &cache_db,
2973                                    &evm_env,
2974                                    &mut inspector,
2975                                    tx_env,
2976                                    op_deposit,
2977                                )?;
2978
2979                            inspector.print_logs();
2980                            if self.print_traces {
2981                                inspector.print_traces(self.call_trace_decoder.clone());
2982                            }
2983
2984                            let tracing_inspector = inspector.tracer.expect("tracer disappeared");
2985
2986                            Ok(tracing_inspector
2987                                .into_geth_builder()
2988                                .geth_call_traces(call_config, result.tx_gas_used())
2989                                .into())
2990                        }
2991                        GethDebugBuiltInTracerType::PreStateTracer => {
2992                            let pre_state_config = tracer_config
2993                                .into_pre_state_config()
2994                                .map_err(|e| RpcError::invalid_params(e.to_string()))?;
2995
2996                            let mut inspector = TracingInspector::new(
2997                                TracingInspectorConfig::from_geth_prestate_config(
2998                                    &pre_state_config,
2999                                ),
3000                            );
3001
3002                            let (evm_env, tx_env, op_deposit) =
3003                                self.build_call_env(request, fee_details, block);
3004                            let result = self.transact_with_inspector_ref(
3005                                &cache_db,
3006                                &evm_env,
3007                                &mut inspector,
3008                                tx_env,
3009                                op_deposit,
3010                            )?;
3011
3012                            Ok(inspector
3013                                .into_geth_builder()
3014                                .geth_prestate_traces(&result, &pre_state_config, cache_db)?
3015                                .into())
3016                        }
3017                        GethDebugBuiltInTracerType::NoopTracer => Ok(NoopFrame::default().into()),
3018                        GethDebugBuiltInTracerType::FourByteTracer
3019                        | GethDebugBuiltInTracerType::MuxTracer
3020                        | GethDebugBuiltInTracerType::FlatCallTracer
3021                        | GethDebugBuiltInTracerType::Erc7562Tracer => {
3022                            Err(RpcError::invalid_params("unsupported tracer type").into())
3023                        }
3024                    },
3025                    #[cfg(not(feature = "js-tracer"))]
3026                    GethDebugTracerType::JsTracer(_) => {
3027                        Err(RpcError::invalid_params("unsupported tracer type").into())
3028                    }
3029                    #[cfg(feature = "js-tracer")]
3030                    GethDebugTracerType::JsTracer(code) => {
3031                        let config = tracer_config.into_json();
3032                        let mut inspector =
3033                            revm_inspectors::tracing::js::JsInspector::new(code, config)
3034                                .map_err(|err| BlockchainError::Message(err.to_string()))?;
3035
3036                        let (evm_env, tx_env, op_deposit) =
3037                            self.build_call_env(request, fee_details, block.clone());
3038                        let result = self.transact_with_inspector_ref(
3039                            &cache_db,
3040                            &evm_env,
3041                            &mut inspector,
3042                            tx_env.clone(),
3043                            op_deposit,
3044                        )?;
3045                        let res = inspector
3046                            .json_result(result, &tx_env, &block, &cache_db)
3047                            .map_err(|err| BlockchainError::Message(err.to_string()))?;
3048
3049                        Ok(GethTrace::JS(res))
3050                    }
3051                };
3052            }
3053
3054            // defaults to StructLog tracer used since no tracer is specified
3055            let mut inspector = self
3056                .build_inspector()
3057                .with_tracing_config(TracingInspectorConfig::from_geth_config(&config));
3058
3059            let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block);
3060            let ResultAndState { result, state: _ } = self.transact_with_inspector_ref(
3061                &cache_db,
3062                &evm_env,
3063                &mut inspector,
3064                tx_env,
3065                op_deposit,
3066            )?;
3067
3068            let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result);
3069
3070            let tracing_inspector = inspector.tracer.expect("tracer disappeared");
3071            let return_value = out.as_ref().map(|o| o.data()).cloned().unwrap_or_default();
3072
3073            trace!(target: "backend", ?exit_reason, ?out, %gas_used, %block_number, "trace call");
3074
3075            let res = tracing_inspector
3076                .into_geth_builder()
3077                .geth_traces(gas_used, return_value, config)
3078                .into();
3079
3080            Ok(res)
3081        })
3082        .await?
3083    }
3084
3085    /// Helper function to execute a closure with the database at a specific block
3086    pub async fn with_database_at<F, T>(
3087        &self,
3088        block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3089        f: F,
3090    ) -> Result<T, BlockchainError>
3091    where
3092        F: FnOnce(Box<dyn MaybeFullDatabase + '_>, BlockEnv) -> T,
3093    {
3094        let block_number = match block_request {
3095            Some(BlockRequest::Pending(pool_transactions)) => {
3096                let result = self
3097                    .with_pending_block(pool_transactions, |state, block| {
3098                        let block = block.block;
3099                        f(state, block_env_from_header(&block.header))
3100                    })
3101                    .await;
3102                return Ok(result);
3103            }
3104            Some(BlockRequest::Number(bn)) => Some(BlockNumber::Number(bn)),
3105            None => None,
3106        };
3107        let block_number = self.convert_block_number(block_number);
3108        let current_number = self.best_number();
3109
3110        // Reject requests for future blocks that don't exist yet
3111        if block_number > current_number {
3112            return Err(BlockchainError::BlockOutOfRange(current_number, block_number));
3113        }
3114
3115        if block_number < current_number {
3116            if let Some((block_hash, block)) = self
3117                .block_by_number(BlockNumber::Number(block_number))
3118                .await?
3119                .map(|block| (block.header.hash, block))
3120            {
3121                let read_guard = self.states.upgradable_read();
3122                if let Some(state_db) = read_guard.get_state(&block_hash) {
3123                    return Ok(f(Box::new(state_db), block_env_from_header(&block.header)));
3124                }
3125
3126                let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
3127                if let Some(state) = write_guard.get_on_disk_state(&block_hash) {
3128                    return Ok(f(Box::new(state), block_env_from_header(&block.header)));
3129                }
3130            }
3131
3132            warn!(target: "backend", "Not historic state found for block={}", block_number);
3133            return Err(BlockchainError::BlockOutOfRange(current_number, block_number));
3134        }
3135
3136        let db = self.db.read().await;
3137        let block = self.evm_env.read().block_env.clone();
3138        Ok(f(Box::new(&**db), block))
3139    }
3140
3141    pub async fn storage_at(
3142        &self,
3143        address: Address,
3144        index: U256,
3145        block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3146    ) -> Result<B256, BlockchainError> {
3147        self.with_database_at(block_request, |db, _| {
3148            trace!(target: "backend", "get storage for {:?} at {:?}", address, index);
3149            let val = db.storage_ref(address, index)?;
3150            Ok(val.into())
3151        })
3152        .await?
3153    }
3154
3155    /// Returns storage values for multiple accounts and slots in a single call.
3156    pub async fn storage_values(
3157        &self,
3158        requests: HashMap<Address, Vec<B256>>,
3159        block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3160    ) -> Result<HashMap<Address, Vec<B256>>, BlockchainError> {
3161        self.with_database_at(block_request, |db, _| {
3162            trace!(target: "backend", "get storage values for {} addresses", requests.len());
3163            let mut result: HashMap<Address, Vec<B256>> = HashMap::default();
3164            for (address, slots) in &requests {
3165                let mut values = Vec::with_capacity(slots.len());
3166                for slot in slots {
3167                    let val = db.storage_ref(*address, (*slot).into())?;
3168                    values.push(val.into());
3169                }
3170                result.insert(*address, values);
3171            }
3172            Ok(result)
3173        })
3174        .await?
3175    }
3176
3177    /// Returns the code of the address
3178    ///
3179    /// If the code is not present and fork mode is enabled then this will try to fetch it from the
3180    /// forked client
3181    pub async fn get_code(
3182        &self,
3183        address: Address,
3184        block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3185    ) -> Result<Bytes, BlockchainError> {
3186        self.with_database_at(block_request, |db, _| self.get_code_with_state(&db, address)).await?
3187    }
3188
3189    /// Returns the balance of the address
3190    ///
3191    /// If the requested number predates the fork then this will fetch it from the endpoint
3192    pub async fn get_balance(
3193        &self,
3194        address: Address,
3195        block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3196    ) -> Result<U256, BlockchainError> {
3197        self.with_database_at(block_request, |db, _| self.get_balance_with_state(db, address))
3198            .await?
3199    }
3200
3201    pub async fn get_account_at_block(
3202        &self,
3203        address: Address,
3204        block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3205    ) -> Result<TrieAccount, BlockchainError> {
3206        self.with_database_at(block_request, |block_db, _| {
3207            let db = block_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?;
3208            let account = db.get(&address).cloned().unwrap_or_default();
3209            let storage_root = storage_root(&account.storage);
3210            let code_hash = account.info.code_hash;
3211            let balance = account.info.balance;
3212            let nonce = account.info.nonce;
3213            Ok(TrieAccount { balance, nonce, code_hash, storage_root })
3214        })
3215        .await?
3216    }
3217
3218    /// Returns the nonce of the address
3219    ///
3220    /// If the requested number predates the fork then this will fetch it from the endpoint
3221    pub async fn get_nonce(
3222        &self,
3223        address: Address,
3224        block_request: BlockRequest<FoundryTxEnvelope>,
3225    ) -> Result<u64, BlockchainError> {
3226        if let BlockRequest::Pending(pool_transactions) = &block_request
3227            && let Some(value) = get_pool_transactions_nonce(pool_transactions, address)
3228        {
3229            return Ok(value);
3230        }
3231        let final_block_request = match block_request {
3232            BlockRequest::Pending(_) => BlockRequest::Number(self.best_number()),
3233            BlockRequest::Number(bn) => BlockRequest::Number(bn),
3234        };
3235
3236        self.with_database_at(Some(final_block_request), |db, _| {
3237            trace!(target: "backend", "get nonce for {:?}", address);
3238            Ok(db.basic_ref(address)?.unwrap_or_default().nonce)
3239        })
3240        .await?
3241    }
3242
3243    fn replay_tx_with_inspector<I, F, T>(
3244        &self,
3245        hash: B256,
3246        mut inspector: I,
3247        f: F,
3248    ) -> Result<T, BlockchainError>
3249    where
3250        for<'a> I: BackendInspector<WrapDatabaseRef<&'a CacheDB<Box<&'a StateDb>>>> + 'a,
3251        for<'a> F:
3252            FnOnce(ResultAndState<HaltReason>, CacheDB<Box<&'a StateDb>>, I, TxEnv, EvmEnv) -> T,
3253    {
3254        let block = {
3255            let storage = self.blockchain.storage.read();
3256            let MinedTransaction { block_hash, .. } = storage
3257                .transactions
3258                .get(&hash)
3259                .cloned()
3260                .ok_or(BlockchainError::TransactionNotFound)?;
3261
3262            storage.blocks.get(&block_hash).cloned().ok_or(BlockchainError::BlockNotFound)?
3263        };
3264
3265        let index = block
3266            .body
3267            .transactions
3268            .iter()
3269            .position(|tx| tx.hash() == hash)
3270            .expect("transaction not found in block");
3271
3272        let pool_txs: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>> = block.body.transactions
3273            [..index]
3274            .iter()
3275            .map(|tx| {
3276                let pending_tx =
3277                    PendingTransaction::from_maybe_impersonated(tx.clone()).expect("is valid");
3278                Arc::new(PoolTransaction {
3279                    pending_transaction: pending_tx,
3280                    requires: vec![],
3281                    provides: vec![],
3282                    priority: crate::eth::pool::transactions::TransactionPriority(0),
3283                })
3284            })
3285            .collect();
3286
3287        let trace = |parent_state: &StateDb| -> Result<T, BlockchainError> {
3288            let mut cache_db = AnvilCacheDB::new(Box::new(parent_state));
3289
3290            // configure the blockenv for the block of the transaction
3291            let mut evm_env = self.evm_env.read().clone();
3292
3293            evm_env.block_env = block_env_from_header(&block.header);
3294
3295            let spec_id = *evm_env.spec_id();
3296
3297            let inspector_tx_config = self.inspector_tx_config();
3298            let gas_config = self.pool_tx_gas_config(&evm_env);
3299
3300            self.execute_with_block_executor(
3301                &mut cache_db,
3302                &evm_env,
3303                block.header.parent_hash,
3304                spec_id,
3305                &pool_txs,
3306                &gas_config,
3307                &inspector_tx_config,
3308                &|pending, account| self.validate_pool_transaction_for(pending, account, &evm_env),
3309            );
3310
3311            // Extract inner CacheDB to match the expected types for the target tx execution
3312            let cache_db = cache_db.0;
3313
3314            let target_tx = block.body.transactions[index].clone();
3315            let target_tx = PendingTransaction::from_maybe_impersonated(target_tx)?;
3316            let (result, base_tx_env) = self.transact_envelope_with_inspector_ref(
3317                &cache_db,
3318                &evm_env,
3319                &mut inspector,
3320                target_tx.transaction.as_ref(),
3321                *target_tx.sender(),
3322            )?;
3323
3324            Ok(f(result, cache_db, inspector, base_tx_env, evm_env))
3325        };
3326
3327        let read_guard = self.states.upgradable_read();
3328        if let Some(state) = read_guard.get_state(&block.header.parent_hash) {
3329            trace(state)
3330        } else {
3331            let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
3332            let state = write_guard
3333                .get_on_disk_state(&block.header.parent_hash)
3334                .ok_or(BlockchainError::BlockNotFound)?;
3335            trace(state)
3336        }
3337    }
3338
3339    /// Traces the transaction with the js tracer
3340    #[cfg(feature = "js-tracer")]
3341    pub async fn trace_tx_with_js_tracer(
3342        &self,
3343        hash: B256,
3344        code: String,
3345        opts: GethDebugTracingOptions,
3346    ) -> Result<GethTrace, BlockchainError> {
3347        let GethDebugTracingOptions { tracer_config, .. } = opts;
3348        let config = tracer_config.into_json();
3349        let inspector = revm_inspectors::tracing::js::JsInspector::new(code, config)
3350            .map_err(|err| BlockchainError::Message(err.to_string()))?;
3351        let trace = self.replay_tx_with_inspector(
3352            hash,
3353            inspector,
3354            |result, cache_db, mut inspector, tx_env, evm_env| {
3355                inspector
3356                    .json_result(
3357                        result,
3358                        &alloy_evm::IntoTxEnv::into_tx_env(tx_env),
3359                        &evm_env.block_env,
3360                        &cache_db,
3361                    )
3362                    .map_err(|e| BlockchainError::Message(e.to_string()))
3363            },
3364        )??;
3365        Ok(GethTrace::JS(trace))
3366    }
3367
3368    /// Prove an account's existence or nonexistence in the state trie.
3369    ///
3370    /// Returns a merkle proof of the account's trie node, `account_key` == keccak(address)
3371    pub async fn prove_account_at(
3372        &self,
3373        address: Address,
3374        keys: Vec<B256>,
3375        block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3376    ) -> Result<AccountProof, BlockchainError> {
3377        let block_number = block_request.as_ref().map(|r| r.block_number());
3378
3379        self.with_database_at(block_request, |block_db, _| {
3380            trace!(target: "backend", "get proof for {:?} at {:?}", address, block_number);
3381            let db = block_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?;
3382            let account = db.get(&address).cloned().unwrap_or_default();
3383
3384            let mut builder = HashBuilder::default()
3385                .with_proof_retainer(ProofRetainer::new(vec![Nibbles::unpack(keccak256(address))]));
3386
3387            for (key, account) in trie_accounts(db) {
3388                builder.add_leaf(key, &account);
3389            }
3390
3391            let _ = builder.root();
3392
3393            let proof = builder
3394                .take_proof_nodes()
3395                .into_nodes_sorted()
3396                .into_iter()
3397                .map(|(_, v)| v)
3398                .collect();
3399            let (storage_hash, storage_proofs) = prove_storage(&account.storage, &keys);
3400
3401            let account_proof = AccountProof {
3402                address,
3403                balance: account.info.balance,
3404                nonce: account.info.nonce,
3405                code_hash: account.info.code_hash,
3406                storage_hash,
3407                account_proof: proof,
3408                storage_proof: keys
3409                    .into_iter()
3410                    .zip(storage_proofs)
3411                    .map(|(key, proof)| {
3412                        let storage_key: U256 = key.into();
3413                        let value = account.storage.get(&storage_key).copied().unwrap_or_default();
3414                        StorageProof { key: JsonStorageKey::Hash(key), value, proof }
3415                    })
3416                    .collect(),
3417            };
3418
3419            Ok(account_proof)
3420        })
3421        .await?
3422    }
3423}
3424
3425impl<N: Network> Backend<N>
3426where
3427    N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
3428{
3429    /// Rollback the chain to a common height.
3430    ///
3431    /// The state of the chain is rewound using `rewind` to the common block, including the db,
3432    /// storage, and env.
3433    pub async fn rollback(&self, common_block: Block) -> Result<(), BlockchainError> {
3434        let hash = common_block.header.hash_slow();
3435
3436        // Get the database at the common block
3437        let common_state = {
3438            let return_state_or_throw_err =
3439                |db: Option<&StateDb>| -> Result<AddressMap<DbAccount>, BlockchainError> {
3440                    let state_db = db.ok_or(BlockchainError::DataUnavailable)?;
3441                    let db_full =
3442                        state_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?;
3443                    Ok(db_full.clone())
3444                };
3445
3446            let read_guard = self.states.upgradable_read();
3447            if let Some(db) = read_guard.get_state(&hash) {
3448                return_state_or_throw_err(Some(db))?
3449            } else {
3450                let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
3451                return_state_or_throw_err(write_guard.get_on_disk_state(&hash))?
3452            }
3453        };
3454
3455        {
3456            // Unwind the storage back to the common ancestor first
3457            let removed_blocks =
3458                self.blockchain.storage.write().unwind_to(common_block.header.number(), hash);
3459
3460            // Clean up in-memory and on-disk states for removed blocks
3461            let removed_hashes: Vec<_> =
3462                removed_blocks.iter().map(|b| b.header.hash_slow()).collect();
3463            self.states.write().remove_block_states(&removed_hashes);
3464
3465            // Set environment back to common block
3466            let mut env = self.evm_env.write();
3467            env.block_env.number = U256::from(common_block.header.number());
3468            env.block_env.timestamp = U256::from(common_block.header.timestamp());
3469            env.block_env.gas_limit = common_block.header.gas_limit();
3470            env.block_env.difficulty = common_block.header.difficulty();
3471            env.block_env.prevrandao = common_block.header.mix_hash();
3472
3473            self.time.reset(env.block_env.timestamp.saturating_to());
3474        }
3475
3476        {
3477            // Collect block hashes before acquiring db lock to avoid holding blockchain storage
3478            // lock across await. Only collect the last 256 blocks since that's all BLOCKHASH can
3479            // access.
3480            let block_hashes: Vec<_> = {
3481                let storage = self.blockchain.storage.read();
3482                let min_block = common_block.header.number().saturating_sub(256);
3483                storage
3484                    .hashes
3485                    .iter()
3486                    .filter(|(num, _)| **num >= min_block)
3487                    .map(|(&num, &hash)| (num, hash))
3488                    .collect()
3489            };
3490
3491            // Acquire db lock once for the entire restore operation to reduce lock churn.
3492            let mut db = self.db.write().await;
3493            db.clear();
3494
3495            // Insert account info before storage to prevent fork-mode RPC fetches after clear.
3496            for (address, acc) in common_state {
3497                db.insert_account(address, acc.info);
3498                for (key, value) in acc.storage {
3499                    db.set_storage_at(address, key.into(), value.into())?;
3500                }
3501            }
3502
3503            // Restore block hashes from blockchain storage (now unwound, contains only valid
3504            // blocks).
3505            for (block_num, hash) in block_hashes {
3506                db.insert_block_hash(U256::from(block_num), hash);
3507            }
3508        }
3509
3510        Ok(())
3511    }
3512
3513    /// Returns the traces for the given transaction
3514    pub async fn debug_trace_transaction(
3515        &self,
3516        hash: B256,
3517        opts: GethDebugTracingOptions,
3518    ) -> Result<GethTrace, BlockchainError> {
3519        #[cfg(feature = "js-tracer")]
3520        if let Some(tracer_type) = opts.tracer.as_ref()
3521            && tracer_type.is_js()
3522        {
3523            return self
3524                .trace_tx_with_js_tracer(hash, tracer_type.as_str().to_string(), opts.clone())
3525                .await;
3526        }
3527
3528        if let Some(trace) = self.mined_geth_trace_transaction(hash, opts.clone()).await {
3529            return trace;
3530        }
3531
3532        if let Some(fork) = self.get_fork() {
3533            return Ok(fork.debug_trace_transaction(hash, opts).await?);
3534        }
3535
3536        Ok(GethTrace::Default(Default::default()))
3537    }
3538
3539    /// Returns geth-style traces for all transactions in a block by hash.
3540    pub async fn debug_trace_block_by_hash(
3541        &self,
3542        block_hash: B256,
3543        opts: GethDebugTracingOptions,
3544    ) -> Result<Vec<TraceResult>, BlockchainError> {
3545        if let Some(block) = self.blockchain.get_block_by_hash(&block_hash) {
3546            let mut traces = Vec::new();
3547            for tx in &block.body.transactions {
3548                let tx_hash = tx.hash();
3549                match self.debug_trace_transaction(tx_hash, opts.clone()).await {
3550                    Ok(trace) => {
3551                        traces.push(TraceResult::Success { result: trace, tx_hash: Some(tx_hash) });
3552                    }
3553                    Err(error) => {
3554                        traces.push(TraceResult::Error {
3555                            error: error.to_string(),
3556                            tx_hash: Some(tx_hash),
3557                        });
3558                    }
3559                }
3560            }
3561            return Ok(traces);
3562        }
3563
3564        if let Some(fork) = self.get_fork() {
3565            return Ok(fork.debug_trace_block_by_hash(block_hash, opts).await?);
3566        }
3567
3568        Err(BlockchainError::BlockNotFound)
3569    }
3570
3571    /// Returns geth-style traces for all transactions in a block by number.
3572    pub async fn debug_trace_block_by_number(
3573        &self,
3574        block_number: BlockNumber,
3575        opts: GethDebugTracingOptions,
3576    ) -> Result<Vec<TraceResult>, BlockchainError> {
3577        let number = self.convert_block_number(Some(block_number));
3578
3579        if let Some(block) = self.get_block(BlockId::Number(BlockNumber::Number(number))) {
3580            let mut traces = Vec::new();
3581            for tx in &block.body.transactions {
3582                let tx_hash = tx.hash();
3583                match self.debug_trace_transaction(tx_hash, opts.clone()).await {
3584                    Ok(trace) => {
3585                        traces.push(TraceResult::Success { result: trace, tx_hash: Some(tx_hash) });
3586                    }
3587                    Err(error) => {
3588                        traces.push(TraceResult::Error {
3589                            error: error.to_string(),
3590                            tx_hash: Some(tx_hash),
3591                        });
3592                    }
3593                }
3594            }
3595            return Ok(traces);
3596        }
3597
3598        if let Some(fork) = self.get_fork() {
3599            return Ok(fork.debug_trace_block_by_number(number, opts).await?);
3600        }
3601
3602        Err(BlockchainError::BlockNotFound)
3603    }
3604
3605    fn geth_trace(
3606        &self,
3607        tx: &MinedTransaction<N>,
3608        opts: GethDebugTracingOptions,
3609    ) -> Result<GethTrace, BlockchainError> {
3610        let GethDebugTracingOptions { config, tracer, tracer_config, .. } = opts;
3611
3612        if let Some(tracer) = tracer {
3613            match tracer {
3614                GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
3615                    GethDebugBuiltInTracerType::FourByteTracer => {
3616                        let inspector = FourByteInspector::default();
3617                        let res = self.replay_tx_with_inspector(
3618                            tx.info.transaction_hash,
3619                            inspector,
3620                            |_, _, inspector, _, _| FourByteFrame::from(inspector).into(),
3621                        )?;
3622                        return Ok(res);
3623                    }
3624                    GethDebugBuiltInTracerType::CallTracer => {
3625                        return match tracer_config.into_call_config() {
3626                            Ok(call_config) => {
3627                                let inspector = TracingInspector::new(
3628                                    TracingInspectorConfig::from_geth_call_config(&call_config),
3629                                );
3630                                let frame = self.replay_tx_with_inspector(
3631                                    tx.info.transaction_hash,
3632                                    inspector,
3633                                    |_, _, inspector, _, _| {
3634                                        inspector
3635                                            .geth_builder()
3636                                            .geth_call_traces(
3637                                                call_config,
3638                                                tx.receipt.cumulative_gas_used(),
3639                                            )
3640                                            .into()
3641                                    },
3642                                )?;
3643                                Ok(frame)
3644                            }
3645                            Err(e) => Err(RpcError::invalid_params(e.to_string()).into()),
3646                        };
3647                    }
3648                    GethDebugBuiltInTracerType::PreStateTracer => {
3649                        return match tracer_config.into_pre_state_config() {
3650                            Ok(pre_state_config) => {
3651                                let inspector = TracingInspector::new(
3652                                    TracingInspectorConfig::from_geth_prestate_config(
3653                                        &pre_state_config,
3654                                    ),
3655                                );
3656                                let frame = self.replay_tx_with_inspector(
3657                                    tx.info.transaction_hash,
3658                                    inspector,
3659                                    |state, db, inspector, _, _| {
3660                                        inspector.geth_builder().geth_prestate_traces(
3661                                            &state,
3662                                            &pre_state_config,
3663                                            db,
3664                                        )
3665                                    },
3666                                )??;
3667                                Ok(frame.into())
3668                            }
3669                            Err(e) => Err(RpcError::invalid_params(e.to_string()).into()),
3670                        };
3671                    }
3672                    GethDebugBuiltInTracerType::NoopTracer
3673                    | GethDebugBuiltInTracerType::MuxTracer
3674                    | GethDebugBuiltInTracerType::Erc7562Tracer
3675                    | GethDebugBuiltInTracerType::FlatCallTracer => {}
3676                },
3677                GethDebugTracerType::JsTracer(_code) => {}
3678            }
3679
3680            return Ok(NoopFrame::default().into());
3681        }
3682
3683        // default structlog tracer
3684        Ok(GethTraceBuilder::new(tx.info.traces.clone())
3685            .geth_traces(
3686                tx.receipt.cumulative_gas_used(),
3687                tx.info.out.clone().unwrap_or_default(),
3688                config,
3689            )
3690            .into())
3691    }
3692
3693    async fn mined_geth_trace_transaction(
3694        &self,
3695        hash: B256,
3696        opts: GethDebugTracingOptions,
3697    ) -> Option<Result<GethTrace, BlockchainError>> {
3698        self.blockchain.storage.read().transactions.get(&hash).map(|tx| self.geth_trace(tx, opts))
3699    }
3700
3701    /// returns all receipts for the given transactions
3702    fn get_receipts(
3703        &self,
3704        tx_hashes: impl IntoIterator<Item = TxHash>,
3705    ) -> Vec<FoundryReceiptEnvelope> {
3706        let storage = self.blockchain.storage.read();
3707        let mut receipts = vec![];
3708
3709        for hash in tx_hashes {
3710            if let Some(tx) = storage.transactions.get(&hash) {
3711                receipts.push(tx.receipt.clone());
3712            }
3713        }
3714
3715        receipts
3716    }
3717
3718    pub async fn transaction_receipt(
3719        &self,
3720        hash: B256,
3721    ) -> Result<Option<FoundryTxReceipt>, BlockchainError> {
3722        if let Some(receipt) = self.mined_transaction_receipt(hash) {
3723            return Ok(Some(receipt.inner));
3724        }
3725
3726        if let Some(fork) = self.get_fork() {
3727            let receipt = fork.transaction_receipt(hash).await?;
3728            let number = self.convert_block_number(
3729                receipt.clone().and_then(|r| r.block_number()).map(BlockNumber::from),
3730            );
3731
3732            if fork.predates_fork_inclusive(number) {
3733                return Ok(receipt);
3734            }
3735        }
3736
3737        Ok(None)
3738    }
3739
3740    /// Returns all transaction receipts of the block
3741    pub fn mined_block_receipts(&self, id: impl Into<BlockId>) -> Option<Vec<FoundryTxReceipt>> {
3742        let mut receipts = Vec::new();
3743        let block = self.get_block(id)?;
3744
3745        for transaction in block.body.transactions {
3746            let receipt = self.mined_transaction_receipt(transaction.hash())?;
3747            receipts.push(receipt.inner);
3748        }
3749
3750        Some(receipts)
3751    }
3752
3753    /// Returns the transaction receipt for the given hash
3754    pub(crate) fn mined_transaction_receipt(
3755        &self,
3756        hash: B256,
3757    ) -> Option<MinedTransactionReceipt<FoundryNetwork>> {
3758        let MinedTransaction { info, receipt: tx_receipt, block_hash, .. } =
3759            self.blockchain.get_transaction_by_hash(&hash)?;
3760
3761        let index = info.transaction_index as usize;
3762        let block = self.blockchain.get_block_by_hash(&block_hash)?;
3763        let transaction = block.body.transactions[index].clone();
3764
3765        // Cancun specific
3766        let excess_blob_gas = block.header.excess_blob_gas();
3767        let blob_gas_price =
3768            alloy_eips::eip4844::calc_blob_gasprice(excess_blob_gas.unwrap_or_default());
3769        let blob_gas_used = transaction.blob_gas_used();
3770
3771        let effective_gas_price = transaction.effective_gas_price(block.header.base_fee_per_gas());
3772
3773        let receipts = self.get_receipts(block.body.transactions.iter().map(|tx| tx.hash()));
3774        let next_log_index = receipts[..index].iter().map(|r| r.logs().len()).sum::<usize>();
3775
3776        let tx_receipt = tx_receipt.convert_logs_rpc(
3777            BlockNumHash::new(block.header.number(), block_hash),
3778            block.header.timestamp(),
3779            info.transaction_hash,
3780            info.transaction_index,
3781            next_log_index,
3782        );
3783
3784        let receipt = TransactionReceipt {
3785            inner: tx_receipt,
3786            transaction_hash: info.transaction_hash,
3787            transaction_index: Some(info.transaction_index),
3788            block_number: Some(block.header.number()),
3789            gas_used: info.gas_used,
3790            contract_address: info.contract_address,
3791            effective_gas_price,
3792            block_hash: Some(block_hash),
3793            from: info.from,
3794            to: info.to,
3795            blob_gas_price: Some(blob_gas_price),
3796            blob_gas_used,
3797        };
3798
3799        // Include timestamp in receipt to avoid extra block lookups (e.g., in Otterscan API)
3800        let mut inner = FoundryTxReceipt::with_timestamp(receipt, block.header.timestamp());
3801        if self.is_tempo() {
3802            inner = inner.with_fee_payer(info.from);
3803        }
3804        Some(MinedTransactionReceipt { inner, out: info.out })
3805    }
3806
3807    /// Returns the blocks receipts for the given number
3808    pub async fn block_receipts(
3809        &self,
3810        number: BlockId,
3811    ) -> Result<Option<Vec<FoundryTxReceipt>>, BlockchainError> {
3812        if let Some(receipts) = self.mined_block_receipts(number) {
3813            return Ok(Some(receipts));
3814        }
3815
3816        if let Some(fork) = self.get_fork() {
3817            let number = match self.ensure_block_number(Some(number)).await {
3818                Err(_) => return Ok(None),
3819                Ok(n) => n,
3820            };
3821
3822            if fork.predates_fork_inclusive(number) {
3823                let receipts = fork.block_receipts(number).await?;
3824
3825                return Ok(receipts);
3826            }
3827        }
3828
3829        Ok(None)
3830    }
3831}
3832
3833impl<N: Network<ReceiptEnvelope = FoundryReceiptEnvelope>> Backend<N> {
3834    /// Get the current state.
3835    pub async fn serialized_state(
3836        &self,
3837        preserve_historical_states: bool,
3838    ) -> Result<SerializableState, BlockchainError> {
3839        let at = self.evm_env.read().block_env.clone();
3840        let best_number = self.blockchain.storage.read().best_number;
3841        let blocks = self.blockchain.storage.read().serialized_blocks();
3842        let transactions = self.blockchain.storage.read().serialized_transactions();
3843        let historical_states =
3844            preserve_historical_states.then(|| self.states.write().serialized_states());
3845
3846        let state = self.db.read().await.dump_state(
3847            at,
3848            best_number,
3849            blocks,
3850            transactions,
3851            historical_states,
3852        )?;
3853        state.ok_or_else(|| {
3854            RpcError::invalid_params("Dumping state not supported with the current configuration")
3855                .into()
3856        })
3857    }
3858
3859    /// Write all chain data to serialized bytes buffer
3860    pub async fn dump_state(
3861        &self,
3862        preserve_historical_states: bool,
3863    ) -> Result<Bytes, BlockchainError> {
3864        let state = self.serialized_state(preserve_historical_states).await?;
3865        let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
3866        encoder
3867            .write_all(&serde_json::to_vec(&state).unwrap_or_default())
3868            .map_err(|_| BlockchainError::DataUnavailable)?;
3869        Ok(encoder.finish().unwrap_or_default().into())
3870    }
3871
3872    /// Apply [SerializableState] data to the backend storage.
3873    pub async fn load_state(&self, state: SerializableState) -> Result<bool, BlockchainError> {
3874        // load the blocks and transactions into the storage atomically so concurrent readers
3875        // never observe blocks without their transactions
3876        {
3877            let mut storage = self.blockchain.storage.write();
3878            storage.load_blocks(state.blocks.clone());
3879            storage.load_transactions(state.transactions.clone());
3880        }
3881        // reset the block env
3882        if let Some(block) = state.block.clone() {
3883            self.evm_env.write().block_env = block.clone();
3884
3885            // Set the current best block number.
3886            // Defaults to block number for compatibility with existing state files.
3887            let fork_num_and_hash = self.get_fork().map(|f| (f.block_number(), f.block_hash()));
3888
3889            let best_number = state.best_block_number.unwrap_or(block.number.saturating_to());
3890            if let Some((number, hash)) = fork_num_and_hash {
3891                trace!(target: "backend", state_block_number=?best_number, fork_block_number=?number);
3892                // If the state.block_number is greater than the fork block number, set best number
3893                // to the state block number.
3894                // Ref: https://github.com/foundry-rs/foundry/issues/9539
3895                if best_number > number {
3896                    self.blockchain.storage.write().best_number = best_number;
3897                    let best_hash = self
3898                        .blockchain
3899                        .storage
3900                        .read()
3901                        .hash(best_number.into(), self.slots_in_an_epoch)
3902                        .ok_or_else(|| {
3903                            BlockchainError::RpcError(RpcError::internal_error_with(format!(
3904                                "Best hash not found for best number {best_number}",
3905                            )))
3906                        })?;
3907                    self.blockchain.storage.write().best_hash = best_hash;
3908                } else {
3909                    // If loading state file on a fork, set best number to the fork block number.
3910                    // Ref: https://github.com/foundry-rs/foundry/pull/9215#issue-2618681838
3911                    self.blockchain.storage.write().best_number = number;
3912                    self.blockchain.storage.write().best_hash = hash;
3913                }
3914            } else {
3915                self.blockchain.storage.write().best_number = best_number;
3916
3917                // Set the current best block hash;
3918                let best_hash = self
3919                    .blockchain
3920                    .storage
3921                    .read()
3922                    .hash(best_number.into(), self.slots_in_an_epoch)
3923                    .ok_or_else(|| {
3924                        BlockchainError::RpcError(RpcError::internal_error_with(format!(
3925                            "Best hash not found for best number {best_number}",
3926                        )))
3927                    })?;
3928
3929                self.blockchain.storage.write().best_hash = best_hash;
3930            }
3931        }
3932
3933        if let Some(latest) = state.blocks.iter().max_by_key(|b| b.header.number()) {
3934            let header = &latest.header;
3935            let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
3936                header.gas_used(),
3937                header.gas_limit(),
3938                header.base_fee_per_gas().unwrap_or_default(),
3939            );
3940            let next_block_excess_blob_gas = self.fees.get_next_block_blob_excess_gas(
3941                header.excess_blob_gas().unwrap_or_default(),
3942                header.blob_gas_used().unwrap_or_default(),
3943            );
3944
3945            // update next base fee
3946            self.fees.set_base_fee(next_block_base_fee);
3947
3948            self.fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(
3949                next_block_excess_blob_gas,
3950                get_blob_base_fee_update_fraction(
3951                    self.evm_env.read().cfg_env.chain_id,
3952                    header.timestamp,
3953                ),
3954            ));
3955        }
3956
3957        if !self.db.write().await.load_state(state.clone())? {
3958            return Err(RpcError::invalid_params(
3959                "Loading state not supported with the current configuration",
3960            )
3961            .into());
3962        }
3963
3964        if let Some(historical_states) = state.historical_states {
3965            self.states.write().load_states(historical_states);
3966        }
3967
3968        Ok(true)
3969    }
3970
3971    /// Deserialize and add all chain data to the backend storage
3972    pub async fn load_state_bytes(&self, buf: Bytes) -> Result<bool, BlockchainError> {
3973        let orig_buf = &buf.0[..];
3974        let mut decoder = GzDecoder::new(orig_buf);
3975        let mut decoded_data = Vec::new();
3976
3977        let state: SerializableState = serde_json::from_slice(if decoder.header().is_some() {
3978            decoder
3979                .read_to_end(decoded_data.as_mut())
3980                .map_err(|_| BlockchainError::FailedToDecodeStateDump)?;
3981            &decoded_data
3982        } else {
3983            &buf.0
3984        })
3985        .map_err(|_| BlockchainError::FailedToDecodeStateDump)?;
3986
3987        self.load_state(state).await
3988    }
3989}
3990
3991impl Backend<FoundryNetwork> {
3992    /// Simulates the payload by executing the calls in request.
3993    pub async fn simulate(
3994        &self,
3995        request: SimulatePayload,
3996        block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3997    ) -> Result<Vec<SimulatedBlock<AnyRpcBlock>>, BlockchainError> {
3998        self.with_database_at(block_request, |state, mut block_env| {
3999            let SimulatePayload {
4000                block_state_calls,
4001                trace_transfers,
4002                validation,
4003                return_full_transactions,
4004            } = request;
4005            let mut cache_db = CacheDB::new(state);
4006            let mut block_res = Vec::with_capacity(block_state_calls.len());
4007
4008            // execute the blocks
4009            for block in block_state_calls {
4010                let SimBlock { block_overrides, state_overrides, calls } = block;
4011                let mut call_res = Vec::with_capacity(calls.len());
4012                let mut log_index = 0;
4013                let mut gas_used = 0;
4014                let mut transactions = Vec::with_capacity(calls.len());
4015                let mut logs= Vec::new();
4016
4017                // apply state overrides before executing the transactions
4018                if let Some(state_overrides) = state_overrides {
4019                    apply_state_overrides(state_overrides, &mut cache_db)?;
4020                }
4021                if let Some(block_overrides) = block_overrides {
4022                    cache_db.apply_block_overrides(block_overrides, &mut block_env);
4023                }
4024
4025                // execute all calls in that block
4026                for (req_idx, request) in calls.into_iter().enumerate() {
4027                    let fee_details = FeeDetails::new(
4028                        request.gas_price,
4029                        request.max_fee_per_gas,
4030                        request.max_priority_fee_per_gas,
4031                        request.max_fee_per_blob_gas,
4032                    )?
4033                    .or_zero_fees();
4034
4035                    let (mut evm_env, tx_env, op_deposit) = self.build_call_env(
4036                        WithOtherFields::new(request.clone()),
4037                        fee_details,
4038                        block_env.clone(),
4039                    );
4040
4041                    // Always disable EIP-3607
4042                    evm_env.cfg_env.disable_eip3607 = true;
4043
4044                    if !validation {
4045                        evm_env.cfg_env.disable_base_fee = !validation;
4046                        evm_env.block_env.basefee = 0;
4047                    }
4048
4049                    let mut inspector = self.build_inspector();
4050
4051                    // transact
4052                    if trace_transfers {
4053                        inspector = inspector.with_transfers();
4054                    }
4055                    trace!(target: "backend", env=?evm_env, spec=?evm_env.spec_id(),"simulate evm env");
4056                    let ResultAndState { result, state } = self.transact_with_inspector_ref(
4057                        &cache_db,
4058                        &evm_env,
4059                        &mut inspector,
4060                        tx_env,
4061                        op_deposit,
4062                    )?;
4063                    trace!(target: "backend", ?result, ?request, "simulate call");
4064
4065                    inspector.print_logs();
4066                    if self.print_traces {
4067                        inspector.into_print_traces(self.call_trace_decoder.clone());
4068                    }
4069
4070                    // commit the transaction
4071                    cache_db.commit(state);
4072                    gas_used += result.tx_gas_used();
4073
4074                    // create the transaction from a request
4075                    let from = request.from.unwrap_or_default();
4076
4077                    let mut request = Into::<FoundryTransactionRequest>::into(WithOtherFields::new(request));
4078                    request.prep_for_submission();
4079
4080                    let typed_tx = request.build_unsigned().map_err(|e| BlockchainError::InvalidTransactionRequest(e.to_string()))?;
4081
4082                    let tx = build_impersonated(typed_tx);
4083                    let tx_hash = tx.hash();
4084                    let rpc_tx = transaction_build(
4085                        None,
4086                        MaybeImpersonatedTransaction::impersonated(tx, from),
4087                        None,
4088                        None,
4089                        Some(block_env.basefee),
4090                    );
4091                    transactions.push(rpc_tx);
4092
4093                    let return_data = result.output().cloned().unwrap_or_default();
4094                    let sim_res = SimCallResult {
4095                        return_data,
4096                        gas_used: result.tx_gas_used(),
4097                        max_used_gas: None,
4098                        status: result.is_success(),
4099                        error: result.is_success().not().then(|| {
4100                            alloy_rpc_types::simulate::SimulateError {
4101                                code: -3200,
4102                                message: "execution failed".to_string(),
4103                                data: None,
4104                            }
4105                        }),
4106                        logs: result.clone()
4107                            .into_logs()
4108                            .into_iter()
4109                            .enumerate()
4110                            .map(|(idx, log)| Log {
4111                                inner: log,
4112                                block_number: Some(block_env.number.saturating_to()),
4113                                block_timestamp: Some(block_env.timestamp.saturating_to()),
4114                                transaction_index: Some(req_idx as u64),
4115                                log_index: Some((idx + log_index) as u64),
4116                                removed: false,
4117
4118                                block_hash: None,
4119                                transaction_hash: Some(tx_hash),
4120                            })
4121                            .collect(),
4122                    };
4123                    logs.extend(sim_res.logs.iter().map(|log| log.inner.clone()));
4124                    log_index += sim_res.logs.len();
4125                    call_res.push(sim_res);
4126                }
4127
4128                let transactions_envelopes: Vec<AnyTxEnvelope> = transactions
4129                .iter()
4130                .map(|tx| AnyTxEnvelope::from(tx.clone()))
4131                .collect();
4132                let header = Header {
4133                    logs_bloom: logs_bloom(logs.iter()),
4134                    transactions_root: calculate_transaction_root(&transactions_envelopes),
4135                    receipts_root: calculate_receipt_root(&transactions_envelopes),
4136                    parent_hash: Default::default(),
4137                    beneficiary: block_env.beneficiary,
4138                    state_root: Default::default(),
4139                    difficulty: Default::default(),
4140                    number: block_env.number.saturating_to(),
4141                    gas_limit: block_env.gas_limit,
4142                    gas_used,
4143                    timestamp: block_env.timestamp.saturating_to(),
4144                    extra_data: Default::default(),
4145                    mix_hash: Default::default(),
4146                    nonce: Default::default(),
4147                    base_fee_per_gas: Some(block_env.basefee),
4148                    withdrawals_root: None,
4149                    blob_gas_used: None,
4150                    excess_blob_gas: None,
4151                    parent_beacon_block_root: None,
4152                    requests_hash: None,
4153                    ..Default::default()
4154                };
4155                let mut block = alloy_rpc_types::Block {
4156                    header: AnyRpcHeader {
4157                        hash: header.hash_slow(),
4158                        inner: header.into(),
4159                        total_difficulty: None,
4160                        size: None,
4161                    },
4162                    uncles: vec![],
4163                    transactions: BlockTransactions::Full(transactions),
4164                    withdrawals: None,
4165                };
4166
4167                if !return_full_transactions {
4168                    block.transactions.convert_to_hashes();
4169                }
4170
4171                for res in &mut call_res {
4172                    res.logs.iter_mut().for_each(|log| {
4173                        log.block_hash = Some(block.header.hash);
4174                    });
4175                }
4176
4177                let simulated_block = SimulatedBlock {
4178                    inner: AnyRpcBlock::new(WithOtherFields::new(block)),
4179                    calls: call_res,
4180                };
4181
4182                // update block env
4183                block_env.number += U256::from(1);
4184                block_env.timestamp += U256::from(12);
4185                block_env.basefee = simulated_block
4186                    .inner
4187                    .header
4188                    .next_block_base_fee(self.fees.base_fee_params())
4189                    .unwrap_or_default();
4190
4191                block_res.push(simulated_block);
4192            }
4193
4194            Ok(block_res)
4195        })
4196        .await?
4197    }
4198
4199    pub fn get_blob_by_tx_hash(&self, hash: B256) -> Result<Option<Vec<alloy_consensus::Blob>>> {
4200        // Try to get the mined transaction by hash
4201        if let Some(tx) = self.mined_transaction_by_hash(hash)
4202            && let Ok(typed_tx) = FoundryTxEnvelope::try_from(tx)
4203            && let Some(sidecar) = typed_tx.sidecar()
4204        {
4205            return Ok(Some(sidecar.sidecar.blobs().to_vec()));
4206        }
4207
4208        Ok(None)
4209    }
4210
4211    /// Sets the fee token for a user address (Tempo-only).
4212    pub async fn set_fee_token(&self, user: Address, token: Address) -> DatabaseResult<()> {
4213        let hardfork = self.hardfork();
4214        let chain_id = self.evm_env.read().cfg_env.chain_id;
4215        let timestamp = U256::from(self.evm_env.read().block_env.timestamp);
4216        let block_number: u64 = self.evm_env.read().block_env.number.to();
4217        let mut db = self.db.write().await;
4218        let mut storage = AnvilStorageProvider::new(
4219            &mut **db,
4220            chain_id,
4221            timestamp,
4222            block_number,
4223            hardfork.into(),
4224        );
4225        StorageCtx::enter(&mut storage, || {
4226            let mut fee_manager = TipFeeManager::new();
4227            fee_manager
4228                .set_user_token(user, IFeeManager::setUserTokenCall { token })
4229                .map_err(|e| DatabaseError::AnyRequest(Arc::new(eyre::eyre!("{e}"))))
4230        })
4231    }
4232
4233    /// Sets the fee token for a validator address (Tempo-only).
4234    pub async fn set_validator_fee_token(
4235        &self,
4236        validator: Address,
4237        token: Address,
4238    ) -> DatabaseResult<()> {
4239        let hardfork = self.hardfork();
4240        let chain_id = self.evm_env.read().cfg_env.chain_id;
4241        let timestamp = U256::from(self.evm_env.read().block_env.timestamp);
4242        let block_number: u64 = self.evm_env.read().block_env.number.to();
4243        let mut db = self.db.write().await;
4244        let mut storage = AnvilStorageProvider::new(
4245            &mut **db,
4246            chain_id,
4247            timestamp,
4248            block_number,
4249            hardfork.into(),
4250        );
4251        StorageCtx::enter(&mut storage, || {
4252            let mut fee_manager = TipFeeManager::new();
4253            // Use Address::ZERO as beneficiary so the check `sender != beneficiary` passes
4254            fee_manager
4255                .set_validator_token(
4256                    validator,
4257                    IFeeManager::setValidatorTokenCall { token },
4258                    Address::ZERO,
4259                )
4260                .map_err(|e| DatabaseError::AnyRequest(Arc::new(eyre::eyre!("{e}"))))
4261        })
4262    }
4263
4264    /// Mints FeeAMM liquidity for a token pair (Tempo-only).
4265    pub async fn set_fee_amm_liquidity(
4266        &self,
4267        user_token: Address,
4268        validator_token: Address,
4269        amount: U256,
4270    ) -> DatabaseResult<()> {
4271        let hardfork = self.hardfork();
4272        let chain_id = self.evm_env.read().cfg_env.chain_id;
4273        let timestamp = U256::from(self.evm_env.read().block_env.timestamp);
4274        let block_number: u64 = self.evm_env.read().block_env.number.to();
4275        let admin = Address::ZERO;
4276        let mut db = self.db.write().await;
4277        let mut storage = AnvilStorageProvider::new(
4278            &mut **db,
4279            chain_id,
4280            timestamp,
4281            block_number,
4282            hardfork.into(),
4283        );
4284        StorageCtx::enter(&mut storage, || {
4285            // Mint the required tokens to admin so it can provide liquidity.
4286            // grant_role_internal bypasses the caller check, matching genesis seeding.
4287            for &token_address in &[user_token, validator_token] {
4288                let mut token = TIP20Token::from_address(token_address)
4289                    .map_err(|e| DatabaseError::AnyRequest(Arc::new(eyre::eyre!("{e}"))))?;
4290                token
4291                    .grant_role_internal(admin, *ISSUER_ROLE)
4292                    .map_err(|e| DatabaseError::AnyRequest(Arc::new(eyre::eyre!("{e}"))))?;
4293                token
4294                    .mint(admin, ITIP20::mintCall { to: admin, amount })
4295                    .map_err(|e| DatabaseError::AnyRequest(Arc::new(eyre::eyre!("{e}"))))?;
4296            }
4297            let mut fee_manager = TipFeeManager::new();
4298            fee_manager
4299                .mint(admin, user_token, validator_token, amount, admin)
4300                .map_err(|e| DatabaseError::AnyRequest(Arc::new(eyre::eyre!("{e}"))))?;
4301            Ok(())
4302        })
4303    }
4304}
4305
4306/// Get max nonce from transaction pool by address.
4307fn get_pool_transactions_nonce(
4308    pool_transactions: &[Arc<PoolTransaction<FoundryTxEnvelope>>],
4309    address: Address,
4310) -> Option<u64> {
4311    if let Some(highest_nonce) = pool_transactions
4312        .iter()
4313        .filter(|tx| {
4314            *tx.pending_transaction.sender() == address
4315                && !tx.pending_transaction.transaction.as_ref().has_nonzero_tempo_nonce_key()
4316        })
4317        .map(|tx| tx.pending_transaction.nonce())
4318        .max()
4319    {
4320        let tx_count = highest_nonce.saturating_add(1);
4321        return Some(tx_count);
4322    }
4323    None
4324}
4325
4326#[async_trait::async_trait]
4327impl<N: Network> TransactionValidator<FoundryTxEnvelope> for Backend<N>
4328where
4329    N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
4330{
4331    async fn validate_pool_transaction(
4332        &self,
4333        tx: &PendingTransaction<FoundryTxEnvelope>,
4334    ) -> Result<(), BlockchainError> {
4335        let address = *tx.sender();
4336        let account = self.get_account(address).await?;
4337        let evm_env = self.next_evm_env();
4338
4339        // Tempo AA: validate time bounds and fee token balance (async checks)
4340        if let FoundryTxEnvelope::Tempo(aa_tx) = tx.transaction.as_ref() {
4341            let tempo_tx = aa_tx.tx();
4342            let current_time = evm_env.block_env.timestamp.saturating_to::<u64>();
4343
4344            // Reject if valid_before is expired or too close to current time (< 3 seconds)
4345            const AA_VALID_BEFORE_MIN_SECS: u64 = 3;
4346            if let Some(valid_before) = tempo_tx.valid_before.map(|v| v.get()) {
4347                let min_allowed = current_time.saturating_add(AA_VALID_BEFORE_MIN_SECS);
4348                if valid_before <= min_allowed {
4349                    return Err(InvalidTransactionError::TempoValidBeforeExpired {
4350                        valid_before,
4351                        min_allowed,
4352                    }
4353                    .into());
4354                }
4355            }
4356
4357            // Reject if valid_after is too far in the future (> 1 hour)
4358            const AA_VALID_AFTER_MAX_SECS: u64 = 3600;
4359            if let Some(valid_after) = tempo_tx.valid_after.map(|v| v.get()) {
4360                let max_allowed = current_time.saturating_add(AA_VALID_AFTER_MAX_SECS);
4361                if valid_after > max_allowed {
4362                    return Err(InvalidTransactionError::TempoValidAfterTooFar {
4363                        valid_after,
4364                        max_allowed,
4365                    }
4366                    .into());
4367                }
4368            }
4369
4370            // Fee token balance check
4371            let fee_payer = tempo_tx.recover_fee_payer(address).unwrap_or(address);
4372            let fee_token =
4373                tempo_tx.fee_token.unwrap_or(foundry_evm::core::tempo::PATH_USD_ADDRESS);
4374
4375            // gas_limit * max_fee_per_gas in wei, scaled to 6-decimal token units
4376            let required_wei =
4377                U256::from(tempo_tx.gas_limit).saturating_mul(U256::from(tempo_tx.max_fee_per_gas));
4378            let required = required_wei / U256::from(10u64.pow(12));
4379
4380            let balance = self.get_fee_token_balance(fee_token, fee_payer).await?;
4381            if balance < required {
4382                return Err(InvalidTransactionError::TempoInsufficientFeeTokenBalance {
4383                    balance,
4384                    required,
4385                }
4386                .into());
4387            }
4388        }
4389
4390        Ok(self.validate_pool_transaction_for(tx, &account, &evm_env)?)
4391    }
4392
4393    fn validate_pool_transaction_for(
4394        &self,
4395        pending: &PendingTransaction<FoundryTxEnvelope>,
4396        account: &AccountInfo,
4397        evm_env: &EvmEnv,
4398    ) -> Result<(), InvalidTransactionError> {
4399        let tx = &pending.transaction;
4400
4401        if let Some(tx_chain_id) = tx.chain_id() {
4402            let chain_id = self.chain_id();
4403            if chain_id.to::<u64>() != tx_chain_id {
4404                if let FoundryTxEnvelope::Legacy(tx) = tx.as_ref() {
4405                    // <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md>
4406                    if evm_env.cfg_env.spec >= SpecId::SPURIOUS_DRAGON && tx.chain_id().is_none() {
4407                        debug!(target: "backend", ?chain_id, ?tx_chain_id, "incompatible EIP155-based V");
4408                        return Err(InvalidTransactionError::IncompatibleEIP155);
4409                    }
4410                } else {
4411                    debug!(target: "backend", ?chain_id, ?tx_chain_id, "invalid chain id");
4412                    return Err(InvalidTransactionError::InvalidChainId);
4413                }
4414            }
4415        }
4416
4417        // Reject native value transfers on Tempo networks
4418        if self.is_tempo() && !tx.value().is_zero() {
4419            warn!(target: "backend", "[{:?}] native value transfer not allowed in Tempo mode", tx.hash());
4420            return Err(InvalidTransactionError::TempoNativeValueTransfer);
4421        }
4422
4423        // Tempo AA: cap authorization list size
4424        if let FoundryTxEnvelope::Tempo(aa_tx) = tx.as_ref() {
4425            const MAX_TEMPO_AUTHORIZATIONS: usize = 16;
4426            let auth_count = aa_tx.tx().tempo_authorization_list.len();
4427            if auth_count > MAX_TEMPO_AUTHORIZATIONS {
4428                warn!(target: "backend", "[{:?}] Tempo tx has too many authorizations: {}", tx.hash(), auth_count);
4429                return Err(InvalidTransactionError::TempoTooManyAuthorizations {
4430                    count: auth_count,
4431                    max: MAX_TEMPO_AUTHORIZATIONS,
4432                });
4433            }
4434        }
4435
4436        // Nonce validation — skip for deposits (L1→L2) and Tempo txs (2D nonce system)
4437        #[cfg(feature = "optimism")]
4438        let is_deposit_tx = pending.transaction.as_ref().is_deposit();
4439        #[cfg(not(feature = "optimism"))]
4440        let is_deposit_tx = false;
4441        let is_tempo_tx = pending.transaction.as_ref().is_tempo();
4442        let nonce = tx.nonce();
4443        if nonce < account.nonce && !is_deposit_tx && !is_tempo_tx {
4444            debug!(target: "backend", "[{:?}] nonce too low", tx.hash());
4445            return Err(InvalidTransactionError::NonceTooLow);
4446        }
4447
4448        // EIP-4844 structural validation
4449        if evm_env.cfg_env.spec >= SpecId::CANCUN && tx.is_eip4844() {
4450            // Heavy (blob validation) checks
4451            let blob_tx = match tx.as_ref() {
4452                FoundryTxEnvelope::Eip4844(tx) => tx.tx(),
4453                _ => unreachable!(),
4454            };
4455
4456            let blob_count = blob_tx.tx().blob_versioned_hashes.len();
4457
4458            // Ensure there are blob hashes.
4459            if blob_count == 0 {
4460                return Err(InvalidTransactionError::NoBlobHashes);
4461            }
4462
4463            // Ensure the tx does not exceed the max blobs per transaction.
4464            let max_blobs_per_tx = self.blob_params().max_blobs_per_tx as usize;
4465            if blob_count > max_blobs_per_tx {
4466                return Err(InvalidTransactionError::TooManyBlobs(blob_count, max_blobs_per_tx));
4467            }
4468
4469            // Check for any blob validation errors if not impersonating.
4470            if !self.skip_blob_validation(Some(*pending.sender()))
4471                && let Err(err) = blob_tx.validate(EnvKzgSettings::default().get())
4472            {
4473                return Err(InvalidTransactionError::BlobTransactionValidationError(err));
4474            }
4475        }
4476
4477        // EIP-3860 initcode size validation, respects --code-size-limit / --disable-code-size-limit
4478        if evm_env.cfg_env.spec >= SpecId::SHANGHAI && tx.kind() == TxKind::Create {
4479            let max_initcode_size = evm_env
4480                .cfg_env
4481                .limit_contract_code_size
4482                .map(|limit| limit.saturating_mul(2))
4483                .unwrap_or(revm::primitives::eip3860::MAX_INITCODE_SIZE);
4484            if tx.input().len() > max_initcode_size {
4485                return Err(InvalidTransactionError::MaxInitCodeSizeExceeded);
4486            }
4487        }
4488
4489        // Balance and fee related checks
4490        if !self.disable_pool_balance_checks {
4491            // Gas limit validation
4492            if tx.gas_limit() < MIN_TRANSACTION_GAS as u64 {
4493                debug!(target: "backend", "[{:?}] gas too low", tx.hash());
4494                return Err(InvalidTransactionError::GasTooLow);
4495            }
4496
4497            // Check tx gas limit against block gas limit, if block gas limit is set.
4498            if !evm_env.cfg_env.disable_block_gas_limit
4499                && tx.gas_limit() > evm_env.block_env.gas_limit
4500            {
4501                debug!(target: "backend", "[{:?}] gas too high", tx.hash());
4502                return Err(InvalidTransactionError::GasTooHigh(ErrDetail {
4503                    detail: String::from("tx.gas_limit > env.block.gas_limit"),
4504                }));
4505            }
4506
4507            // Check tx gas limit against tx gas limit cap (Osaka hard fork and later).
4508            if evm_env.cfg_env.tx_gas_limit_cap.is_none()
4509                && tx.gas_limit() > evm_env.cfg_env().tx_gas_limit_cap()
4510            {
4511                debug!(target: "backend", "[{:?}] gas too high", tx.hash());
4512                return Err(InvalidTransactionError::GasTooHigh(ErrDetail {
4513                    detail: String::from("tx.gas_limit > env.cfg.tx_gas_limit_cap"),
4514                }));
4515            }
4516
4517            // EIP-1559 fee validation (London hard fork and later).
4518            if evm_env.cfg_env.spec >= SpecId::LONDON {
4519                if tx.max_fee_per_gas() < evm_env.block_env.basefee.into() && !is_deposit_tx {
4520                    debug!(target: "backend", "max fee per gas={}, too low, block basefee={}", tx.max_fee_per_gas(), evm_env.block_env.basefee);
4521                    return Err(InvalidTransactionError::FeeCapTooLow);
4522                }
4523
4524                if let (Some(max_priority_fee_per_gas), max_fee_per_gas) =
4525                    (tx.as_ref().max_priority_fee_per_gas(), tx.as_ref().max_fee_per_gas())
4526                    && max_priority_fee_per_gas > max_fee_per_gas
4527                {
4528                    debug!(target: "backend", "max priority fee per gas={}, too high, max fee per gas={}", max_priority_fee_per_gas, max_fee_per_gas);
4529                    return Err(InvalidTransactionError::TipAboveFeeCap);
4530                }
4531            }
4532
4533            // EIP-4844 blob fee validation
4534            if evm_env.cfg_env.spec >= SpecId::CANCUN
4535                && tx.is_eip4844()
4536                && let Some(max_fee_per_blob_gas) = tx.max_fee_per_blob_gas()
4537                && let Some(blob_gas_and_price) = &evm_env.block_env.blob_excess_gas_and_price
4538                && max_fee_per_blob_gas < blob_gas_and_price.blob_gasprice
4539            {
4540                debug!(target: "backend", "max fee per blob gas={}, too low, block blob gas price={}", max_fee_per_blob_gas, blob_gas_and_price.blob_gasprice);
4541                return Err(InvalidTransactionError::BlobFeeCapTooLow(
4542                    max_fee_per_blob_gas,
4543                    blob_gas_and_price.blob_gasprice,
4544                ));
4545            }
4546
4547            let max_cost =
4548                (tx.gas_limit() as u128).saturating_mul(tx.max_fee_per_gas()).saturating_add(
4549                    tx.blob_gas_used()
4550                        .map(|g| g as u128)
4551                        .unwrap_or(0)
4552                        .mul(tx.max_fee_per_blob_gas().unwrap_or(0)),
4553                );
4554            let value = tx.value();
4555            match tx.as_ref() {
4556                #[cfg(feature = "optimism")]
4557                FoundryTxEnvelope::Deposit(deposit_tx) => {
4558                    // Deposit transactions
4559                    // https://specs.optimism.io/protocol/deposits.html#execution
4560                    // 1. no gas cost check required since already have prepaid gas from L1
4561                    // 2. increment account balance by deposited amount before checking for
4562                    //    sufficient funds `tx.value <= existing account value + deposited value`
4563                    if value > account.balance + U256::from(deposit_tx.mint) {
4564                        debug!(target: "backend", "[{:?}] insufficient balance={}, required={} account={:?}", tx.hash(), account.balance + U256::from(deposit_tx.mint), value, *pending.sender());
4565                        return Err(InvalidTransactionError::InsufficientFunds);
4566                    }
4567                }
4568                FoundryTxEnvelope::Tempo(_) => {
4569                    // Tempo AA transactions pay gas with fee tokens, not ETH.
4570                    // Fee token balance is validated in validate_pool_transaction (async).
4571                }
4572                _ => {
4573                    // check sufficient funds: `gas * price + value`
4574                    let req_funds =
4575                        max_cost.checked_add(value.saturating_to()).ok_or_else(|| {
4576                            debug!(target: "backend", "[{:?}] cost too high", tx.hash());
4577                            InvalidTransactionError::InsufficientFunds
4578                        })?;
4579                    if account.balance < U256::from(req_funds) {
4580                        debug!(target: "backend", "[{:?}] insufficient balance={}, required={} account={:?}", tx.hash(), account.balance, req_funds, *pending.sender());
4581                        return Err(InvalidTransactionError::InsufficientFunds);
4582                    }
4583                }
4584            }
4585        }
4586        Ok(())
4587    }
4588
4589    fn validate_for(
4590        &self,
4591        tx: &PendingTransaction<FoundryTxEnvelope>,
4592        account: &AccountInfo,
4593        evm_env: &EvmEnv,
4594    ) -> Result<(), InvalidTransactionError> {
4595        self.validate_pool_transaction_for(tx, account, evm_env)?;
4596        if tx.nonce() > account.nonce {
4597            return Err(InvalidTransactionError::NonceTooHigh);
4598        }
4599        Ok(())
4600    }
4601}
4602
4603/// Replaces the cached hash of a [`Signed`] transaction, preserving the inner tx and signature.
4604fn rehash<T>(signed: Signed<T>, hash: B256) -> Signed<T>
4605where
4606    T: alloy_consensus::transaction::RlpEcdsaEncodableTx,
4607{
4608    let (t, sig, _) = signed.into_parts();
4609    Signed::new_unchecked(t, sig, hash)
4610}
4611
4612/// Creates a `AnyRpcTransaction` as it's expected for the `eth` RPC api from storage data
4613pub fn transaction_build(
4614    tx_hash: Option<B256>,
4615    eth_transaction: MaybeImpersonatedTransaction<FoundryTxEnvelope>,
4616    block: Option<&Block>,
4617    info: Option<TransactionInfo>,
4618    base_fee: Option<u64>,
4619) -> AnyRpcTransaction {
4620    #[cfg(feature = "optimism")]
4621    if let FoundryTxEnvelope::Deposit(deposit_tx) = eth_transaction.as_ref() {
4622        let dep_tx = deposit_tx;
4623
4624        let ser = serde_json::to_value(dep_tx).expect("could not serialize TxDeposit");
4625        let maybe_deposit_fields = OtherFields::try_from(ser);
4626
4627        match maybe_deposit_fields {
4628            Ok(mut fields) => {
4629                // Add zeroed signature fields for backwards compatibility
4630                // https://specs.optimism.io/protocol/deposits.html#the-deposited-transaction-type
4631                fields.insert("v".to_string(), serde_json::to_value("0x0").unwrap());
4632                fields.insert("r".to_string(), serde_json::to_value(B256::ZERO).unwrap());
4633                fields.insert(String::from("s"), serde_json::to_value(B256::ZERO).unwrap());
4634                fields.insert(String::from("nonce"), serde_json::to_value("0x0").unwrap());
4635
4636                let inner = UnknownTypedTransaction {
4637                    ty: AnyTxType(DEPOSIT_TX_TYPE_ID),
4638                    fields,
4639                    memo: Default::default(),
4640                };
4641
4642                let envelope = AnyTxEnvelope::Unknown(UnknownTxEnvelope {
4643                    hash: eth_transaction.hash(),
4644                    inner,
4645                });
4646
4647                let tx = Transaction {
4648                    inner: Recovered::new_unchecked(envelope, deposit_tx.from),
4649                    block_hash: block
4650                        .as_ref()
4651                        .map(|block| B256::from(keccak256(alloy_rlp::encode(&block.header)))),
4652                    block_number: block.as_ref().map(|block| block.header.number()),
4653                    transaction_index: info.as_ref().map(|info| info.transaction_index),
4654                    effective_gas_price: None,
4655                    block_timestamp: block.as_ref().map(|block| block.header.timestamp()),
4656                };
4657
4658                return AnyRpcTransaction::from(WithOtherFields::new(tx));
4659            }
4660            Err(_) => {
4661                error!(target: "backend", "failed to serialize deposit transaction");
4662            }
4663        }
4664    }
4665
4666    if let FoundryTxEnvelope::Tempo(tempo_tx) = eth_transaction.as_ref() {
4667        let from = eth_transaction.recover().unwrap_or_default();
4668        let ser = serde_json::to_value(tempo_tx).expect("could not serialize Tempo transaction");
4669        let maybe_tempo_fields = OtherFields::try_from(ser);
4670
4671        match maybe_tempo_fields {
4672            Ok(fields) => {
4673                let inner = UnknownTypedTransaction {
4674                    ty: AnyTxType(TEMPO_TX_TYPE_ID),
4675                    fields,
4676                    memo: Default::default(),
4677                };
4678
4679                let envelope = AnyTxEnvelope::Unknown(UnknownTxEnvelope {
4680                    hash: eth_transaction.hash(),
4681                    inner,
4682                });
4683
4684                let tx = Transaction {
4685                    inner: Recovered::new_unchecked(envelope, from),
4686                    block_hash: block.as_ref().map(|block| block.header.hash_slow()),
4687                    block_number: block.as_ref().map(|block| block.header.number()),
4688                    transaction_index: info.as_ref().map(|info| info.transaction_index),
4689                    effective_gas_price: None,
4690                    block_timestamp: block.as_ref().map(|block| block.header.timestamp()),
4691                };
4692
4693                return AnyRpcTransaction::from(WithOtherFields::new(tx));
4694            }
4695            Err(_) => {
4696                error!(target: "backend", "failed to serialize tempo transaction");
4697            }
4698        }
4699    }
4700
4701    let from = eth_transaction.recover().unwrap_or_default();
4702    let effective_gas_price = eth_transaction.effective_gas_price(base_fee);
4703
4704    // if a specific hash was provided we update the transaction's hash
4705    // This is important for impersonated transactions since they all use the
4706    // `BYPASS_SIGNATURE` which would result in different hashes
4707    // Note: for impersonated transactions this only concerns pending transactions because
4708    // there's no `info` yet.
4709    let hash = tx_hash.unwrap_or_else(|| eth_transaction.hash());
4710
4711    let eth_envelope = FoundryTxEnvelope::from(eth_transaction)
4712        .try_into_eth()
4713        .expect("non-standard transactions are handled above");
4714
4715    let envelope = match eth_envelope {
4716        TxEnvelope::Legacy(s) => AnyTxEnvelope::Ethereum(TxEnvelope::Legacy(rehash(s, hash))),
4717        TxEnvelope::Eip1559(s) => AnyTxEnvelope::Ethereum(TxEnvelope::Eip1559(rehash(s, hash))),
4718        TxEnvelope::Eip2930(s) => AnyTxEnvelope::Ethereum(TxEnvelope::Eip2930(rehash(s, hash))),
4719        TxEnvelope::Eip4844(s) => AnyTxEnvelope::Ethereum(TxEnvelope::Eip4844(rehash(s, hash))),
4720        TxEnvelope::Eip7702(s) => AnyTxEnvelope::Ethereum(TxEnvelope::Eip7702(rehash(s, hash))),
4721    };
4722
4723    let tx = Transaction {
4724        inner: Recovered::new_unchecked(envelope, from),
4725        block_hash: block.as_ref().map(|block| block.header.hash_slow()),
4726        block_number: block.as_ref().map(|block| block.header.number()),
4727        transaction_index: info.as_ref().map(|info| info.transaction_index),
4728        // deprecated
4729        effective_gas_price: Some(effective_gas_price),
4730        block_timestamp: block.as_ref().map(|block| block.header.timestamp()),
4731    };
4732    AnyRpcTransaction::from(WithOtherFields::new(tx))
4733}
4734
4735/// Prove a storage key's existence or nonexistence in the account's storage trie.
4736///
4737/// `storage_key` is the hash of the desired storage key, meaning
4738/// this will only work correctly under a secure trie.
4739/// `storage_key` == keccak(key)
4740pub fn prove_storage(
4741    storage: &alloy_primitives::map::U256Map<U256>,
4742    keys: &[B256],
4743) -> (B256, Vec<Vec<Bytes>>) {
4744    let keys: Vec<_> = keys.iter().map(|key| Nibbles::unpack(keccak256(key))).collect();
4745
4746    let mut builder = HashBuilder::default().with_proof_retainer(ProofRetainer::new(keys.clone()));
4747
4748    for (key, value) in trie_storage(storage) {
4749        builder.add_leaf(key, &value);
4750    }
4751
4752    let root = builder.root();
4753
4754    let mut proofs = Vec::new();
4755    let all_proof_nodes = builder.take_proof_nodes();
4756
4757    for proof_key in keys {
4758        // Iterate over all proof nodes and find the matching ones.
4759        // The filtered results are guaranteed to be in order.
4760        let matching_proof_nodes =
4761            all_proof_nodes.matching_nodes_sorted(&proof_key).into_iter().map(|(_, node)| node);
4762        proofs.push(matching_proof_nodes.collect());
4763    }
4764
4765    (root, proofs)
4766}
4767
4768pub fn is_arbitrum(chain_id: u64) -> bool {
4769    if let Ok(chain) = NamedChain::try_from(chain_id) {
4770        return chain.is_arbitrum();
4771    }
4772    false
4773}
4774
4775/// Unpacks an [`ExecutionResult`] into its exit reason, gas used, output, and logs.
4776fn unpack_execution_result<H: IntoInstructionResult>(
4777    result: ExecutionResult<H>,
4778) -> (InstructionResult, u64, Option<Output>, Vec<revm::primitives::Log>) {
4779    match result {
4780        ExecutionResult::Success { reason, gas, output, logs, .. } => {
4781            (reason.into(), gas.tx_gas_used(), Some(output), logs)
4782        }
4783        ExecutionResult::Revert { gas, output, logs, .. } => {
4784            (InstructionResult::Revert, gas.tx_gas_used(), Some(Output::Call(output)), logs)
4785        }
4786        ExecutionResult::Halt { reason, gas, logs, .. } => {
4787            (reason.into_instruction_result(), gas.tx_gas_used(), None, logs)
4788        }
4789    }
4790}
4791
4792/// Converts a halt reason into an [`InstructionResult`].
4793///
4794/// Abstracts over network-specific halt reason types (`HaltReason`, `OpHaltReason`)
4795/// so that anvil code doesn't need to match on each variant directly.
4796pub use foundry_evm::core::evm::IntoInstructionResult;
4797
4798#[cfg(test)]
4799mod tests {
4800    use crate::{NodeConfig, spawn};
4801
4802    #[tokio::test]
4803    async fn test_deterministic_block_mining() {
4804        // Test that mine_block produces deterministic block hashes with same initial conditions
4805        let genesis_timestamp = 1743944919u64;
4806
4807        // Create two identical backends
4808        let config_a = NodeConfig::test().with_genesis_timestamp(genesis_timestamp.into());
4809        let config_b = NodeConfig::test().with_genesis_timestamp(genesis_timestamp.into());
4810
4811        let (api_a, _handle_a) = spawn(config_a).await;
4812        let (api_b, _handle_b) = spawn(config_b).await;
4813
4814        // Mine empty blocks (no transactions) on both backends
4815        let outcome_a_1 = api_a.backend.mine_block(vec![]).await;
4816        let outcome_b_1 = api_b.backend.mine_block(vec![]).await;
4817
4818        // Both should mine the same block number
4819        assert_eq!(outcome_a_1.block_number, outcome_b_1.block_number);
4820
4821        // Get the actual blocks to compare hashes
4822        let block_a_1 =
4823            api_a.block_by_number(outcome_a_1.block_number.into()).await.unwrap().unwrap();
4824        let block_b_1 =
4825            api_b.block_by_number(outcome_b_1.block_number.into()).await.unwrap().unwrap();
4826
4827        // The block hashes should be identical
4828        assert_eq!(
4829            block_a_1.header.hash, block_b_1.header.hash,
4830            "Block hashes should be deterministic. Got {} vs {}",
4831            block_a_1.header.hash, block_b_1.header.hash
4832        );
4833
4834        // Mine another block to ensure it remains deterministic
4835        let outcome_a_2 = api_a.backend.mine_block(vec![]).await;
4836        let outcome_b_2 = api_b.backend.mine_block(vec![]).await;
4837
4838        let block_a_2 =
4839            api_a.block_by_number(outcome_a_2.block_number.into()).await.unwrap().unwrap();
4840        let block_b_2 =
4841            api_b.block_by_number(outcome_b_2.block_number.into()).await.unwrap().unwrap();
4842
4843        assert_eq!(
4844            block_a_2.header.hash, block_b_2.header.hash,
4845            "Second block hashes should also be deterministic. Got {} vs {}",
4846            block_a_2.header.hash, block_b_2.header.hash
4847        );
4848
4849        // Ensure the blocks are different (sanity check)
4850        assert_ne!(
4851            block_a_1.header.hash, block_a_2.header.hash,
4852            "Different blocks should have different hashes"
4853        );
4854    }
4855}