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            #[allow(clippy::collapsible_match)]
312            match block.transactions() {
313                BlockTransactions::Full(txs) => {
314                    if let Some(tx) = txs.get(index) {
315                        return Ok(Some(tx.clone()));
316                    }
317                }
318                BlockTransactions::Hashes(hashes) => {
319                    if let Some(tx_hash) = hashes.get(index) {
320                        return self.transaction_by_hash(*tx_hash).await;
321                    }
322                }
323                // TODO(evalir): Is it possible to reach this case? Should we support it
324                BlockTransactions::Uncle => panic!("Uncles not supported"),
325            }
326        }
327        Ok(None)
328    }
329
330    pub async fn transaction_by_block_hash_and_index(
331        &self,
332        hash: B256,
333        index: usize,
334    ) -> Result<Option<AnyRpcTransaction>, TransportError> {
335        if let Some(block) = self.block_by_hash(hash).await? {
336            #[allow(clippy::collapsible_match)]
337            match block.transactions() {
338                BlockTransactions::Full(txs) => {
339                    if let Some(tx) = txs.get(index) {
340                        return Ok(Some(tx.clone()));
341                    }
342                }
343                BlockTransactions::Hashes(hashes) => {
344                    if let Some(tx_hash) = hashes.get(index) {
345                        return self.transaction_by_hash(*tx_hash).await;
346                    }
347                }
348                // TODO(evalir): Is it possible to reach this case? Should we support it
349                BlockTransactions::Uncle => panic!("Uncles not supported"),
350            }
351        }
352        Ok(None)
353    }
354
355    pub async fn transaction_by_hash(
356        &self,
357        hash: B256,
358    ) -> Result<Option<AnyRpcTransaction>, TransportError> {
359        trace!(target: "backend::fork", "transaction_by_hash={:?}", hash);
360        if let tx @ Some(_) = self.storage_read().transactions.get(&hash).cloned() {
361            return Ok(tx);
362        }
363
364        let tx = self.provider().get_transaction_by_hash(hash).await?;
365        if let Some(tx) = tx.clone() {
366            let mut storage = self.storage_write();
367            storage.transactions.insert(hash, tx);
368        }
369        Ok(tx)
370    }
371
372    pub async fn trace_transaction(&self, hash: B256) -> Result<Vec<Trace>, TransportError> {
373        if let Some(traces) = self.storage_read().transaction_traces.get(&hash).cloned() {
374            return Ok(traces);
375        }
376
377        let traces = self.provider().trace_transaction(hash).await?.into_iter().collect::<Vec<_>>();
378
379        let mut storage = self.storage_write();
380        storage.transaction_traces.insert(hash, traces.clone());
381
382        Ok(traces)
383    }
384
385    pub async fn debug_trace_transaction(
386        &self,
387        hash: B256,
388        opts: GethDebugTracingOptions,
389    ) -> Result<GethTrace, TransportError> {
390        if let Some(traces) = self.storage_read().geth_transaction_traces.get(&hash).cloned() {
391            return Ok(traces);
392        }
393
394        let trace = self.provider().debug_trace_transaction(hash, opts).await?;
395
396        let mut storage = self.storage_write();
397        storage.geth_transaction_traces.insert(hash, trace.clone());
398
399        Ok(trace)
400    }
401
402    pub async fn debug_code_by_hash(
403        &self,
404        code_hash: B256,
405        block_id: Option<BlockId>,
406    ) -> Result<Option<Bytes>, TransportError> {
407        self.provider().debug_code_by_hash(code_hash, block_id).await
408    }
409
410    pub async fn trace_block(&self, number: u64) -> Result<Vec<Trace>, TransportError> {
411        if let Some(traces) = self.storage_read().block_traces.get(&number).cloned() {
412            return Ok(traces);
413        }
414
415        let traces =
416            self.provider().trace_block(number.into()).await?.into_iter().collect::<Vec<_>>();
417
418        let mut storage = self.storage_write();
419        storage.block_traces.insert(number, traces.clone());
420
421        Ok(traces)
422    }
423
424    pub async fn trace_replay_block_transactions(
425        &self,
426        number: u64,
427        trace_types: HashSet<TraceType>,
428    ) -> Result<Vec<TraceResultsWithTransactionHash>, TransportError> {
429        // Forward to upstream provider for historical blocks
430        let params = (number, trace_types.iter().map(|t| format!("{t:?}")).collect::<Vec<_>>());
431        self.provider().raw_request("trace_replayBlockTransactions".into(), params).await
432    }
433
434    pub async fn transaction_receipt(
435        &self,
436        hash: B256,
437    ) -> Result<Option<FoundryTxReceipt>, BlockchainError> {
438        if let Some(receipt) = self.storage_read().transaction_receipts.get(&hash).cloned() {
439            return Ok(Some(receipt));
440        }
441
442        if let Some(receipt) = self.provider().get_transaction_receipt(hash).await? {
443            let receipt = FoundryTxReceipt::try_from(receipt)
444                .map_err(|_| BlockchainError::FailedToDecodeReceipt)?;
445            let mut storage = self.storage_write();
446            storage.transaction_receipts.insert(hash, receipt.clone());
447            return Ok(Some(receipt));
448        }
449
450        Ok(None)
451    }
452
453    pub async fn block_receipts(
454        &self,
455        number: u64,
456    ) -> Result<Option<Vec<FoundryTxReceipt>>, BlockchainError> {
457        if let receipts @ Some(_) = self.storage_read().block_receipts.get(&number).cloned() {
458            return Ok(receipts);
459        }
460
461        // TODO Needs to be removed.
462        // Since alloy doesn't indicate in the result whether the block exists,
463        // this is being temporarily implemented in anvil.
464        if self.predates_fork_inclusive(number) {
465            let receipts = self.provider().get_block_receipts(BlockId::from(number)).await?;
466            let receipts = receipts
467                .map(|r| {
468                    r.into_iter()
469                        .map(|r| {
470                            FoundryTxReceipt::try_from(r)
471                                .map_err(|_| BlockchainError::FailedToDecodeReceipt)
472                        })
473                        .collect::<Result<Vec<_>, _>>()
474                })
475                .transpose()?;
476
477            if let Some(receipts) = receipts.clone() {
478                let mut storage = self.storage_write();
479                storage.block_receipts.insert(number, receipts);
480            }
481
482            return Ok(receipts);
483        }
484
485        Ok(None)
486    }
487
488    pub async fn block_by_hash(&self, hash: B256) -> Result<Option<AnyRpcBlock>, TransportError> {
489        if let Some(mut block) = self.storage_read().blocks.get(&hash).cloned() {
490            block.transactions.convert_to_hashes();
491            return Ok(Some(block));
492        }
493
494        Ok(self.fetch_full_block(hash).await?.map(|mut b| {
495            b.transactions.convert_to_hashes();
496            b
497        }))
498    }
499
500    pub async fn block_by_hash_full(
501        &self,
502        hash: B256,
503    ) -> Result<Option<AnyRpcBlock>, TransportError> {
504        if let Some(block) = self.storage_read().blocks.get(&hash).cloned() {
505            return Ok(Some(self.convert_to_full_block(block)));
506        }
507        self.fetch_full_block(hash).await
508    }
509
510    pub async fn block_by_number(
511        &self,
512        block_number: u64,
513    ) -> Result<Option<AnyRpcBlock>, TransportError> {
514        if let Some(mut block) = self
515            .storage_read()
516            .hashes
517            .get(&block_number)
518            .and_then(|hash| self.storage_read().blocks.get(hash).cloned())
519        {
520            block.transactions.convert_to_hashes();
521            return Ok(Some(block));
522        }
523
524        let mut block = self.fetch_full_block(block_number).await?;
525        if let Some(block) = &mut block {
526            block.transactions.convert_to_hashes();
527        }
528        Ok(block)
529    }
530
531    pub async fn block_by_number_full(
532        &self,
533        block_number: u64,
534    ) -> Result<Option<AnyRpcBlock>, TransportError> {
535        if let Some(block) = self
536            .storage_read()
537            .hashes
538            .get(&block_number)
539            .copied()
540            .and_then(|hash| self.storage_read().blocks.get(&hash).cloned())
541        {
542            return Ok(Some(self.convert_to_full_block(block)));
543        }
544
545        self.fetch_full_block(block_number).await
546    }
547
548    async fn fetch_full_block(
549        &self,
550        block_id: impl Into<BlockId>,
551    ) -> Result<Option<AnyRpcBlock>, TransportError> {
552        if let Some(block) = self.provider().get_block(block_id.into()).full().await? {
553            let hash = block.header.hash;
554            let block_number = block.header.number;
555            let mut storage = self.storage_write();
556            // also insert all transactions
557            let block_txs = match block.transactions() {
558                BlockTransactions::Full(txs) => txs.to_owned(),
559                _ => vec![],
560            };
561            storage.transactions.extend(block_txs.iter().map(|tx| (tx.tx_hash(), tx.clone())));
562            storage.hashes.insert(block_number, hash);
563            storage.blocks.insert(hash, block.clone());
564            return Ok(Some(block));
565        }
566
567        Ok(None)
568    }
569
570    pub async fn uncle_by_block_hash_and_index(
571        &self,
572        hash: B256,
573        index: usize,
574    ) -> Result<Option<AnyRpcBlock>, TransportError> {
575        if let Some(block) = self.block_by_hash(hash).await? {
576            return self.uncles_by_block_and_index(block, index).await;
577        }
578        Ok(None)
579    }
580
581    pub async fn uncle_by_block_number_and_index(
582        &self,
583        number: u64,
584        index: usize,
585    ) -> Result<Option<AnyRpcBlock>, TransportError> {
586        if let Some(block) = self.block_by_number(number).await? {
587            return self.uncles_by_block_and_index(block, index).await;
588        }
589        Ok(None)
590    }
591
592    async fn uncles_by_block_and_index(
593        &self,
594        block: AnyRpcBlock,
595        index: usize,
596    ) -> Result<Option<AnyRpcBlock>, TransportError> {
597        let block_hash = block.header.hash;
598        let block_number = block.header.number;
599        if let Some(uncles) = self.storage_read().uncles.get(&block_hash) {
600            return Ok(uncles.get(index).cloned());
601        }
602
603        let mut uncles = Vec::with_capacity(block.uncles.len());
604        for (uncle_idx, _) in block.uncles.iter().enumerate() {
605            let uncle =
606                match self.provider().get_uncle(block_number.into(), uncle_idx as u64).await? {
607                    Some(u) => u,
608                    None => return Ok(None),
609                };
610            uncles.push(uncle);
611        }
612        self.storage_write().uncles.insert(block_hash, uncles.clone());
613        Ok(uncles.get(index).cloned())
614    }
615
616    /// Converts a block of hashes into a full block
617    fn convert_to_full_block(&self, mut block: AnyRpcBlock) -> AnyRpcBlock {
618        let storage = self.storage.read();
619        let block_txs_len = match block.transactions {
620            BlockTransactions::Full(ref txs) => txs.len(),
621            BlockTransactions::Hashes(ref hashes) => hashes.len(),
622            // TODO: Should this be supported at all?
623            BlockTransactions::Uncle => 0,
624        };
625        let mut transactions = Vec::with_capacity(block_txs_len);
626        for tx in block.transactions.hashes() {
627            if let Some(tx) = storage.transactions.get(&tx).cloned() {
628                transactions.push(tx);
629            }
630        }
631        // TODO: fix once blocks have generic transactions
632        block.inner.transactions = BlockTransactions::Full(transactions);
633
634        block
635    }
636}
637
638/// Contains all fork metadata
639#[derive(Clone, Debug)]
640pub struct ClientForkConfig {
641    pub eth_rpc_url: String,
642    /// The block number of the forked block
643    pub block_number: u64,
644    /// The hash of the forked block
645    pub block_hash: B256,
646    /// The transaction hash we forked off of, if any.
647    pub transaction_hash: Option<B256>,
648    // TODO make provider agnostic
649    pub provider: Arc<RetryProvider>,
650    pub chain_id: u64,
651    pub override_chain_id: Option<u64>,
652    /// The timestamp for the forked block
653    pub timestamp: u64,
654    /// The basefee of the forked block
655    pub base_fee: Option<u128>,
656    /// Blob gas used of the forked block
657    pub blob_gas_used: Option<u128>,
658    /// Blob excess gas and price of the forked block
659    pub blob_excess_gas_and_price: Option<BlobExcessGasAndPrice>,
660    /// request timeout
661    pub timeout: Duration,
662    /// request retries for spurious networks
663    pub retries: u32,
664    /// request retries for spurious networks
665    pub backoff: Duration,
666    /// available CUPS
667    pub compute_units_per_second: u64,
668    /// total difficulty of the chain until this block
669    pub total_difficulty: U256,
670    /// Transactions to force include in the forked chain
671    pub force_transactions: Option<Vec<PoolTransaction>>,
672}
673
674impl ClientForkConfig {
675    /// Updates the provider URL
676    ///
677    /// # Errors
678    ///
679    /// This will fail if no new provider could be established (erroneous URL)
680    fn update_url(&mut self, url: String) -> Result<(), BlockchainError> {
681        // let interval = self.provider.get_interval();
682        self.provider = Arc::new(
683            ProviderBuilder::new(url.as_str())
684                .timeout(self.timeout)
685                // .timeout_retry(self.retries)
686                .max_retry(self.retries)
687                .initial_backoff(self.backoff.as_millis() as u64)
688                .compute_units_per_second(self.compute_units_per_second)
689                .build()
690                .map_err(|e| BlockchainError::InvalidUrl(format!("{url}: {e}")))?, /* .interval(interval), */
691        );
692        trace!(target: "fork", "Updated rpc url  {}", url);
693        self.eth_rpc_url = url;
694        Ok(())
695    }
696    /// Updates the block forked off `(block number, block hash, timestamp)`
697    pub fn update_block(
698        &mut self,
699        block_number: u64,
700        block_hash: B256,
701        timestamp: u64,
702        base_fee: Option<u128>,
703        total_difficulty: U256,
704    ) {
705        self.block_number = block_number;
706        self.block_hash = block_hash;
707        self.timestamp = timestamp;
708        self.base_fee = base_fee;
709        self.total_difficulty = total_difficulty;
710        trace!(target: "fork", "Updated block number={} hash={:?}", block_number, block_hash);
711    }
712}
713
714/// Contains cached state fetched to serve EthApi requests
715///
716/// This is used as a cache so repeated requests to the same data are not sent to the remote client
717#[derive(Clone, Debug, Default)]
718pub struct ForkedStorage {
719    pub uncles: FbHashMap<32, Vec<AnyRpcBlock>>,
720    pub blocks: FbHashMap<32, AnyRpcBlock>,
721    pub hashes: HashMap<u64, B256>,
722    pub transactions: FbHashMap<32, AnyRpcTransaction>,
723    pub transaction_receipts: FbHashMap<32, FoundryTxReceipt>,
724    pub transaction_traces: FbHashMap<32, Vec<Trace>>,
725    pub logs: HashMap<Filter, Vec<Log>>,
726    pub geth_transaction_traces: FbHashMap<32, GethTrace>,
727    pub block_traces: HashMap<u64, Vec<Trace>>,
728    pub block_receipts: HashMap<u64, Vec<FoundryTxReceipt>>,
729    pub code_at: HashMap<(Address, u64), Bytes>,
730}
731
732impl ForkedStorage {
733    /// Clears all data
734    pub fn clear(&mut self) {
735        // simply replace with a completely new, empty instance
736        *self = Self::default()
737    }
738}