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, pool::transactions::PoolTransaction};
4use alloy_consensus::{BlockHeader, TrieAccount};
5use alloy_eips::eip2930::AccessListResult;
6use alloy_network::{
7    AnyNetwork, AnyRpcBlock, BlockResponse, Network, TransactionResponse,
8    primitives::HeaderResponse,
9};
10use alloy_primitives::{
11    Address, B256, Bytes, StorageValue, U256,
12    map::{FbHashMap, HashMap, HashSet},
13};
14use alloy_provider::{
15    Provider,
16    ext::{DebugApi, TraceApi},
17};
18use alloy_rpc_types::{
19    BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, EIP1186AccountProofResponse,
20    FeeHistory, Filter, Log,
21    simulate::{SimulatePayload, SimulatedBlock},
22    trace::{
23        geth::{GethDebugTracingOptions, GethTrace},
24        parity::{LocalizedTransactionTrace as Trace, TraceResultsWithTransactionHash, TraceType},
25    },
26};
27use alloy_transport::TransportError;
28use foundry_common::provider::{ProviderBuilder, RetryProvider};
29use foundry_primitives::{FoundryTxEnvelope, FoundryTxReceipt};
30use parking_lot::{
31    RawRwLock, RwLock,
32    lock_api::{RwLockReadGuard, RwLockWriteGuard},
33};
34use revm::context_interface::block::BlobExcessGasAndPrice;
35use std::{sync::Arc, time::Duration};
36use tokio::sync::RwLock as AsyncRwLock;
37
38/// Represents a fork of a remote client
39///
40/// This type contains a subset of the [`EthApi`](crate::eth::EthApi) functions but will exclusively
41/// fetch the requested data from the remote client, if it wasn't already fetched.
42#[derive(Clone, Debug)]
43pub struct ClientFork<N: Network = AnyNetwork> {
44    /// Contains the cached data
45    pub storage: Arc<RwLock<ForkedStorage<N>>>,
46    /// contains the info how the fork is configured
47    // Wrapping this in a lock, ensures we can update this on the fly via additional custom RPC
48    // endpoints
49    pub config: Arc<RwLock<ClientForkConfig<N>>>,
50    /// This also holds a handle to the underlying database
51    pub database: Arc<AsyncRwLock<Box<dyn Db>>>,
52}
53
54impl<N: Network> ClientFork<N> {
55    /// Creates a new instance of the fork
56    pub fn new(config: ClientForkConfig<N>, database: Arc<AsyncRwLock<Box<dyn Db>>>) -> Self {
57        Self { storage: Default::default(), config: Arc::new(RwLock::new(config)), database }
58    }
59
60    /// Removes all data cached from previous responses
61    pub fn clear_cached_storage(&self) {
62        self.storage.write().clear()
63    }
64
65    /// Returns true whether the block predates the fork
66    pub fn predates_fork(&self, block: u64) -> bool {
67        block < self.block_number()
68    }
69
70    /// Returns true whether the block predates the fork _or_ is the same block as the fork
71    pub fn predates_fork_inclusive(&self, block: u64) -> bool {
72        block <= self.block_number()
73    }
74
75    pub fn timestamp(&self) -> u64 {
76        self.config.read().timestamp
77    }
78
79    pub fn block_number(&self) -> u64 {
80        self.config.read().block_number
81    }
82
83    /// Returns the transaction hash we forked off of, if any.
84    pub fn transaction_hash(&self) -> Option<B256> {
85        self.config.read().transaction_hash
86    }
87
88    pub fn total_difficulty(&self) -> U256 {
89        self.config.read().total_difficulty
90    }
91
92    pub fn base_fee(&self) -> Option<u128> {
93        self.config.read().base_fee
94    }
95
96    pub fn block_hash(&self) -> B256 {
97        self.config.read().block_hash
98    }
99
100    pub fn eth_rpc_url(&self) -> String {
101        self.config.read().eth_rpc_url.clone()
102    }
103
104    pub fn chain_id(&self) -> u64 {
105        self.config.read().chain_id
106    }
107
108    fn provider(&self) -> Arc<RetryProvider<N>> {
109        self.config.read().provider.clone()
110    }
111
112    fn storage_read(&self) -> RwLockReadGuard<'_, RawRwLock, ForkedStorage<N>> {
113        self.storage.read()
114    }
115
116    fn storage_write(&self) -> RwLockWriteGuard<'_, RawRwLock, ForkedStorage<N>> {
117        self.storage.write()
118    }
119
120    /// Returns the fee history  `eth_feeHistory`
121    pub async fn fee_history(
122        &self,
123        block_count: u64,
124        newest_block: BlockNumber,
125        reward_percentiles: &[f64],
126    ) -> Result<FeeHistory, TransportError> {
127        self.provider().get_fee_history(block_count, newest_block, reward_percentiles).await
128    }
129
130    /// Sends `eth_getProof`
131    pub async fn get_proof(
132        &self,
133        address: Address,
134        keys: Vec<B256>,
135        block_number: Option<BlockId>,
136    ) -> Result<EIP1186AccountProofResponse, TransportError> {
137        self.provider().get_proof(address, keys).block_id(block_number.unwrap_or_default()).await
138    }
139
140    pub async fn storage_at(
141        &self,
142        address: Address,
143        index: U256,
144        number: Option<BlockNumber>,
145    ) -> Result<StorageValue, TransportError> {
146        self.provider()
147            .get_storage_at(address, index)
148            .block_id(number.unwrap_or_default().into())
149            .await
150    }
151
152    pub async fn logs(&self, filter: &Filter) -> Result<Vec<Log>, TransportError> {
153        if let Some(logs) = self.storage_read().logs.get(filter).cloned() {
154            return Ok(logs);
155        }
156
157        let logs = self.provider().get_logs(filter).await?;
158
159        let mut storage = self.storage_write();
160        storage.logs.insert(filter.clone(), logs.clone());
161        Ok(logs)
162    }
163
164    pub async fn get_code(
165        &self,
166        address: Address,
167        blocknumber: u64,
168    ) -> Result<Bytes, TransportError> {
169        trace!(target: "backend::fork", "get_code={:?}", address);
170        if let Some(code) = self.storage_read().code_at.get(&(address, blocknumber)).cloned() {
171            return Ok(code);
172        }
173
174        let block_id = BlockId::number(blocknumber);
175
176        let code = self.provider().get_code_at(address).block_id(block_id).await?;
177
178        let mut storage = self.storage_write();
179        storage.code_at.insert((address, blocknumber), code.clone());
180
181        Ok(code)
182    }
183
184    pub async fn get_balance(
185        &self,
186        address: Address,
187        blocknumber: u64,
188    ) -> Result<U256, TransportError> {
189        trace!(target: "backend::fork", "get_balance={:?}", address);
190        self.provider().get_balance(address).block_id(blocknumber.into()).await
191    }
192
193    pub async fn get_nonce(&self, address: Address, block: u64) -> Result<u64, TransportError> {
194        trace!(target: "backend::fork", "get_nonce={:?}", address);
195        self.provider().get_transaction_count(address).block_id(block.into()).await
196    }
197
198    pub async fn get_account(
199        &self,
200        address: Address,
201        blocknumber: u64,
202    ) -> Result<TrieAccount, TransportError> {
203        trace!(target: "backend::fork", "get_account={:?}", address);
204        self.provider().get_account(address).block_id(blocknumber.into()).await
205    }
206
207    pub async fn trace_transaction(&self, hash: B256) -> Result<Vec<Trace>, TransportError> {
208        if let Some(traces) = self.storage_read().transaction_traces.get(&hash).cloned() {
209            return Ok(traces);
210        }
211
212        let traces = self.provider().trace_transaction(hash).await?.into_iter().collect::<Vec<_>>();
213
214        let mut storage = self.storage_write();
215        storage.transaction_traces.insert(hash, traces.clone());
216
217        Ok(traces)
218    }
219
220    pub async fn debug_trace_transaction(
221        &self,
222        hash: B256,
223        opts: GethDebugTracingOptions,
224    ) -> Result<GethTrace, TransportError> {
225        if let Some(traces) = self.storage_read().geth_transaction_traces.get(&hash).cloned() {
226            return Ok(traces);
227        }
228
229        let trace = self.provider().debug_trace_transaction(hash, opts).await?;
230
231        let mut storage = self.storage_write();
232        storage.geth_transaction_traces.insert(hash, trace.clone());
233
234        Ok(trace)
235    }
236
237    pub async fn debug_code_by_hash(
238        &self,
239        code_hash: B256,
240        block_id: Option<BlockId>,
241    ) -> Result<Option<Bytes>, TransportError> {
242        self.provider().debug_code_by_hash(code_hash, block_id).await
243    }
244
245    pub async fn trace_block(&self, number: u64) -> Result<Vec<Trace>, TransportError> {
246        if let Some(traces) = self.storage_read().block_traces.get(&number).cloned() {
247            return Ok(traces);
248        }
249
250        let traces =
251            self.provider().trace_block(number.into()).await?.into_iter().collect::<Vec<_>>();
252
253        let mut storage = self.storage_write();
254        storage.block_traces.insert(number, traces.clone());
255
256        Ok(traces)
257    }
258
259    pub async fn trace_replay_block_transactions(
260        &self,
261        number: u64,
262        trace_types: HashSet<TraceType>,
263    ) -> Result<Vec<TraceResultsWithTransactionHash>, TransportError> {
264        // Forward to upstream provider for historical blocks
265        let params = (number, trace_types.iter().map(|t| format!("{t:?}")).collect::<Vec<_>>());
266        self.provider().raw_request("trace_replayBlockTransactions".into(), params).await
267    }
268
269    /// Reset the fork to a fresh forked state, and optionally update the fork config
270    pub async fn reset(
271        &self,
272        url: Option<String>,
273        block_number: impl Into<BlockId>,
274    ) -> Result<(), BlockchainError> {
275        let block_number = block_number.into();
276        {
277            self.database
278                .write()
279                .await
280                .maybe_reset(url.clone(), block_number)
281                .map_err(BlockchainError::Internal)?;
282        }
283
284        if let Some(url) = url {
285            self.config.write().update_url(url)?;
286            let override_chain_id = self.config.read().override_chain_id;
287            let chain_id = if let Some(chain_id) = override_chain_id {
288                chain_id
289            } else {
290                self.provider().get_chain_id().await?
291            };
292            self.config.write().chain_id = chain_id;
293        }
294
295        let provider = self.provider();
296        let block =
297            provider.get_block(block_number).await?.ok_or(BlockchainError::BlockNotFound)?;
298        let block_hash = block.header().hash();
299        let timestamp = block.header().timestamp();
300        let base_fee = block.header().base_fee_per_gas();
301        let total_difficulty = block.header().difficulty();
302
303        let number = block.header().number();
304        self.config.write().update_block(
305            number,
306            block_hash,
307            timestamp,
308            base_fee.map(|g| g as u128),
309            total_difficulty,
310        );
311
312        self.clear_cached_storage();
313
314        self.database.write().await.insert_block_hash(U256::from(number), block_hash);
315
316        Ok(())
317    }
318
319    /// Sends `eth_call`
320    pub async fn call(
321        &self,
322        request: &N::TransactionRequest,
323        block: Option<BlockNumber>,
324    ) -> Result<Bytes, TransportError> {
325        let block = block.unwrap_or(BlockNumber::Latest);
326        let res = self.provider().call(request.clone()).block(block.into()).await?;
327
328        Ok(res)
329    }
330
331    /// Sends `eth_simulateV1`
332    pub async fn simulate_v1(
333        &self,
334        request: &SimulatePayload,
335        block: Option<BlockNumber>,
336    ) -> Result<Vec<SimulatedBlock<N::BlockResponse>>, TransportError> {
337        let mut simulate_call = self.provider().simulate(request);
338        if let Some(n) = block {
339            simulate_call = simulate_call.number(n.as_number().unwrap());
340        }
341
342        let res = simulate_call.await?;
343
344        Ok(res)
345    }
346
347    /// Sends `eth_estimateGas`
348    pub async fn estimate_gas(
349        &self,
350        request: &N::TransactionRequest,
351        block: Option<BlockNumber>,
352    ) -> Result<u128, TransportError> {
353        let block = block.unwrap_or_default();
354        let res = self.provider().estimate_gas(request.clone()).block(block.into()).await?;
355
356        Ok(res as u128)
357    }
358
359    /// Sends `eth_createAccessList`
360    pub async fn create_access_list(
361        &self,
362        request: &N::TransactionRequest,
363        block: Option<BlockNumber>,
364    ) -> Result<AccessListResult, TransportError> {
365        self.provider().create_access_list(request).block_id(block.unwrap_or_default().into()).await
366    }
367
368    pub async fn transaction_by_block_number_and_index(
369        &self,
370        number: u64,
371        index: usize,
372    ) -> Result<Option<N::TransactionResponse>, TransportError> {
373        if let Some(block) = self.block_by_number(number).await? {
374            #[allow(clippy::collapsible_match)]
375            match block.transactions() {
376                BlockTransactions::Full(txs) => {
377                    if let Some(tx) = txs.get(index) {
378                        return Ok(Some(tx.clone()));
379                    }
380                }
381                BlockTransactions::Hashes(hashes) => {
382                    if let Some(tx_hash) = hashes.get(index) {
383                        return self.transaction_by_hash(*tx_hash).await;
384                    }
385                }
386                // TODO(evalir): Is it possible to reach this case? Should we support it
387                BlockTransactions::Uncle => panic!("Uncles not supported"),
388            }
389        }
390        Ok(None)
391    }
392
393    pub async fn transaction_by_block_hash_and_index(
394        &self,
395        hash: B256,
396        index: usize,
397    ) -> Result<Option<N::TransactionResponse>, TransportError> {
398        if let Some(block) = self.block_by_hash(hash).await? {
399            #[allow(clippy::collapsible_match)]
400            match block.transactions() {
401                BlockTransactions::Full(txs) => {
402                    if let Some(tx) = txs.get(index) {
403                        return Ok(Some(tx.clone()));
404                    }
405                }
406                BlockTransactions::Hashes(hashes) => {
407                    if let Some(tx_hash) = hashes.get(index) {
408                        return self.transaction_by_hash(*tx_hash).await;
409                    }
410                }
411                // TODO(evalir): Is it possible to reach this case? Should we support it
412                BlockTransactions::Uncle => panic!("Uncles not supported"),
413            }
414        }
415        Ok(None)
416    }
417
418    pub async fn transaction_by_hash(
419        &self,
420        hash: B256,
421    ) -> Result<Option<N::TransactionResponse>, TransportError> {
422        trace!(target: "backend::fork", "transaction_by_hash={:?}", hash);
423        if let tx @ Some(_) = self.storage_read().transactions.get(&hash).cloned() {
424            return Ok(tx);
425        }
426
427        let tx = self.provider().get_transaction_by_hash(hash).await?;
428        if let Some(tx) = tx.clone() {
429            let mut storage = self.storage_write();
430            storage.transactions.insert(hash, tx);
431        }
432        Ok(tx)
433    }
434
435    pub async fn block_by_hash(
436        &self,
437        hash: B256,
438    ) -> Result<Option<N::BlockResponse>, TransportError> {
439        if let Some(mut block) = self.storage_read().blocks.get(&hash).cloned() {
440            block.transactions_mut().convert_to_hashes();
441            return Ok(Some(block));
442        }
443
444        Ok(self.fetch_full_block(hash).await?.map(|mut b| {
445            b.transactions_mut().convert_to_hashes();
446            b
447        }))
448    }
449
450    pub async fn block_by_hash_full(
451        &self,
452        hash: B256,
453    ) -> Result<Option<N::BlockResponse>, TransportError> {
454        if let Some(block) = self.storage_read().blocks.get(&hash).cloned() {
455            return Ok(Some(self.convert_to_full_block(block)));
456        }
457        self.fetch_full_block(hash).await
458    }
459
460    pub async fn block_by_number(
461        &self,
462        block_number: u64,
463    ) -> Result<Option<N::BlockResponse>, TransportError> {
464        if let Some(mut block) = self
465            .storage_read()
466            .hashes
467            .get(&block_number)
468            .and_then(|hash| self.storage_read().blocks.get(hash).cloned())
469        {
470            block.transactions_mut().convert_to_hashes();
471            return Ok(Some(block));
472        }
473
474        let mut block = self.fetch_full_block(block_number).await?;
475        if let Some(block) = &mut block {
476            block.transactions_mut().convert_to_hashes();
477        }
478        Ok(block)
479    }
480
481    pub async fn block_by_number_full(
482        &self,
483        block_number: u64,
484    ) -> Result<Option<N::BlockResponse>, TransportError> {
485        if let Some(block) = self
486            .storage_read()
487            .hashes
488            .get(&block_number)
489            .copied()
490            .and_then(|hash| self.storage_read().blocks.get(&hash).cloned())
491        {
492            return Ok(Some(self.convert_to_full_block(block)));
493        }
494
495        self.fetch_full_block(block_number).await
496    }
497
498    async fn fetch_full_block(
499        &self,
500        block_id: impl Into<BlockId>,
501    ) -> Result<Option<N::BlockResponse>, TransportError> {
502        if let Some(block) = self.provider().get_block(block_id.into()).full().await? {
503            let hash = block.header().hash();
504            let block_number = block.header().number();
505            let mut storage = self.storage_write();
506            // also insert all transactions
507            let block_txs = match block.transactions() {
508                BlockTransactions::Full(txs) => txs.to_owned(),
509                _ => vec![],
510            };
511            storage.transactions.extend(block_txs.iter().map(|tx| (tx.tx_hash(), tx.clone())));
512            storage.hashes.insert(block_number, hash);
513            storage.blocks.insert(hash, block.clone());
514            return Ok(Some(block));
515        }
516
517        Ok(None)
518    }
519
520    /// Converts a block of hashes into a full block
521    fn convert_to_full_block(&self, mut block: N::BlockResponse) -> N::BlockResponse {
522        let storage = self.storage.read();
523        let transactions = block
524            .transactions()
525            .hashes()
526            .filter_map(|hash| storage.transactions.get(&hash).cloned())
527            .collect();
528        *block.transactions_mut() = BlockTransactions::Full(transactions);
529        block
530    }
531}
532
533impl ClientFork {
534    pub async fn transaction_receipt(
535        &self,
536        hash: B256,
537    ) -> Result<Option<FoundryTxReceipt>, BlockchainError> {
538        if let Some(receipt) = self.storage_read().transaction_receipts.get(&hash).cloned() {
539            return Ok(Some(receipt));
540        }
541
542        if let Some(receipt) = self.provider().get_transaction_receipt(hash).await? {
543            let receipt = FoundryTxReceipt::try_from(receipt)
544                .map_err(|_| BlockchainError::FailedToDecodeReceipt)?;
545            let mut storage = self.storage_write();
546            storage.transaction_receipts.insert(hash, receipt.clone());
547            return Ok(Some(receipt));
548        }
549
550        Ok(None)
551    }
552
553    pub async fn block_receipts(
554        &self,
555        number: u64,
556    ) -> Result<Option<Vec<FoundryTxReceipt>>, BlockchainError> {
557        if let receipts @ Some(_) = self.storage_read().block_receipts.get(&number).cloned() {
558            return Ok(receipts);
559        }
560
561        // TODO Needs to be removed.
562        // Since alloy doesn't indicate in the result whether the block exists,
563        // this is being temporarily implemented in anvil.
564        if self.predates_fork_inclusive(number) {
565            let receipts = self.provider().get_block_receipts(BlockId::from(number)).await?;
566            let receipts = receipts
567                .map(|r| {
568                    r.into_iter()
569                        .map(|r| {
570                            FoundryTxReceipt::try_from(r)
571                                .map_err(|_| BlockchainError::FailedToDecodeReceipt)
572                        })
573                        .collect::<Result<Vec<_>, _>>()
574                })
575                .transpose()?;
576
577            if let Some(receipts) = receipts.clone() {
578                let mut storage = self.storage_write();
579                storage.block_receipts.insert(number, receipts);
580            }
581
582            return Ok(receipts);
583        }
584
585        Ok(None)
586    }
587
588    pub async fn uncle_by_block_hash_and_index(
589        &self,
590        hash: B256,
591        index: usize,
592    ) -> Result<Option<AnyRpcBlock>, TransportError> {
593        if let Some(block) = self.block_by_hash(hash).await? {
594            return self.uncles_by_block_and_index(block, index).await;
595        }
596        Ok(None)
597    }
598
599    pub async fn uncle_by_block_number_and_index(
600        &self,
601        number: u64,
602        index: usize,
603    ) -> Result<Option<AnyRpcBlock>, TransportError> {
604        if let Some(block) = self.block_by_number(number).await? {
605            return self.uncles_by_block_and_index(block, index).await;
606        }
607        Ok(None)
608    }
609
610    async fn uncles_by_block_and_index(
611        &self,
612        block: AnyRpcBlock,
613        index: usize,
614    ) -> Result<Option<AnyRpcBlock>, TransportError> {
615        let block_hash = block.header().hash();
616        let block_number = block.header().number();
617        if let Some(uncles) = self.storage_read().uncles.get(&block_hash) {
618            return Ok(uncles.get(index).cloned());
619        }
620
621        let mut uncles = Vec::with_capacity(block.uncles.len());
622        for (uncle_idx, _) in block.uncles.iter().enumerate() {
623            let uncle =
624                match self.provider().get_uncle(block_number.into(), uncle_idx as u64).await? {
625                    Some(u) => u,
626                    None => return Ok(None),
627                };
628            uncles.push(uncle);
629        }
630        self.storage_write().uncles.insert(block_hash, uncles.clone());
631        Ok(uncles.get(index).cloned())
632    }
633}
634
635/// Contains all fork metadata
636#[derive(Clone, Debug)]
637pub struct ClientForkConfig<N: Network = AnyNetwork> {
638    pub eth_rpc_url: String,
639    /// The block number of the forked block
640    pub block_number: u64,
641    /// The hash of the forked block
642    pub block_hash: B256,
643    /// The transaction hash we forked off of, if any.
644    pub transaction_hash: Option<B256>,
645    pub provider: Arc<RetryProvider<N>>,
646    pub chain_id: u64,
647    pub override_chain_id: Option<u64>,
648    /// The timestamp for the forked block
649    pub timestamp: u64,
650    /// The basefee of the forked block
651    pub base_fee: Option<u128>,
652    /// Blob gas used of the forked block
653    pub blob_gas_used: Option<u128>,
654    /// Blob excess gas and price of the forked block
655    pub blob_excess_gas_and_price: Option<BlobExcessGasAndPrice>,
656    /// request timeout
657    pub timeout: Duration,
658    /// request retries for spurious networks
659    pub retries: u32,
660    /// request retries for spurious networks
661    pub backoff: Duration,
662    /// available CUPS
663    pub compute_units_per_second: u64,
664    /// total difficulty of the chain until this block
665    pub total_difficulty: U256,
666    /// Transactions to force include in the forked chain
667    pub force_transactions: Option<Vec<PoolTransaction<FoundryTxEnvelope>>>,
668}
669
670impl<N: Network> ClientForkConfig<N> {
671    /// Updates the provider URL
672    ///
673    /// # Errors
674    ///
675    /// This will fail if no new provider could be established (erroneous URL)
676    fn update_url(&mut self, url: String) -> Result<(), BlockchainError> {
677        // let interval = self.provider.get_interval();
678        self.provider = Arc::new(
679            ProviderBuilder::<N>::new(url.as_str())
680                .timeout(self.timeout)
681                // .timeout_retry(self.retries)
682                .max_retry(self.retries)
683                .initial_backoff(self.backoff.as_millis() as u64)
684                .compute_units_per_second(self.compute_units_per_second)
685                .build()
686                .map_err(|e| BlockchainError::InvalidUrl(format!("{url}: {e}")))?, /* .interval(interval), */
687        );
688        trace!(target: "fork", "Updated rpc url  {}", url);
689        self.eth_rpc_url = url;
690        Ok(())
691    }
692    /// Updates the block forked off `(block number, block hash, timestamp)`
693    pub fn update_block(
694        &mut self,
695        block_number: u64,
696        block_hash: B256,
697        timestamp: u64,
698        base_fee: Option<u128>,
699        total_difficulty: U256,
700    ) {
701        self.block_number = block_number;
702        self.block_hash = block_hash;
703        self.timestamp = timestamp;
704        self.base_fee = base_fee;
705        self.total_difficulty = total_difficulty;
706        trace!(target: "fork", "Updated block number={} hash={:?}", block_number, block_hash);
707    }
708}
709
710/// Contains cached state fetched to serve EthApi requests
711///
712/// This is used as a cache so repeated requests to the same data are not sent to the remote client
713#[derive(Clone, Debug)]
714pub struct ForkedStorage<N: Network = AnyNetwork> {
715    pub uncles: FbHashMap<32, Vec<N::BlockResponse>>,
716    pub blocks: FbHashMap<32, N::BlockResponse>,
717    pub hashes: HashMap<u64, B256>,
718    pub transactions: FbHashMap<32, N::TransactionResponse>,
719    pub transaction_receipts: FbHashMap<32, FoundryTxReceipt>,
720    pub transaction_traces: FbHashMap<32, Vec<Trace>>,
721    pub logs: HashMap<Filter, Vec<Log>>,
722    pub geth_transaction_traces: FbHashMap<32, GethTrace>,
723    pub block_traces: HashMap<u64, Vec<Trace>>,
724    pub block_receipts: HashMap<u64, Vec<FoundryTxReceipt>>,
725    pub code_at: HashMap<(Address, u64), Bytes>,
726}
727
728impl<N: Network> Default for ForkedStorage<N> {
729    fn default() -> Self {
730        Self {
731            uncles: Default::default(),
732            blocks: Default::default(),
733            hashes: Default::default(),
734            transactions: Default::default(),
735            transaction_receipts: Default::default(),
736            transaction_traces: Default::default(),
737            logs: Default::default(),
738            geth_transaction_traces: Default::default(),
739            block_traces: Default::default(),
740            block_receipts: Default::default(),
741            code_at: Default::default(),
742        }
743    }
744}
745
746impl<N: Network> ForkedStorage<N> {
747    /// Clears all data
748    pub fn clear(&mut self) {
749        // simply replace with a completely new, empty instance
750        *self = Self::default()
751    }
752}