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