Skip to main content

anvil/eth/backend/
fork.rs

1//! Support for forking off another client
2
3use crate::eth::{backend::db::Db, error::BlockchainError};
4use alloy_chains::NamedChain;
5use alloy_consensus::{BlockHeader, TrieAccount};
6use alloy_eips::eip2930::AccessListResult;
7use alloy_network::{
8    AnyNetwork, AnyRpcBlock, BlockResponse, Network, TransactionResponse,
9    primitives::HeaderResponse,
10};
11use alloy_primitives::{
12    Address, B256, Bytes, StorageValue, U256,
13    map::{FbHashMap, HashMap, HashSet},
14};
15use alloy_provider::{
16    Provider,
17    ext::{DebugApi, TraceApi},
18};
19use alloy_rpc_types::{
20    BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, EIP1186AccountProofResponse,
21    FeeHistory, Filter, Index, Log,
22    request::TransactionRequest,
23    simulate::{SimulatePayload, SimulatedBlock},
24    state::StateOverride,
25    trace::{
26        geth::{GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, TraceResult},
27        opcode::{BlockOpcodeGas, TransactionOpcodeGas},
28        parity::{
29            LocalizedTransactionTrace as Trace, TraceResults, TraceResultsWithTransactionHash,
30            TraceType,
31        },
32    },
33};
34use alloy_rpc_types_eth::{AccountInfo, Bundle, EthCallResponse, StateContext};
35use alloy_rpc_types_mev::{EthCallBundle, EthCallBundleResponse};
36use alloy_serde::WithOtherFields;
37use alloy_transport::TransportError;
38use foundry_common::provider::{ProviderBuilder, RetryProvider};
39use foundry_evm::hardfork::FoundryHardfork;
40use foundry_primitives::FoundryTxReceipt;
41use parking_lot::{
42    RawRwLock, RwLock,
43    lock_api::{RwLockReadGuard, RwLockWriteGuard},
44};
45use revm::context_interface::block::BlobExcessGasAndPrice;
46use std::{sync::Arc, time::Duration};
47use tokio::sync::RwLock as AsyncRwLock;
48
49/// Ensures the fork network can be executed by Anvil's EVM backend.
50pub(crate) fn ensure_fork_network_supported(chain_id: u64) -> Result<(), BlockchainError> {
51    if matches!(NamedChain::try_from(chain_id), Ok(NamedChain::ZkSync | NamedChain::ZkSyncTestnet))
52    {
53        return Err(BlockchainError::UnsupportedForkNetwork {
54            chain_id,
55            reason: "Anvil's EVM backend cannot execute native EraVM bytecode; use `anvil-zksync` for zkSync Era forks",
56        });
57    }
58    Ok(())
59}
60
61/// Represents a fork of a remote client
62///
63/// This type contains a subset of the [`EthApi`](crate::eth::EthApi) functions but will exclusively
64/// fetch the requested data from the remote client, if it wasn't already fetched.
65#[derive(Clone, Debug)]
66pub struct ClientFork<N: Network = AnyNetwork> {
67    /// Contains the cached data
68    pub storage: Arc<RwLock<ForkedStorage<N>>>,
69    /// contains the info how the fork is configured
70    // Wrapping this in a lock, ensures we can update this on the fly via additional custom RPC
71    // endpoints
72    pub config: Arc<RwLock<ClientForkConfig<N>>>,
73    /// This also holds a handle to the underlying database
74    pub database: Arc<AsyncRwLock<Box<dyn Db>>>,
75    /// The RPC URL associated with the state in the underlying database.
76    database_rpc_url: Arc<RwLock<Option<String>>>,
77}
78
79impl<N: Network> ClientFork<N> {
80    /// Creates a new instance of the fork
81    pub fn new(config: ClientForkConfig<N>, database: Arc<AsyncRwLock<Box<dyn Db>>>) -> Self {
82        let database_rpc_url = config.eth_rpc_url().map(ToOwned::to_owned);
83        Self {
84            storage: Default::default(),
85            config: Arc::new(RwLock::new(config)),
86            database,
87            database_rpc_url: Arc::new(RwLock::new(database_rpc_url)),
88        }
89    }
90
91    /// Removes all data cached from previous responses
92    pub fn clear_cached_storage(&self) {
93        self.storage.write().clear()
94    }
95
96    /// Returns true whether the block predates the fork
97    pub fn predates_fork(&self, block: u64) -> bool {
98        block < self.block_number()
99    }
100
101    /// Returns true whether the block predates the fork _or_ is the same block as the fork
102    pub fn predates_fork_inclusive(&self, block: u64) -> bool {
103        block <= self.block_number()
104    }
105
106    pub fn timestamp(&self) -> u64 {
107        self.config.read().timestamp
108    }
109
110    pub fn block_number(&self) -> u64 {
111        self.config.read().block_number
112    }
113
114    /// Returns the transaction hash we forked off of, if any.
115    pub fn transaction_hash(&self) -> Option<B256> {
116        self.config.read().transaction_hash
117    }
118
119    pub fn total_difficulty(&self) -> U256 {
120        self.config.read().total_difficulty
121    }
122
123    pub fn base_fee(&self) -> Option<u128> {
124        self.config.read().base_fee
125    }
126
127    pub fn block_hash(&self) -> B256 {
128        self.config.read().block_hash
129    }
130
131    pub fn eth_rpc_url(&self) -> Option<String> {
132        self.config.read().eth_rpc_url().map(|s| s.to_string())
133    }
134
135    pub(crate) fn database_rpc_url(&self) -> Option<String> {
136        self.database_rpc_url.read().clone()
137    }
138
139    pub(crate) fn set_database_rpc_url(&self, url: Option<String>) {
140        *self.database_rpc_url.write() = url;
141    }
142
143    pub fn chain_id(&self) -> u64 {
144        self.config.read().chain_id
145    }
146
147    fn provider(&self) -> Arc<RetryProvider<N>> {
148        self.config.read().provider.clone()
149    }
150
151    fn storage_read(&self) -> RwLockReadGuard<'_, RawRwLock, ForkedStorage<N>> {
152        self.storage.read()
153    }
154
155    fn storage_write(&self) -> RwLockWriteGuard<'_, RawRwLock, ForkedStorage<N>> {
156        self.storage.write()
157    }
158
159    /// Returns the fee history  `eth_feeHistory`
160    pub async fn fee_history(
161        &self,
162        block_count: u64,
163        newest_block: BlockNumber,
164        reward_percentiles: &[f64],
165    ) -> Result<FeeHistory, TransportError> {
166        self.provider().get_fee_history(block_count, newest_block, reward_percentiles).await
167    }
168
169    /// Sends `eth_getProof`
170    pub async fn get_proof(
171        &self,
172        address: Address,
173        keys: Vec<B256>,
174        block_number: Option<BlockId>,
175    ) -> Result<EIP1186AccountProofResponse, TransportError> {
176        self.provider().get_proof(address, keys).block_id(block_number.unwrap_or_default()).await
177    }
178
179    /// Sends `eth_getBlockAccessList`
180    pub async fn block_access_list(
181        &self,
182        block_id: BlockId,
183    ) -> Result<Option<serde_json::Value>, TransportError> {
184        self.provider().raw_request("eth_getBlockAccessList".into(), (block_id,)).await
185    }
186
187    /// Sends `eth_getBlockAccessListByBlockHash`
188    pub async fn block_access_list_by_hash(
189        &self,
190        block_hash: B256,
191    ) -> Result<Option<serde_json::Value>, TransportError> {
192        self.provider().raw_request("eth_getBlockAccessListByBlockHash".into(), (block_hash,)).await
193    }
194
195    /// Sends `eth_getBlockAccessListByBlockNumber`
196    pub async fn block_access_list_by_number(
197        &self,
198        block_number: BlockNumber,
199    ) -> Result<Option<serde_json::Value>, TransportError> {
200        self.provider()
201            .raw_request("eth_getBlockAccessListByBlockNumber".into(), (block_number,))
202            .await
203    }
204
205    /// Sends `eth_getBlockAccessListRaw`.
206    pub async fn block_access_list_raw(
207        &self,
208        block_id: BlockId,
209    ) -> Result<Option<Bytes>, TransportError> {
210        self.provider().raw_request("eth_getBlockAccessListRaw".into(), (block_id,)).await
211    }
212
213    pub async fn storage_at(
214        &self,
215        address: Address,
216        index: U256,
217        number: Option<BlockNumber>,
218    ) -> Result<StorageValue, TransportError> {
219        self.provider()
220            .get_storage_at(address, index)
221            .block_id(number.unwrap_or_default().into())
222            .await
223    }
224
225    pub async fn logs(&self, filter: &Filter) -> Result<Vec<Log>, TransportError> {
226        if let Some(logs) = self.storage_read().logs.get(filter).cloned() {
227            return Ok(logs);
228        }
229
230        let logs = self.provider().get_logs(filter).await?;
231
232        let mut storage = self.storage_write();
233        storage.logs.insert(filter.clone(), logs.clone());
234        Ok(logs)
235    }
236
237    pub async fn get_code(
238        &self,
239        address: Address,
240        blocknumber: u64,
241    ) -> Result<Bytes, TransportError> {
242        trace!(target: "backend::fork", "get_code={:?}", address);
243        if let Some(code) = self.storage_read().code_at.get(&(address, blocknumber)).cloned() {
244            return Ok(code);
245        }
246
247        let block_id = BlockId::number(blocknumber);
248
249        let code = self.provider().get_code_at(address).block_id(block_id).await?;
250
251        let mut storage = self.storage_write();
252        storage.code_at.insert((address, blocknumber), code.clone());
253
254        Ok(code)
255    }
256
257    pub async fn get_balance(
258        &self,
259        address: Address,
260        blocknumber: u64,
261    ) -> Result<U256, TransportError> {
262        trace!(target: "backend::fork", "get_balance={:?}", address);
263        self.provider().get_balance(address).block_id(blocknumber.into()).await
264    }
265
266    pub async fn get_nonce(&self, address: Address, block: u64) -> Result<u64, TransportError> {
267        trace!(target: "backend::fork", "get_nonce={:?}", address);
268        self.provider().get_transaction_count(address).block_id(block.into()).await
269    }
270
271    pub async fn get_account(
272        &self,
273        address: Address,
274        blocknumber: u64,
275    ) -> Result<TrieAccount, TransportError> {
276        trace!(target: "backend::fork", "get_account={:?}", address);
277        self.provider().get_account(address).block_id(blocknumber.into()).await
278    }
279
280    pub async fn trace_transaction(&self, hash: B256) -> Result<Vec<Trace>, TransportError> {
281        if let Some(traces) = self.storage_read().transaction_traces.get(&hash).cloned() {
282            return Ok(traces);
283        }
284
285        let traces = self.provider().trace_transaction(hash).await?.into_iter().collect::<Vec<_>>();
286
287        let mut storage = self.storage_write();
288        storage.transaction_traces.insert(hash, traces.clone());
289
290        Ok(traces)
291    }
292
293    pub async fn trace_transaction_opcode_gas(
294        &self,
295        hash: B256,
296    ) -> Result<Option<TransactionOpcodeGas>, TransportError> {
297        self.provider().raw_request("trace_transactionOpcodeGas".into(), (hash,)).await
298    }
299
300    /// Sends `trace_call`.
301    pub async fn trace_call(
302        &self,
303        request: WithOtherFields<TransactionRequest>,
304        trace_types: HashSet<TraceType>,
305        block: BlockId,
306    ) -> Result<TraceResults, TransportError> {
307        self.provider().raw_request("trace_call".into(), (request, trace_types, block)).await
308    }
309
310    /// Sends `trace_get`.
311    pub async fn trace_get(
312        &self,
313        hash: B256,
314        indices: Vec<Index>,
315    ) -> Result<Option<Trace>, TransportError> {
316        self.provider().raw_request("trace_get".into(), (hash, indices)).await
317    }
318
319    pub async fn debug_trace_transaction(
320        &self,
321        hash: B256,
322        opts: GethDebugTracingOptions,
323    ) -> Result<GethTrace, TransportError> {
324        if let Some(traces) = self.storage_read().geth_transaction_traces.get(&hash).cloned() {
325            return Ok(traces);
326        }
327
328        let trace = self.provider().debug_trace_transaction(hash, opts).await?;
329
330        let mut storage = self.storage_write();
331        storage.geth_transaction_traces.insert(hash, trace.clone());
332
333        Ok(trace)
334    }
335
336    pub async fn debug_trace_call(
337        &self,
338        request: WithOtherFields<TransactionRequest>,
339        block_id: BlockId,
340        opts: GethDebugTracingCallOptions,
341    ) -> Result<GethTrace, TransportError> {
342        self.provider().raw_request("debug_traceCall".into(), (request, block_id, opts)).await
343    }
344
345    pub async fn debug_code_by_hash(
346        &self,
347        code_hash: B256,
348        block_id: Option<BlockId>,
349    ) -> Result<Option<Bytes>, TransportError> {
350        self.provider().debug_code_by_hash(code_hash, block_id).await
351    }
352
353    pub async fn debug_account_info_at(
354        &self,
355        block_id: BlockId,
356        tx_index: Index,
357        address: Address,
358    ) -> Result<Option<AccountInfo>, TransportError> {
359        self.provider()
360            .raw_request("debug_accountInfoAt".into(), (block_id, tx_index, address))
361            .await
362    }
363
364    pub async fn debug_trace_block_by_hash(
365        &self,
366        block_hash: B256,
367        opts: GethDebugTracingOptions,
368    ) -> Result<Vec<TraceResult>, TransportError> {
369        if let Some(traces) = self.storage_read().geth_block_traces.get(&block_hash).cloned() {
370            return Ok(traces);
371        }
372
373        let trace_results = self.provider().debug_trace_block_by_hash(block_hash, opts).await?;
374
375        let mut storage = self.storage_write();
376        storage.geth_block_traces.insert(block_hash, trace_results.clone());
377
378        Ok(trace_results)
379    }
380
381    pub async fn debug_trace_block_by_number(
382        &self,
383        number: u64,
384        opts: GethDebugTracingOptions,
385    ) -> Result<Vec<TraceResult>, TransportError> {
386        if let Ok(Some(block)) = self.provider().get_block_by_number(number.into()).await {
387            let block_hash = block.header().hash();
388            return self.debug_trace_block_by_hash(block_hash, opts).await;
389        }
390
391        self.provider().debug_trace_block_by_number(number.into(), opts).await
392    }
393
394    pub async fn trace_block(&self, number: u64) -> Result<Vec<Trace>, TransportError> {
395        if let Some(traces) = self.storage_read().block_traces.get(&number).cloned() {
396            return Ok(traces);
397        }
398
399        let traces =
400            self.provider().trace_block(number.into()).await?.into_iter().collect::<Vec<_>>();
401
402        let mut storage = self.storage_write();
403        storage.block_traces.insert(number, traces.clone());
404
405        Ok(traces)
406    }
407
408    pub async fn trace_replay_block_transactions(
409        &self,
410        number: u64,
411        trace_types: HashSet<TraceType>,
412    ) -> Result<Vec<TraceResultsWithTransactionHash>, TransportError> {
413        // Forward to upstream provider for historical blocks. Use the typed trace API so the block
414        // and trace types are serialized in the format upstream providers expect.
415        self.provider()
416            .trace_replay_block_transactions(BlockId::number(number))
417            .trace_types(trace_types)
418            .await
419    }
420
421    pub async fn trace_replay_transaction(
422        &self,
423        hash: B256,
424        trace_types: HashSet<TraceType>,
425    ) -> Result<TraceResults, TransportError> {
426        self.provider().raw_request("trace_replayTransaction".into(), (hash, trace_types)).await
427    }
428
429    pub async fn trace_block_opcode_gas(
430        &self,
431        block_id: BlockId,
432    ) -> Result<Option<BlockOpcodeGas>, TransportError> {
433        self.provider().raw_request("trace_blockOpcodeGas".into(), (block_id,)).await
434    }
435
436    /// Reset the fork to a fresh forked state, and optionally update the fork config
437    pub async fn reset(
438        &self,
439        urls: Vec<String>,
440        block_number: impl Into<BlockId>,
441    ) -> Result<(), BlockchainError> {
442        let block_number = block_number.into();
443        self.prepare_reset(urls.clone(), block_number).await?;
444        {
445            self.database
446                .write()
447                .await
448                .maybe_reset(urls.clone(), block_number)
449                .map_err(BlockchainError::Internal)?;
450        }
451
452        let number = self.block_number();
453        let block_hash = self.block_hash();
454        self.database.write().await.insert_block_hash(U256::from(number), block_hash);
455
456        Ok(())
457    }
458
459    /// Updates the fork configuration for a reset without modifying the current database.
460    pub(crate) async fn prepare_reset(
461        &self,
462        urls: Vec<String>,
463        block_number: BlockId,
464    ) -> Result<(), BlockchainError> {
465        let (provider, source_chain_id) = if urls.is_empty() {
466            (self.provider(), None)
467        } else {
468            let config = self.config.read().clone();
469            let (provider, source_chain_id) = config.validated_provider_for_urls(&urls).await?;
470            (provider, Some(source_chain_id))
471        };
472        let block =
473            provider.get_block(block_number).await?.ok_or(BlockchainError::BlockNotFound)?;
474        let block_hash = block.header().hash();
475        let timestamp = block.header().timestamp();
476        let base_fee = block.header().base_fee_per_gas();
477        let total_difficulty = block.header().difficulty();
478
479        let number = block.header().number();
480        let mut config = self.config.write();
481        if let Some(source_chain_id) = source_chain_id {
482            if config.override_chain_id.is_none() {
483                config.chain_id = source_chain_id;
484            }
485            config.provider = provider;
486            config.fork_urls = urls;
487        }
488        config.update_block(
489            number,
490            block_hash,
491            timestamp,
492            base_fee.map(|g| g as u128),
493            total_difficulty,
494        );
495
496        self.clear_cached_storage();
497
498        Ok(())
499    }
500
501    /// Sends `eth_call`
502    pub async fn call(
503        &self,
504        request: &N::TransactionRequest,
505        block: Option<BlockNumber>,
506    ) -> Result<Bytes, TransportError> {
507        let block = block.unwrap_or(BlockNumber::Latest);
508        let res = self.provider().call(request.clone()).block(block.into()).await?;
509
510        Ok(res)
511    }
512
513    /// Sends `eth_call` with a network-specific request.
514    pub async fn call_raw(
515        &self,
516        request: &WithOtherFields<TransactionRequest>,
517        block: Option<BlockNumber>,
518    ) -> Result<Bytes, TransportError> {
519        self.provider()
520            .raw_request("eth_call".into(), (request, block.unwrap_or(BlockNumber::Latest)))
521            .await
522    }
523
524    /// Sends `eth_callMany`
525    pub async fn call_many(
526        &self,
527        bundles: Vec<Bundle<WithOtherFields<TransactionRequest>>>,
528        state_context: Option<StateContext>,
529        state_override: Option<StateOverride>,
530    ) -> Result<Vec<Vec<EthCallResponse>>, TransportError> {
531        self.provider()
532            .raw_request("eth_callMany".into(), (bundles, state_context, state_override))
533            .await
534    }
535
536    /// Sends `eth_callBundle`.
537    pub async fn call_bundle(
538        &self,
539        bundle: EthCallBundle,
540    ) -> Result<EthCallBundleResponse, TransportError> {
541        self.provider().raw_request("eth_callBundle".into(), (bundle,)).await
542    }
543
544    /// Sends `eth_simulateV1`
545    pub async fn simulate_v1(
546        &self,
547        request: &SimulatePayload<WithOtherFields<TransactionRequest>>,
548        block: Option<BlockId>,
549    ) -> Result<Vec<SimulatedBlock<N::BlockResponse>>, TransportError> {
550        self.provider().raw_request("eth_simulateV1".into(), (request, block)).await
551    }
552
553    /// Sends `eth_estimateGas`
554    pub async fn estimate_gas(
555        &self,
556        request: &N::TransactionRequest,
557        block: Option<BlockNumber>,
558    ) -> Result<u128, TransportError> {
559        let block = block.unwrap_or_default();
560        let res = self.provider().estimate_gas(request.clone()).block(block.into()).await?;
561
562        Ok(res as u128)
563    }
564
565    /// Sends `eth_estimateGas` with a network-specific request.
566    pub async fn estimate_gas_raw(
567        &self,
568        request: &WithOtherFields<TransactionRequest>,
569        block: Option<BlockNumber>,
570    ) -> Result<u128, TransportError> {
571        let gas: U256 = self
572            .provider()
573            .raw_request("eth_estimateGas".into(), (request, block.unwrap_or_default()))
574            .await?;
575        Ok(gas.saturating_to())
576    }
577
578    /// Sends `eth_createAccessList`
579    pub async fn create_access_list(
580        &self,
581        request: &N::TransactionRequest,
582        block: Option<BlockNumber>,
583    ) -> Result<AccessListResult, TransportError> {
584        self.provider().create_access_list(request).block_id(block.unwrap_or_default().into()).await
585    }
586
587    /// Sends `eth_createAccessList` with a network-specific request.
588    pub async fn create_access_list_raw(
589        &self,
590        request: &WithOtherFields<TransactionRequest>,
591        block: Option<BlockNumber>,
592    ) -> Result<AccessListResult, TransportError> {
593        self.provider()
594            .raw_request("eth_createAccessList".into(), (request, block.unwrap_or_default()))
595            .await
596    }
597
598    pub async fn transaction_by_block_number_and_index(
599        &self,
600        number: u64,
601        index: usize,
602    ) -> Result<Option<N::TransactionResponse>, TransportError> {
603        let block = self.block_by_number(number).await?;
604        self.transaction_at_block_index(block, index).await
605    }
606
607    pub async fn transaction_by_block_hash_and_index(
608        &self,
609        hash: B256,
610        index: usize,
611    ) -> Result<Option<N::TransactionResponse>, TransportError> {
612        let block = self.block_by_hash(hash).await?;
613        self.transaction_at_block_index(block, index).await
614    }
615
616    async fn transaction_at_block_index(
617        &self,
618        block: Option<N::BlockResponse>,
619        index: usize,
620    ) -> Result<Option<N::TransactionResponse>, TransportError> {
621        if let Some(block) = block {
622            match block.transactions() {
623                BlockTransactions::Full(txs) => {
624                    if let Some(tx) = txs.get(index) {
625                        return Ok(Some(tx.clone()));
626                    }
627                }
628                BlockTransactions::Hashes(hashes) => {
629                    if let Some(tx_hash) = hashes.get(index) {
630                        return self.transaction_by_hash(*tx_hash).await;
631                    }
632                }
633                BlockTransactions::Uncle => {}
634            }
635        }
636        Ok(None)
637    }
638
639    pub async fn transaction_by_hash(
640        &self,
641        hash: B256,
642    ) -> Result<Option<N::TransactionResponse>, TransportError> {
643        trace!(target: "backend::fork", "transaction_by_hash={:?}", hash);
644        if let tx @ Some(_) = self.storage_read().transactions.get(&hash).cloned() {
645            return Ok(tx);
646        }
647
648        let tx = self.provider().get_transaction_by_hash(hash).await?;
649        if let Some(tx) = tx.clone() {
650            let mut storage = self.storage_write();
651            storage.transactions.insert(hash, tx);
652        }
653        Ok(tx)
654    }
655
656    pub async fn block_by_hash(
657        &self,
658        hash: B256,
659    ) -> Result<Option<N::BlockResponse>, TransportError> {
660        if let Some(mut block) = self.storage_read().blocks.get(&hash).cloned() {
661            block.transactions_mut().convert_to_hashes();
662            return Ok(Some(block));
663        }
664
665        Ok(self.fetch_full_block(hash).await?.map(|mut b| {
666            b.transactions_mut().convert_to_hashes();
667            b
668        }))
669    }
670
671    pub async fn block_by_hash_full(
672        &self,
673        hash: B256,
674    ) -> Result<Option<N::BlockResponse>, TransportError> {
675        if let Some(block) = self.storage_read().blocks.get(&hash).cloned()
676            && let Some(block) = self.convert_to_full_block(block)
677        {
678            return Ok(Some(block));
679        }
680        self.fetch_full_block(hash).await
681    }
682
683    pub async fn block_by_number(
684        &self,
685        block_number: u64,
686    ) -> Result<Option<N::BlockResponse>, TransportError> {
687        if let Some(mut block) = self
688            .storage_read()
689            .hashes
690            .get(&block_number)
691            .and_then(|hash| self.storage_read().blocks.get(hash).cloned())
692        {
693            block.transactions_mut().convert_to_hashes();
694            return Ok(Some(block));
695        }
696
697        let mut block = self.fetch_full_block(block_number).await?;
698        if let Some(block) = &mut block {
699            block.transactions_mut().convert_to_hashes();
700        }
701        Ok(block)
702    }
703
704    pub async fn block_by_number_full(
705        &self,
706        block_number: u64,
707    ) -> Result<Option<N::BlockResponse>, TransportError> {
708        if let Some(block) = self
709            .storage_read()
710            .hashes
711            .get(&block_number)
712            .copied()
713            .and_then(|hash| self.storage_read().blocks.get(&hash).cloned())
714            && let Some(block) = self.convert_to_full_block(block)
715        {
716            return Ok(Some(block));
717        }
718
719        self.fetch_full_block(block_number).await
720    }
721
722    /// Fetches a block selected by its original identifier directly from the fork provider.
723    pub async fn fetch_block(
724        &self,
725        block_id: BlockId,
726    ) -> Result<Option<N::BlockResponse>, TransportError> {
727        self.fetch_full_block(block_id).await
728    }
729
730    async fn fetch_full_block(
731        &self,
732        block_id: impl Into<BlockId>,
733    ) -> Result<Option<N::BlockResponse>, TransportError> {
734        if let Some(block) = self.provider().get_block(block_id.into()).full().await? {
735            let hash = block.header().hash();
736            let block_number = block.header().number();
737            let mut storage = self.storage_write();
738            // also insert all transactions
739            let block_txs = match block.transactions() {
740                BlockTransactions::Full(txs) => txs.to_owned(),
741                _ => vec![],
742            };
743            storage.transactions.extend(block_txs.iter().map(|tx| (tx.tx_hash(), tx.clone())));
744            storage.hashes.insert(block_number, hash);
745            storage.blocks.insert(hash, block.clone());
746            return Ok(Some(block));
747        }
748
749        Ok(None)
750    }
751
752    /// Converts a block of hashes into a full block
753    fn convert_to_full_block(&self, mut block: N::BlockResponse) -> Option<N::BlockResponse> {
754        let storage = self.storage.read();
755        let transactions = block
756            .transactions()
757            .hashes()
758            .map(|hash| storage.transactions.get(&hash).cloned())
759            .collect::<Option<Vec<_>>>()?;
760        *block.transactions_mut() = BlockTransactions::Full(transactions);
761        Some(block)
762    }
763}
764
765impl ClientFork {
766    pub async fn transaction_receipt(
767        &self,
768        hash: B256,
769    ) -> Result<Option<FoundryTxReceipt>, BlockchainError> {
770        if let Some(receipt) = self.storage_read().transaction_receipts.get(&hash).cloned() {
771            return Ok(Some(receipt));
772        }
773
774        if let Some(receipt) = self.provider().get_transaction_receipt(hash).await? {
775            let receipt = FoundryTxReceipt::try_from(receipt)
776                .map_err(|_| BlockchainError::FailedToDecodeReceipt)?;
777            let mut storage = self.storage_write();
778            storage.transaction_receipts.insert(hash, receipt.clone());
779            return Ok(Some(receipt));
780        }
781
782        Ok(None)
783    }
784
785    pub async fn block_receipts(
786        &self,
787        number: u64,
788    ) -> Result<Option<Vec<FoundryTxReceipt>>, BlockchainError> {
789        if let receipts @ Some(_) = self.storage_read().block_receipts.get(&number).cloned() {
790            return Ok(receipts);
791        }
792
793        // TODO Needs to be removed.
794        // Since alloy doesn't indicate in the result whether the block exists,
795        // this is being temporarily implemented in anvil.
796        if self.predates_fork_inclusive(number) {
797            let receipts = self.provider().get_block_receipts(BlockId::from(number)).await?;
798            let receipts = receipts
799                .map(|r| {
800                    r.into_iter()
801                        .map(|r| {
802                            FoundryTxReceipt::try_from(r)
803                                .map_err(|_| BlockchainError::FailedToDecodeReceipt)
804                        })
805                        .collect::<Result<Vec<_>, _>>()
806                })
807                .transpose()?;
808
809            if let Some(receipts) = receipts.clone() {
810                let mut storage = self.storage_write();
811                storage.block_receipts.insert(number, receipts);
812            }
813
814            return Ok(receipts);
815        }
816
817        Ok(None)
818    }
819
820    pub async fn uncle_by_block_hash_and_index(
821        &self,
822        hash: B256,
823        index: usize,
824    ) -> Result<Option<AnyRpcBlock>, TransportError> {
825        if let Some(block) = self.block_by_hash(hash).await? {
826            return self.uncles_by_block_and_index(block, index).await;
827        }
828        Ok(None)
829    }
830
831    pub async fn uncle_by_block_number_and_index(
832        &self,
833        number: u64,
834        index: usize,
835    ) -> Result<Option<AnyRpcBlock>, TransportError> {
836        if let Some(block) = self.block_by_number(number).await? {
837            return self.uncles_by_block_and_index(block, index).await;
838        }
839        Ok(None)
840    }
841
842    async fn uncles_by_block_and_index(
843        &self,
844        block: AnyRpcBlock,
845        index: usize,
846    ) -> Result<Option<AnyRpcBlock>, TransportError> {
847        let block_hash = block.header().hash();
848        let block_number = block.header().number();
849        if let Some(uncles) = self.storage_read().uncles.get(&block_hash) {
850            return Ok(uncles.get(index).cloned());
851        }
852
853        let mut uncles = Vec::with_capacity(block.uncles.len());
854        for (uncle_idx, _) in block.uncles.iter().enumerate() {
855            let uncle =
856                match self.provider().get_uncle(block_number.into(), uncle_idx as u64).await? {
857                    Some(u) => u,
858                    None => return Ok(None),
859                };
860            uncles.push(uncle);
861        }
862        self.storage_write().uncles.insert(block_hash, uncles.clone());
863        Ok(uncles.get(index).cloned())
864    }
865}
866
867/// Contains all fork metadata
868#[derive(Clone, Debug)]
869pub struct ClientForkConfig<N: Network = AnyNetwork> {
870    /// All fork URLs. The first entry is the primary endpoint.
871    /// When multiple URLs are present, requests are distributed using
872    /// round-robin load balancing with retry-based failover.
873    pub fork_urls: Vec<String>,
874    /// The block number of the forked block
875    pub block_number: u64,
876    /// The hash of the forked block
877    pub block_hash: B256,
878    /// The transaction hash we forked off of, if any.
879    pub transaction_hash: Option<B256>,
880    pub provider: Arc<RetryProvider<N>>,
881    pub chain_id: u64,
882    pub override_chain_id: Option<u64>,
883    /// The hardfork resolved for the forked block, if known.
884    pub hardfork: Option<FoundryHardfork>,
885    /// The timestamp for the forked block
886    pub timestamp: u64,
887    /// The basefee of the forked block
888    pub base_fee: Option<u128>,
889    /// Blob gas used of the forked block
890    pub blob_gas_used: Option<u128>,
891    /// Blob excess gas and price of the forked block
892    pub blob_excess_gas_and_price: Option<BlobExcessGasAndPrice>,
893    /// request timeout
894    pub timeout: Duration,
895    /// request retries for spurious networks
896    pub retries: u32,
897    /// request retries for spurious networks
898    pub backoff: Duration,
899    /// available CUPS
900    pub compute_units_per_second: u64,
901    /// Headers to include with RPC requests
902    pub headers: Vec<String>,
903    /// total difficulty of the chain until this block
904    pub total_difficulty: U256,
905}
906
907impl<N: Network> ClientForkConfig<N> {
908    /// Returns the primary RPC URL (first entry in `fork_urls`).
909    pub fn eth_rpc_url(&self) -> Option<&str> {
910        self.fork_urls.first().map(|s| s.as_str())
911    }
912
913    /// Builds a provider for the given URLs without changing the active fork configuration.
914    ///
915    /// # Errors
916    ///
917    /// This will fail if no new provider could be established (erroneous URL)
918    pub(crate) fn provider_for_urls(
919        &self,
920        urls: &[String],
921    ) -> Result<Arc<RetryProvider<N>>, BlockchainError> {
922        let primary = urls.first().ok_or_else(|| {
923            BlockchainError::InvalidUrl("at least one fork URL required".to_string())
924        })?;
925
926        let builder = ProviderBuilder::<N>::new(primary.as_str())
927            .timeout(self.timeout)
928            .max_retry(self.retries)
929            .initial_backoff(self.backoff.as_millis() as u64)
930            .compute_units_per_second(self.compute_units_per_second)
931            .headers(self.headers.clone());
932
933        let provider = if urls.len() > 1 {
934            builder
935                .build_fallback(urls.to_vec())
936                .map_err(|e| BlockchainError::InvalidUrl(format!("{primary}: {e}")))?
937        } else {
938            builder.build().map_err(|e| BlockchainError::InvalidUrl(format!("{primary}: {e}")))?
939        };
940        Ok(Arc::new(provider))
941    }
942
943    /// Builds a provider after validating that every URL belongs to the same supported network.
944    pub(crate) async fn validated_provider_for_urls(
945        &self,
946        urls: &[String],
947    ) -> Result<(Arc<RetryProvider<N>>, u64), BlockchainError> {
948        let mut source_chain_id = None;
949        for url in urls {
950            let provider = self.provider_for_urls(std::slice::from_ref(url))?;
951            let chain_id = provider.get_chain_id().await?;
952            ensure_fork_network_supported(chain_id)?;
953            if let Some(source_chain_id) = source_chain_id
954                && chain_id != source_chain_id
955            {
956                return Err(BlockchainError::UnsupportedForkNetwork {
957                    chain_id,
958                    reason: "fork endpoints must use the same chain ID",
959                });
960            }
961            source_chain_id = Some(chain_id);
962        }
963
964        let source_chain_id = source_chain_id.ok_or_else(|| {
965            BlockchainError::InvalidUrl("at least one fork URL required".to_string())
966        })?;
967        Ok((self.provider_for_urls(urls)?, source_chain_id))
968    }
969
970    /// Updates the block forked off `(block number, block hash, timestamp)`
971    pub fn update_block(
972        &mut self,
973        block_number: u64,
974        block_hash: B256,
975        timestamp: u64,
976        base_fee: Option<u128>,
977        total_difficulty: U256,
978    ) {
979        self.block_number = block_number;
980        self.block_hash = block_hash;
981        self.timestamp = timestamp;
982        self.base_fee = base_fee;
983        self.total_difficulty = total_difficulty;
984        trace!(target: "fork", "Updated block number={} hash={:?}", block_number, block_hash);
985    }
986}
987
988/// Contains cached state fetched to serve EthApi requests
989///
990/// This is used as a cache so repeated requests to the same data are not sent to the remote client
991#[derive(Clone, Debug)]
992pub struct ForkedStorage<N: Network = AnyNetwork> {
993    pub uncles: FbHashMap<32, Vec<N::BlockResponse>>,
994    pub blocks: FbHashMap<32, N::BlockResponse>,
995    pub hashes: HashMap<u64, B256>,
996    pub transactions: FbHashMap<32, N::TransactionResponse>,
997    pub transaction_receipts: FbHashMap<32, FoundryTxReceipt>,
998    pub transaction_traces: FbHashMap<32, Vec<Trace>>,
999    pub logs: HashMap<Filter, Vec<Log>>,
1000    pub geth_transaction_traces: FbHashMap<32, GethTrace>,
1001    pub geth_block_traces: FbHashMap<32, Vec<TraceResult>>,
1002    pub block_traces: HashMap<u64, Vec<Trace>>,
1003    pub block_receipts: HashMap<u64, Vec<FoundryTxReceipt>>,
1004    pub code_at: HashMap<(Address, u64), Bytes>,
1005}
1006
1007impl<N: Network> Default for ForkedStorage<N> {
1008    fn default() -> Self {
1009        Self {
1010            uncles: Default::default(),
1011            blocks: Default::default(),
1012            hashes: Default::default(),
1013            transactions: Default::default(),
1014            transaction_receipts: Default::default(),
1015            transaction_traces: Default::default(),
1016            logs: Default::default(),
1017            geth_transaction_traces: Default::default(),
1018            geth_block_traces: Default::default(),
1019            block_traces: Default::default(),
1020            block_receipts: Default::default(),
1021            code_at: Default::default(),
1022        }
1023    }
1024}
1025
1026impl<N: Network> ForkedStorage<N> {
1027    /// Clears all data
1028    pub fn clear(&mut self) {
1029        // simply replace with a completely new, empty instance
1030        *self = Self::default()
1031    }
1032}