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::Account;
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},
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,
23    },
24};
25use alloy_serde::WithOtherFields;
26use alloy_transport::TransportError;
27use anvil_core::eth::transaction::{ReceiptResponse, convert_to_anvil_receipt};
28use foundry_common::provider::{ProviderBuilder, RetryProvider};
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().0.into());
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<Account, 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 transaction_receipt(
423        &self,
424        hash: B256,
425    ) -> Result<Option<ReceiptResponse>, BlockchainError> {
426        if let Some(receipt) = self.storage_read().transaction_receipts.get(&hash).cloned() {
427            return Ok(Some(receipt));
428        }
429
430        if let Some(receipt) = self.provider().get_transaction_receipt(hash).await? {
431            let receipt =
432                convert_to_anvil_receipt(receipt).ok_or(BlockchainError::FailedToDecodeReceipt)?;
433            let mut storage = self.storage_write();
434            storage.transaction_receipts.insert(hash, receipt.clone());
435            return Ok(Some(receipt));
436        }
437
438        Ok(None)
439    }
440
441    pub async fn block_receipts(
442        &self,
443        number: u64,
444    ) -> Result<Option<Vec<ReceiptResponse>>, BlockchainError> {
445        if let receipts @ Some(_) = self.storage_read().block_receipts.get(&number).cloned() {
446            return Ok(receipts);
447        }
448
449        // TODO Needs to be removed.
450        // Since alloy doesn't indicate in the result whether the block exists,
451        // this is being temporarily implemented in anvil.
452        if self.predates_fork_inclusive(number) {
453            let receipts = self.provider().get_block_receipts(BlockId::from(number)).await?;
454            let receipts = receipts
455                .map(|r| {
456                    r.into_iter()
457                        .map(|r| {
458                            convert_to_anvil_receipt(r)
459                                .ok_or(BlockchainError::FailedToDecodeReceipt)
460                        })
461                        .collect::<Result<Vec<_>, _>>()
462                })
463                .transpose()?;
464
465            if let Some(receipts) = receipts.clone() {
466                let mut storage = self.storage_write();
467                storage.block_receipts.insert(number, receipts);
468            }
469
470            return Ok(receipts);
471        }
472
473        Ok(None)
474    }
475
476    pub async fn block_by_hash(&self, hash: B256) -> Result<Option<AnyRpcBlock>, TransportError> {
477        if let Some(mut block) = self.storage_read().blocks.get(&hash).cloned() {
478            block.transactions.convert_to_hashes();
479            return Ok(Some(block));
480        }
481
482        Ok(self.fetch_full_block(hash).await?.map(|mut b| {
483            b.transactions.convert_to_hashes();
484            b
485        }))
486    }
487
488    pub async fn block_by_hash_full(
489        &self,
490        hash: B256,
491    ) -> Result<Option<AnyRpcBlock>, TransportError> {
492        if let Some(block) = self.storage_read().blocks.get(&hash).cloned() {
493            return Ok(Some(self.convert_to_full_block(block)));
494        }
495        self.fetch_full_block(hash).await
496    }
497
498    pub async fn block_by_number(
499        &self,
500        block_number: u64,
501    ) -> Result<Option<AnyRpcBlock>, TransportError> {
502        if let Some(mut block) = self
503            .storage_read()
504            .hashes
505            .get(&block_number)
506            .and_then(|hash| self.storage_read().blocks.get(hash).cloned())
507        {
508            block.transactions.convert_to_hashes();
509            return Ok(Some(block));
510        }
511
512        let mut block = self.fetch_full_block(block_number).await?;
513        if let Some(block) = &mut block {
514            block.transactions.convert_to_hashes();
515        }
516        Ok(block)
517    }
518
519    pub async fn block_by_number_full(
520        &self,
521        block_number: u64,
522    ) -> Result<Option<AnyRpcBlock>, TransportError> {
523        if let Some(block) = self
524            .storage_read()
525            .hashes
526            .get(&block_number)
527            .copied()
528            .and_then(|hash| self.storage_read().blocks.get(&hash).cloned())
529        {
530            return Ok(Some(self.convert_to_full_block(block)));
531        }
532
533        self.fetch_full_block(block_number).await
534    }
535
536    async fn fetch_full_block(
537        &self,
538        block_id: impl Into<BlockId>,
539    ) -> Result<Option<AnyRpcBlock>, TransportError> {
540        if let Some(block) = self.provider().get_block(block_id.into()).full().await? {
541            let hash = block.header.hash;
542            let block_number = block.header.number;
543            let mut storage = self.storage_write();
544            // also insert all transactions
545            let block_txs = match block.transactions() {
546                BlockTransactions::Full(txs) => txs.to_owned(),
547                _ => vec![],
548            };
549            storage.transactions.extend(block_txs.iter().map(|tx| (tx.tx_hash(), tx.clone())));
550            storage.hashes.insert(block_number, hash);
551            storage.blocks.insert(hash, block.clone());
552            return Ok(Some(block));
553        }
554
555        Ok(None)
556    }
557
558    pub async fn uncle_by_block_hash_and_index(
559        &self,
560        hash: B256,
561        index: usize,
562    ) -> Result<Option<AnyRpcBlock>, TransportError> {
563        if let Some(block) = self.block_by_hash(hash).await? {
564            return self.uncles_by_block_and_index(block, index).await;
565        }
566        Ok(None)
567    }
568
569    pub async fn uncle_by_block_number_and_index(
570        &self,
571        number: u64,
572        index: usize,
573    ) -> Result<Option<AnyRpcBlock>, TransportError> {
574        if let Some(block) = self.block_by_number(number).await? {
575            return self.uncles_by_block_and_index(block, index).await;
576        }
577        Ok(None)
578    }
579
580    async fn uncles_by_block_and_index(
581        &self,
582        block: AnyRpcBlock,
583        index: usize,
584    ) -> Result<Option<AnyRpcBlock>, TransportError> {
585        let block_hash = block.header.hash;
586        let block_number = block.header.number;
587        if let Some(uncles) = self.storage_read().uncles.get(&block_hash) {
588            return Ok(uncles.get(index).cloned());
589        }
590
591        let mut uncles = Vec::with_capacity(block.uncles.len());
592        for (uncle_idx, _) in block.uncles.iter().enumerate() {
593            let uncle =
594                match self.provider().get_uncle(block_number.into(), uncle_idx as u64).await? {
595                    Some(u) => u,
596                    None => return Ok(None),
597                };
598            uncles.push(uncle);
599        }
600        self.storage_write().uncles.insert(block_hash, uncles.clone());
601        Ok(uncles.get(index).cloned())
602    }
603
604    /// Converts a block of hashes into a full block
605    fn convert_to_full_block(&self, mut block: AnyRpcBlock) -> AnyRpcBlock {
606        let storage = self.storage.read();
607        let block_txs_len = match block.transactions {
608            BlockTransactions::Full(ref txs) => txs.len(),
609            BlockTransactions::Hashes(ref hashes) => hashes.len(),
610            // TODO: Should this be supported at all?
611            BlockTransactions::Uncle => 0,
612        };
613        let mut transactions = Vec::with_capacity(block_txs_len);
614        for tx in block.transactions.hashes() {
615            if let Some(tx) = storage.transactions.get(&tx).cloned() {
616                transactions.push(tx);
617            }
618        }
619        // TODO: fix once blocks have generic transactions
620        block.inner.transactions = BlockTransactions::Full(transactions);
621
622        block
623    }
624}
625
626/// Contains all fork metadata
627#[derive(Clone, Debug)]
628pub struct ClientForkConfig {
629    pub eth_rpc_url: String,
630    /// The block number of the forked block
631    pub block_number: u64,
632    /// The hash of the forked block
633    pub block_hash: B256,
634    /// The transaction hash we forked off of, if any.
635    pub transaction_hash: Option<B256>,
636    // TODO make provider agnostic
637    pub provider: Arc<RetryProvider>,
638    pub chain_id: u64,
639    pub override_chain_id: Option<u64>,
640    /// The timestamp for the forked block
641    pub timestamp: u64,
642    /// The basefee of the forked block
643    pub base_fee: Option<u128>,
644    /// Blob gas used of the forked block
645    pub blob_gas_used: Option<u128>,
646    /// Blob excess gas and price of the forked block
647    pub blob_excess_gas_and_price: Option<BlobExcessGasAndPrice>,
648    /// request timeout
649    pub timeout: Duration,
650    /// request retries for spurious networks
651    pub retries: u32,
652    /// request retries for spurious networks
653    pub backoff: Duration,
654    /// available CUPS
655    pub compute_units_per_second: u64,
656    /// total difficulty of the chain until this block
657    pub total_difficulty: U256,
658    /// Transactions to force include in the forked chain
659    pub force_transactions: Option<Vec<PoolTransaction>>,
660}
661
662impl ClientForkConfig {
663    /// Updates the provider URL
664    ///
665    /// # Errors
666    ///
667    /// This will fail if no new provider could be established (erroneous URL)
668    fn update_url(&mut self, url: String) -> Result<(), BlockchainError> {
669        // let interval = self.provider.get_interval();
670        self.provider = Arc::new(
671            ProviderBuilder::new(url.as_str())
672                .timeout(self.timeout)
673                // .timeout_retry(self.retries)
674                .max_retry(self.retries)
675                .initial_backoff(self.backoff.as_millis() as u64)
676                .compute_units_per_second(self.compute_units_per_second)
677                .build()
678                .map_err(|_| BlockchainError::InvalidUrl(url.clone()))?, // .interval(interval),
679        );
680        trace!(target: "fork", "Updated rpc url  {}", url);
681        self.eth_rpc_url = url;
682        Ok(())
683    }
684    /// Updates the block forked off `(block number, block hash, timestamp)`
685    pub fn update_block(
686        &mut self,
687        block_number: u64,
688        block_hash: B256,
689        timestamp: u64,
690        base_fee: Option<u128>,
691        total_difficulty: U256,
692    ) {
693        self.block_number = block_number;
694        self.block_hash = block_hash;
695        self.timestamp = timestamp;
696        self.base_fee = base_fee;
697        self.total_difficulty = total_difficulty;
698        trace!(target: "fork", "Updated block number={} hash={:?}", block_number, block_hash);
699    }
700}
701
702/// Contains cached state fetched to serve EthApi requests
703///
704/// This is used as a cache so repeated requests to the same data are not sent to the remote client
705#[derive(Clone, Debug, Default)]
706pub struct ForkedStorage {
707    pub uncles: FbHashMap<32, Vec<AnyRpcBlock>>,
708    pub blocks: FbHashMap<32, AnyRpcBlock>,
709    pub hashes: HashMap<u64, B256>,
710    pub transactions: FbHashMap<32, AnyRpcTransaction>,
711    pub transaction_receipts: FbHashMap<32, ReceiptResponse>,
712    pub transaction_traces: FbHashMap<32, Vec<Trace>>,
713    pub logs: HashMap<Filter, Vec<Log>>,
714    pub geth_transaction_traces: FbHashMap<32, GethTrace>,
715    pub block_traces: HashMap<u64, Vec<Trace>>,
716    pub block_receipts: HashMap<u64, Vec<ReceiptResponse>>,
717    pub code_at: HashMap<(Address, u64), Bytes>,
718}
719
720impl ForkedStorage {
721    /// Clears all data
722    pub fn clear(&mut self) {
723        // simply replace with a completely new, empty instance
724        *self = Self::default()
725    }
726}