1use 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, TraceResult},
24 parity::{LocalizedTransactionTrace as Trace, TraceResultsWithTransactionHash, TraceType},
25 },
26};
27use alloy_transport::TransportError;
28use foundry_common::provider::{ProviderBuilder, RetryProvider};
29use foundry_evm::hardfork::FoundryHardfork;
30use foundry_primitives::{FoundryTxEnvelope, FoundryTxReceipt};
31use parking_lot::{
32 RawRwLock, RwLock,
33 lock_api::{RwLockReadGuard, RwLockWriteGuard},
34};
35use revm::context_interface::block::BlobExcessGasAndPrice;
36use std::{sync::Arc, time::Duration};
37use tokio::sync::RwLock as AsyncRwLock;
38
39#[derive(Clone, Debug)]
44pub struct ClientFork<N: Network = AnyNetwork> {
45 pub storage: Arc<RwLock<ForkedStorage<N>>>,
47 pub config: Arc<RwLock<ClientForkConfig<N>>>,
51 pub database: Arc<AsyncRwLock<Box<dyn Db>>>,
53}
54
55impl<N: Network> ClientFork<N> {
56 pub fn new(config: ClientForkConfig<N>, database: Arc<AsyncRwLock<Box<dyn Db>>>) -> Self {
58 Self { storage: Default::default(), config: Arc::new(RwLock::new(config)), database }
59 }
60
61 pub fn clear_cached_storage(&self) {
63 self.storage.write().clear()
64 }
65
66 pub fn predates_fork(&self, block: u64) -> bool {
68 block < self.block_number()
69 }
70
71 pub fn predates_fork_inclusive(&self, block: u64) -> bool {
73 block <= self.block_number()
74 }
75
76 pub fn timestamp(&self) -> u64 {
77 self.config.read().timestamp
78 }
79
80 pub fn block_number(&self) -> u64 {
81 self.config.read().block_number
82 }
83
84 pub fn transaction_hash(&self) -> Option<B256> {
86 self.config.read().transaction_hash
87 }
88
89 pub fn total_difficulty(&self) -> U256 {
90 self.config.read().total_difficulty
91 }
92
93 pub fn base_fee(&self) -> Option<u128> {
94 self.config.read().base_fee
95 }
96
97 pub fn block_hash(&self) -> B256 {
98 self.config.read().block_hash
99 }
100
101 pub fn eth_rpc_url(&self) -> Option<String> {
102 self.config.read().eth_rpc_url().map(|s| s.to_string())
103 }
104
105 pub fn chain_id(&self) -> u64 {
106 self.config.read().chain_id
107 }
108
109 fn provider(&self) -> Arc<RetryProvider<N>> {
110 self.config.read().provider.clone()
111 }
112
113 fn storage_read(&self) -> RwLockReadGuard<'_, RawRwLock, ForkedStorage<N>> {
114 self.storage.read()
115 }
116
117 fn storage_write(&self) -> RwLockWriteGuard<'_, RawRwLock, ForkedStorage<N>> {
118 self.storage.write()
119 }
120
121 pub async fn fee_history(
123 &self,
124 block_count: u64,
125 newest_block: BlockNumber,
126 reward_percentiles: &[f64],
127 ) -> Result<FeeHistory, TransportError> {
128 self.provider().get_fee_history(block_count, newest_block, reward_percentiles).await
129 }
130
131 pub async fn get_proof(
133 &self,
134 address: Address,
135 keys: Vec<B256>,
136 block_number: Option<BlockId>,
137 ) -> Result<EIP1186AccountProofResponse, TransportError> {
138 self.provider().get_proof(address, keys).block_id(block_number.unwrap_or_default()).await
139 }
140
141 pub async fn storage_at(
142 &self,
143 address: Address,
144 index: U256,
145 number: Option<BlockNumber>,
146 ) -> Result<StorageValue, TransportError> {
147 self.provider()
148 .get_storage_at(address, index)
149 .block_id(number.unwrap_or_default().into())
150 .await
151 }
152
153 pub async fn logs(&self, filter: &Filter) -> Result<Vec<Log>, TransportError> {
154 if let Some(logs) = self.storage_read().logs.get(filter).cloned() {
155 return Ok(logs);
156 }
157
158 let logs = self.provider().get_logs(filter).await?;
159
160 let mut storage = self.storage_write();
161 storage.logs.insert(filter.clone(), logs.clone());
162 Ok(logs)
163 }
164
165 pub async fn get_code(
166 &self,
167 address: Address,
168 blocknumber: u64,
169 ) -> Result<Bytes, TransportError> {
170 trace!(target: "backend::fork", "get_code={:?}", address);
171 if let Some(code) = self.storage_read().code_at.get(&(address, blocknumber)).cloned() {
172 return Ok(code);
173 }
174
175 let block_id = BlockId::number(blocknumber);
176
177 let code = self.provider().get_code_at(address).block_id(block_id).await?;
178
179 let mut storage = self.storage_write();
180 storage.code_at.insert((address, blocknumber), code.clone());
181
182 Ok(code)
183 }
184
185 pub async fn get_balance(
186 &self,
187 address: Address,
188 blocknumber: u64,
189 ) -> Result<U256, TransportError> {
190 trace!(target: "backend::fork", "get_balance={:?}", address);
191 self.provider().get_balance(address).block_id(blocknumber.into()).await
192 }
193
194 pub async fn get_nonce(&self, address: Address, block: u64) -> Result<u64, TransportError> {
195 trace!(target: "backend::fork", "get_nonce={:?}", address);
196 self.provider().get_transaction_count(address).block_id(block.into()).await
197 }
198
199 pub async fn get_account(
200 &self,
201 address: Address,
202 blocknumber: u64,
203 ) -> Result<TrieAccount, TransportError> {
204 trace!(target: "backend::fork", "get_account={:?}", address);
205 self.provider().get_account(address).block_id(blocknumber.into()).await
206 }
207
208 pub async fn trace_transaction(&self, hash: B256) -> Result<Vec<Trace>, TransportError> {
209 if let Some(traces) = self.storage_read().transaction_traces.get(&hash).cloned() {
210 return Ok(traces);
211 }
212
213 let traces = self.provider().trace_transaction(hash).await?.into_iter().collect::<Vec<_>>();
214
215 let mut storage = self.storage_write();
216 storage.transaction_traces.insert(hash, traces.clone());
217
218 Ok(traces)
219 }
220
221 pub async fn debug_trace_transaction(
222 &self,
223 hash: B256,
224 opts: GethDebugTracingOptions,
225 ) -> Result<GethTrace, TransportError> {
226 if let Some(traces) = self.storage_read().geth_transaction_traces.get(&hash).cloned() {
227 return Ok(traces);
228 }
229
230 let trace = self.provider().debug_trace_transaction(hash, opts).await?;
231
232 let mut storage = self.storage_write();
233 storage.geth_transaction_traces.insert(hash, trace.clone());
234
235 Ok(trace)
236 }
237
238 pub async fn debug_code_by_hash(
239 &self,
240 code_hash: B256,
241 block_id: Option<BlockId>,
242 ) -> Result<Option<Bytes>, TransportError> {
243 self.provider().debug_code_by_hash(code_hash, block_id).await
244 }
245
246 pub async fn debug_trace_block_by_hash(
247 &self,
248 block_hash: B256,
249 opts: GethDebugTracingOptions,
250 ) -> Result<Vec<TraceResult>, TransportError> {
251 if let Some(traces) = self.storage_read().geth_block_traces.get(&block_hash).cloned() {
252 return Ok(traces);
253 }
254
255 let trace_results = self.provider().debug_trace_block_by_hash(block_hash, opts).await?;
256
257 let mut storage = self.storage_write();
258 storage.geth_block_traces.insert(block_hash, trace_results.clone());
259
260 Ok(trace_results)
261 }
262
263 pub async fn debug_trace_block_by_number(
264 &self,
265 number: u64,
266 opts: GethDebugTracingOptions,
267 ) -> Result<Vec<TraceResult>, TransportError> {
268 if let Ok(Some(block)) = self.provider().get_block_by_number(number.into()).await {
269 let block_hash = block.header().hash();
270 return self.debug_trace_block_by_hash(block_hash, opts).await;
271 }
272
273 self.provider().debug_trace_block_by_number(number.into(), opts).await
274 }
275
276 pub async fn trace_block(&self, number: u64) -> Result<Vec<Trace>, TransportError> {
277 if let Some(traces) = self.storage_read().block_traces.get(&number).cloned() {
278 return Ok(traces);
279 }
280
281 let traces =
282 self.provider().trace_block(number.into()).await?.into_iter().collect::<Vec<_>>();
283
284 let mut storage = self.storage_write();
285 storage.block_traces.insert(number, traces.clone());
286
287 Ok(traces)
288 }
289
290 pub async fn trace_replay_block_transactions(
291 &self,
292 number: u64,
293 trace_types: HashSet<TraceType>,
294 ) -> Result<Vec<TraceResultsWithTransactionHash>, TransportError> {
295 let params = (number, trace_types.iter().map(|t| format!("{t:?}")).collect::<Vec<_>>());
297 self.provider().raw_request("trace_replayBlockTransactions".into(), params).await
298 }
299
300 pub async fn reset(
302 &self,
303 urls: Vec<String>,
304 block_number: impl Into<BlockId>,
305 ) -> Result<(), BlockchainError> {
306 let block_number = block_number.into();
307 {
308 self.database
309 .write()
310 .await
311 .maybe_reset(urls.clone(), block_number)
312 .map_err(BlockchainError::Internal)?;
313 }
314
315 if !urls.is_empty() {
316 self.config.write().update_urls(urls)?;
317 let override_chain_id = self.config.read().override_chain_id;
318 let chain_id = if let Some(chain_id) = override_chain_id {
319 chain_id
320 } else {
321 self.provider().get_chain_id().await?
322 };
323 self.config.write().chain_id = chain_id;
324 }
325
326 let provider = self.provider();
327 let block =
328 provider.get_block(block_number).await?.ok_or(BlockchainError::BlockNotFound)?;
329 let block_hash = block.header().hash();
330 let timestamp = block.header().timestamp();
331 let base_fee = block.header().base_fee_per_gas();
332 let total_difficulty = block.header().difficulty();
333
334 let number = block.header().number();
335 self.config.write().update_block(
336 number,
337 block_hash,
338 timestamp,
339 base_fee.map(|g| g as u128),
340 total_difficulty,
341 );
342
343 self.clear_cached_storage();
344
345 self.database.write().await.insert_block_hash(U256::from(number), block_hash);
346
347 Ok(())
348 }
349
350 pub async fn call(
352 &self,
353 request: &N::TransactionRequest,
354 block: Option<BlockNumber>,
355 ) -> Result<Bytes, TransportError> {
356 let block = block.unwrap_or(BlockNumber::Latest);
357 let res = self.provider().call(request.clone()).block(block.into()).await?;
358
359 Ok(res)
360 }
361
362 pub async fn simulate_v1(
364 &self,
365 request: &SimulatePayload,
366 block: Option<BlockNumber>,
367 ) -> Result<Vec<SimulatedBlock<N::BlockResponse>>, TransportError> {
368 let mut simulate_call = self.provider().simulate(request);
369 if let Some(n) = block {
370 simulate_call = simulate_call.number(n.as_number().unwrap());
371 }
372
373 let res = simulate_call.await?;
374
375 Ok(res)
376 }
377
378 pub async fn estimate_gas(
380 &self,
381 request: &N::TransactionRequest,
382 block: Option<BlockNumber>,
383 ) -> Result<u128, TransportError> {
384 let block = block.unwrap_or_default();
385 let res = self.provider().estimate_gas(request.clone()).block(block.into()).await?;
386
387 Ok(res as u128)
388 }
389
390 pub async fn create_access_list(
392 &self,
393 request: &N::TransactionRequest,
394 block: Option<BlockNumber>,
395 ) -> Result<AccessListResult, TransportError> {
396 self.provider().create_access_list(request).block_id(block.unwrap_or_default().into()).await
397 }
398
399 pub async fn transaction_by_block_number_and_index(
400 &self,
401 number: u64,
402 index: usize,
403 ) -> Result<Option<N::TransactionResponse>, TransportError> {
404 let block = self.block_by_number(number).await?;
405 self.transaction_at_block_index(block, index).await
406 }
407
408 pub async fn transaction_by_block_hash_and_index(
409 &self,
410 hash: B256,
411 index: usize,
412 ) -> Result<Option<N::TransactionResponse>, TransportError> {
413 let block = self.block_by_hash(hash).await?;
414 self.transaction_at_block_index(block, index).await
415 }
416
417 async fn transaction_at_block_index(
418 &self,
419 block: Option<N::BlockResponse>,
420 index: usize,
421 ) -> Result<Option<N::TransactionResponse>, TransportError> {
422 if let Some(block) = block {
423 match block.transactions() {
424 BlockTransactions::Full(txs) => {
425 if let Some(tx) = txs.get(index) {
426 return Ok(Some(tx.clone()));
427 }
428 }
429 BlockTransactions::Hashes(hashes) => {
430 if let Some(tx_hash) = hashes.get(index) {
431 return self.transaction_by_hash(*tx_hash).await;
432 }
433 }
434 BlockTransactions::Uncle => {}
435 }
436 }
437 Ok(None)
438 }
439
440 pub async fn transaction_by_hash(
441 &self,
442 hash: B256,
443 ) -> Result<Option<N::TransactionResponse>, TransportError> {
444 trace!(target: "backend::fork", "transaction_by_hash={:?}", hash);
445 if let tx @ Some(_) = self.storage_read().transactions.get(&hash).cloned() {
446 return Ok(tx);
447 }
448
449 let tx = self.provider().get_transaction_by_hash(hash).await?;
450 if let Some(tx) = tx.clone() {
451 let mut storage = self.storage_write();
452 storage.transactions.insert(hash, tx);
453 }
454 Ok(tx)
455 }
456
457 pub async fn block_by_hash(
458 &self,
459 hash: B256,
460 ) -> Result<Option<N::BlockResponse>, TransportError> {
461 if let Some(mut block) = self.storage_read().blocks.get(&hash).cloned() {
462 block.transactions_mut().convert_to_hashes();
463 return Ok(Some(block));
464 }
465
466 Ok(self.fetch_full_block(hash).await?.map(|mut b| {
467 b.transactions_mut().convert_to_hashes();
468 b
469 }))
470 }
471
472 pub async fn block_by_hash_full(
473 &self,
474 hash: B256,
475 ) -> Result<Option<N::BlockResponse>, TransportError> {
476 if let Some(block) = self.storage_read().blocks.get(&hash).cloned()
477 && let Some(block) = self.convert_to_full_block(block)
478 {
479 return Ok(Some(block));
480 }
481 self.fetch_full_block(hash).await
482 }
483
484 pub async fn block_by_number(
485 &self,
486 block_number: u64,
487 ) -> Result<Option<N::BlockResponse>, TransportError> {
488 if let Some(mut block) = self
489 .storage_read()
490 .hashes
491 .get(&block_number)
492 .and_then(|hash| self.storage_read().blocks.get(hash).cloned())
493 {
494 block.transactions_mut().convert_to_hashes();
495 return Ok(Some(block));
496 }
497
498 let mut block = self.fetch_full_block(block_number).await?;
499 if let Some(block) = &mut block {
500 block.transactions_mut().convert_to_hashes();
501 }
502 Ok(block)
503 }
504
505 pub async fn block_by_number_full(
506 &self,
507 block_number: u64,
508 ) -> Result<Option<N::BlockResponse>, TransportError> {
509 if let Some(block) = self
510 .storage_read()
511 .hashes
512 .get(&block_number)
513 .copied()
514 .and_then(|hash| self.storage_read().blocks.get(&hash).cloned())
515 && let Some(block) = self.convert_to_full_block(block)
516 {
517 return Ok(Some(block));
518 }
519
520 self.fetch_full_block(block_number).await
521 }
522
523 async fn fetch_full_block(
524 &self,
525 block_id: impl Into<BlockId>,
526 ) -> Result<Option<N::BlockResponse>, TransportError> {
527 if let Some(block) = self.provider().get_block(block_id.into()).full().await? {
528 let hash = block.header().hash();
529 let block_number = block.header().number();
530 let mut storage = self.storage_write();
531 let block_txs = match block.transactions() {
533 BlockTransactions::Full(txs) => txs.to_owned(),
534 _ => vec![],
535 };
536 storage.transactions.extend(block_txs.iter().map(|tx| (tx.tx_hash(), tx.clone())));
537 storage.hashes.insert(block_number, hash);
538 storage.blocks.insert(hash, block.clone());
539 return Ok(Some(block));
540 }
541
542 Ok(None)
543 }
544
545 fn convert_to_full_block(&self, mut block: N::BlockResponse) -> Option<N::BlockResponse> {
547 let storage = self.storage.read();
548 let transactions = block
549 .transactions()
550 .hashes()
551 .map(|hash| storage.transactions.get(&hash).cloned())
552 .collect::<Option<Vec<_>>>()?;
553 *block.transactions_mut() = BlockTransactions::Full(transactions);
554 Some(block)
555 }
556}
557
558impl ClientFork {
559 pub async fn transaction_receipt(
560 &self,
561 hash: B256,
562 ) -> Result<Option<FoundryTxReceipt>, BlockchainError> {
563 if let Some(receipt) = self.storage_read().transaction_receipts.get(&hash).cloned() {
564 return Ok(Some(receipt));
565 }
566
567 if let Some(receipt) = self.provider().get_transaction_receipt(hash).await? {
568 let receipt = FoundryTxReceipt::try_from(receipt)
569 .map_err(|_| BlockchainError::FailedToDecodeReceipt)?;
570 let mut storage = self.storage_write();
571 storage.transaction_receipts.insert(hash, receipt.clone());
572 return Ok(Some(receipt));
573 }
574
575 Ok(None)
576 }
577
578 pub async fn block_receipts(
579 &self,
580 number: u64,
581 ) -> Result<Option<Vec<FoundryTxReceipt>>, BlockchainError> {
582 if let receipts @ Some(_) = self.storage_read().block_receipts.get(&number).cloned() {
583 return Ok(receipts);
584 }
585
586 if self.predates_fork_inclusive(number) {
590 let receipts = self.provider().get_block_receipts(BlockId::from(number)).await?;
591 let receipts = receipts
592 .map(|r| {
593 r.into_iter()
594 .map(|r| {
595 FoundryTxReceipt::try_from(r)
596 .map_err(|_| BlockchainError::FailedToDecodeReceipt)
597 })
598 .collect::<Result<Vec<_>, _>>()
599 })
600 .transpose()?;
601
602 if let Some(receipts) = receipts.clone() {
603 let mut storage = self.storage_write();
604 storage.block_receipts.insert(number, receipts);
605 }
606
607 return Ok(receipts);
608 }
609
610 Ok(None)
611 }
612
613 pub async fn uncle_by_block_hash_and_index(
614 &self,
615 hash: B256,
616 index: usize,
617 ) -> Result<Option<AnyRpcBlock>, TransportError> {
618 if let Some(block) = self.block_by_hash(hash).await? {
619 return self.uncles_by_block_and_index(block, index).await;
620 }
621 Ok(None)
622 }
623
624 pub async fn uncle_by_block_number_and_index(
625 &self,
626 number: u64,
627 index: usize,
628 ) -> Result<Option<AnyRpcBlock>, TransportError> {
629 if let Some(block) = self.block_by_number(number).await? {
630 return self.uncles_by_block_and_index(block, index).await;
631 }
632 Ok(None)
633 }
634
635 async fn uncles_by_block_and_index(
636 &self,
637 block: AnyRpcBlock,
638 index: usize,
639 ) -> Result<Option<AnyRpcBlock>, TransportError> {
640 let block_hash = block.header().hash();
641 let block_number = block.header().number();
642 if let Some(uncles) = self.storage_read().uncles.get(&block_hash) {
643 return Ok(uncles.get(index).cloned());
644 }
645
646 let mut uncles = Vec::with_capacity(block.uncles.len());
647 for (uncle_idx, _) in block.uncles.iter().enumerate() {
648 let uncle =
649 match self.provider().get_uncle(block_number.into(), uncle_idx as u64).await? {
650 Some(u) => u,
651 None => return Ok(None),
652 };
653 uncles.push(uncle);
654 }
655 self.storage_write().uncles.insert(block_hash, uncles.clone());
656 Ok(uncles.get(index).cloned())
657 }
658}
659
660#[derive(Clone, Debug)]
662pub struct ClientForkConfig<N: Network = AnyNetwork> {
663 pub fork_urls: Vec<String>,
667 pub block_number: u64,
669 pub block_hash: B256,
671 pub transaction_hash: Option<B256>,
673 pub provider: Arc<RetryProvider<N>>,
674 pub chain_id: u64,
675 pub override_chain_id: Option<u64>,
676 pub hardfork: Option<FoundryHardfork>,
678 pub timestamp: u64,
680 pub base_fee: Option<u128>,
682 pub blob_gas_used: Option<u128>,
684 pub blob_excess_gas_and_price: Option<BlobExcessGasAndPrice>,
686 pub timeout: Duration,
688 pub retries: u32,
690 pub backoff: Duration,
692 pub compute_units_per_second: u64,
694 pub headers: Vec<String>,
696 pub total_difficulty: U256,
698 pub force_transactions: Option<Vec<PoolTransaction<FoundryTxEnvelope>>>,
700}
701
702impl<N: Network> ClientForkConfig<N> {
703 pub fn eth_rpc_url(&self) -> Option<&str> {
705 self.fork_urls.first().map(|s| s.as_str())
706 }
707
708 fn update_urls(&mut self, urls: Vec<String>) -> Result<(), BlockchainError> {
714 let primary = urls.first().ok_or_else(|| {
715 BlockchainError::InvalidUrl("at least one fork URL required".to_string())
716 })?;
717
718 let builder = ProviderBuilder::<N>::new(primary.as_str())
719 .timeout(self.timeout)
720 .max_retry(self.retries)
721 .initial_backoff(self.backoff.as_millis() as u64)
722 .compute_units_per_second(self.compute_units_per_second)
723 .headers(self.headers.clone());
724
725 self.provider = Arc::new(if urls.len() > 1 {
726 builder
727 .build_fallback(urls.clone())
728 .map_err(|e| BlockchainError::InvalidUrl(format!("{primary}: {e}")))?
729 } else {
730 builder.build().map_err(|e| BlockchainError::InvalidUrl(format!("{primary}: {e}")))?
731 });
732 trace!(target: "fork", "Updated fork urls: {:?}", urls);
733 self.fork_urls = urls;
734 Ok(())
735 }
736
737 pub fn update_block(
739 &mut self,
740 block_number: u64,
741 block_hash: B256,
742 timestamp: u64,
743 base_fee: Option<u128>,
744 total_difficulty: U256,
745 ) {
746 self.block_number = block_number;
747 self.block_hash = block_hash;
748 self.timestamp = timestamp;
749 self.base_fee = base_fee;
750 self.total_difficulty = total_difficulty;
751 trace!(target: "fork", "Updated block number={} hash={:?}", block_number, block_hash);
752 }
753}
754
755#[derive(Clone, Debug)]
759pub struct ForkedStorage<N: Network = AnyNetwork> {
760 pub uncles: FbHashMap<32, Vec<N::BlockResponse>>,
761 pub blocks: FbHashMap<32, N::BlockResponse>,
762 pub hashes: HashMap<u64, B256>,
763 pub transactions: FbHashMap<32, N::TransactionResponse>,
764 pub transaction_receipts: FbHashMap<32, FoundryTxReceipt>,
765 pub transaction_traces: FbHashMap<32, Vec<Trace>>,
766 pub logs: HashMap<Filter, Vec<Log>>,
767 pub geth_transaction_traces: FbHashMap<32, GethTrace>,
768 pub geth_block_traces: FbHashMap<32, Vec<TraceResult>>,
769 pub block_traces: HashMap<u64, Vec<Trace>>,
770 pub block_receipts: HashMap<u64, Vec<FoundryTxReceipt>>,
771 pub code_at: HashMap<(Address, u64), Bytes>,
772}
773
774impl<N: Network> Default for ForkedStorage<N> {
775 fn default() -> Self {
776 Self {
777 uncles: Default::default(),
778 blocks: Default::default(),
779 hashes: Default::default(),
780 transactions: Default::default(),
781 transaction_receipts: Default::default(),
782 transaction_traces: Default::default(),
783 logs: Default::default(),
784 geth_transaction_traces: Default::default(),
785 geth_block_traces: Default::default(),
786 block_traces: Default::default(),
787 block_receipts: Default::default(),
788 code_at: Default::default(),
789 }
790 }
791}
792
793impl<N: Network> ForkedStorage<N> {
794 pub fn clear(&mut self) {
796 *self = Self::default()
798 }
799}