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