1use crate::eth::{backend::db::Db, error::BlockchainError};
4use alloy_chains::NamedChain;
5use alloy_consensus::{BlockHeader, TrieAccount};
6use alloy_eips::eip2930::AccessListResult;
7use alloy_network::{
8 AnyNetwork, AnyRpcBlock, BlockResponse, Network, TransactionResponse,
9 primitives::HeaderResponse,
10};
11use alloy_primitives::{
12 Address, B256, Bytes, StorageValue, U256,
13 map::{FbHashMap, HashMap, HashSet},
14};
15use alloy_provider::{
16 Provider,
17 ext::{DebugApi, TraceApi},
18};
19use alloy_rpc_types::{
20 BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, EIP1186AccountProofResponse,
21 FeeHistory, Filter, Index, Log,
22 request::TransactionRequest,
23 simulate::{SimulatePayload, SimulatedBlock},
24 state::StateOverride,
25 trace::{
26 geth::{GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, TraceResult},
27 opcode::{BlockOpcodeGas, TransactionOpcodeGas},
28 parity::{
29 LocalizedTransactionTrace as Trace, TraceResults, TraceResultsWithTransactionHash,
30 TraceType,
31 },
32 },
33};
34use alloy_rpc_types_eth::{AccountInfo, Bundle, EthCallResponse, StateContext};
35use alloy_rpc_types_mev::{EthCallBundle, EthCallBundleResponse};
36use alloy_serde::WithOtherFields;
37use alloy_transport::TransportError;
38use foundry_common::provider::{ProviderBuilder, RetryProvider};
39use foundry_evm::hardfork::FoundryHardfork;
40use foundry_primitives::FoundryTxReceipt;
41use parking_lot::{
42 RawRwLock, RwLock,
43 lock_api::{RwLockReadGuard, RwLockWriteGuard},
44};
45use revm::context_interface::block::BlobExcessGasAndPrice;
46use std::{sync::Arc, time::Duration};
47use tokio::sync::RwLock as AsyncRwLock;
48
49pub(crate) fn ensure_fork_network_supported(chain_id: u64) -> Result<(), BlockchainError> {
51 if matches!(NamedChain::try_from(chain_id), Ok(NamedChain::ZkSync | NamedChain::ZkSyncTestnet))
52 {
53 return Err(BlockchainError::UnsupportedForkNetwork {
54 chain_id,
55 reason: "Anvil's EVM backend cannot execute native EraVM bytecode; use `anvil-zksync` for zkSync Era forks",
56 });
57 }
58 Ok(())
59}
60
61#[derive(Clone, Debug)]
66pub struct ClientFork<N: Network = AnyNetwork> {
67 pub storage: Arc<RwLock<ForkedStorage<N>>>,
69 pub config: Arc<RwLock<ClientForkConfig<N>>>,
73 pub database: Arc<AsyncRwLock<Box<dyn Db>>>,
75 database_rpc_url: Arc<RwLock<Option<String>>>,
77}
78
79impl<N: Network> ClientFork<N> {
80 pub fn new(config: ClientForkConfig<N>, database: Arc<AsyncRwLock<Box<dyn Db>>>) -> Self {
82 let database_rpc_url = config.eth_rpc_url().map(ToOwned::to_owned);
83 Self {
84 storage: Default::default(),
85 config: Arc::new(RwLock::new(config)),
86 database,
87 database_rpc_url: Arc::new(RwLock::new(database_rpc_url)),
88 }
89 }
90
91 pub fn clear_cached_storage(&self) {
93 self.storage.write().clear()
94 }
95
96 pub fn predates_fork(&self, block: u64) -> bool {
98 block < self.block_number()
99 }
100
101 pub fn predates_fork_inclusive(&self, block: u64) -> bool {
103 block <= self.block_number()
104 }
105
106 pub fn timestamp(&self) -> u64 {
107 self.config.read().timestamp
108 }
109
110 pub fn block_number(&self) -> u64 {
111 self.config.read().block_number
112 }
113
114 pub fn transaction_hash(&self) -> Option<B256> {
116 self.config.read().transaction_hash
117 }
118
119 pub fn total_difficulty(&self) -> U256 {
120 self.config.read().total_difficulty
121 }
122
123 pub fn base_fee(&self) -> Option<u128> {
124 self.config.read().base_fee
125 }
126
127 pub fn block_hash(&self) -> B256 {
128 self.config.read().block_hash
129 }
130
131 pub fn eth_rpc_url(&self) -> Option<String> {
132 self.config.read().eth_rpc_url().map(|s| s.to_string())
133 }
134
135 pub(crate) fn database_rpc_url(&self) -> Option<String> {
136 self.database_rpc_url.read().clone()
137 }
138
139 pub(crate) fn set_database_rpc_url(&self, url: Option<String>) {
140 *self.database_rpc_url.write() = url;
141 }
142
143 pub fn chain_id(&self) -> u64 {
144 self.config.read().chain_id
145 }
146
147 fn provider(&self) -> Arc<RetryProvider<N>> {
148 self.config.read().provider.clone()
149 }
150
151 fn storage_read(&self) -> RwLockReadGuard<'_, RawRwLock, ForkedStorage<N>> {
152 self.storage.read()
153 }
154
155 fn storage_write(&self) -> RwLockWriteGuard<'_, RawRwLock, ForkedStorage<N>> {
156 self.storage.write()
157 }
158
159 pub async fn fee_history(
161 &self,
162 block_count: u64,
163 newest_block: BlockNumber,
164 reward_percentiles: &[f64],
165 ) -> Result<FeeHistory, TransportError> {
166 self.provider().get_fee_history(block_count, newest_block, reward_percentiles).await
167 }
168
169 pub async fn get_proof(
171 &self,
172 address: Address,
173 keys: Vec<B256>,
174 block_number: Option<BlockId>,
175 ) -> Result<EIP1186AccountProofResponse, TransportError> {
176 self.provider().get_proof(address, keys).block_id(block_number.unwrap_or_default()).await
177 }
178
179 pub async fn block_access_list(
181 &self,
182 block_id: BlockId,
183 ) -> Result<Option<serde_json::Value>, TransportError> {
184 self.provider().raw_request("eth_getBlockAccessList".into(), (block_id,)).await
185 }
186
187 pub async fn block_access_list_by_hash(
189 &self,
190 block_hash: B256,
191 ) -> Result<Option<serde_json::Value>, TransportError> {
192 self.provider().raw_request("eth_getBlockAccessListByBlockHash".into(), (block_hash,)).await
193 }
194
195 pub async fn block_access_list_by_number(
197 &self,
198 block_number: BlockNumber,
199 ) -> Result<Option<serde_json::Value>, TransportError> {
200 self.provider()
201 .raw_request("eth_getBlockAccessListByBlockNumber".into(), (block_number,))
202 .await
203 }
204
205 pub async fn block_access_list_raw(
207 &self,
208 block_id: BlockId,
209 ) -> Result<Option<Bytes>, TransportError> {
210 self.provider().raw_request("eth_getBlockAccessListRaw".into(), (block_id,)).await
211 }
212
213 pub async fn storage_at(
214 &self,
215 address: Address,
216 index: U256,
217 number: Option<BlockNumber>,
218 ) -> Result<StorageValue, TransportError> {
219 self.provider()
220 .get_storage_at(address, index)
221 .block_id(number.unwrap_or_default().into())
222 .await
223 }
224
225 pub async fn logs(&self, filter: &Filter) -> Result<Vec<Log>, TransportError> {
226 if let Some(logs) = self.storage_read().logs.get(filter).cloned() {
227 return Ok(logs);
228 }
229
230 let logs = self.provider().get_logs(filter).await?;
231
232 let mut storage = self.storage_write();
233 storage.logs.insert(filter.clone(), logs.clone());
234 Ok(logs)
235 }
236
237 pub async fn get_code(
238 &self,
239 address: Address,
240 blocknumber: u64,
241 ) -> Result<Bytes, TransportError> {
242 trace!(target: "backend::fork", "get_code={:?}", address);
243 if let Some(code) = self.storage_read().code_at.get(&(address, blocknumber)).cloned() {
244 return Ok(code);
245 }
246
247 let block_id = BlockId::number(blocknumber);
248
249 let code = self.provider().get_code_at(address).block_id(block_id).await?;
250
251 let mut storage = self.storage_write();
252 storage.code_at.insert((address, blocknumber), code.clone());
253
254 Ok(code)
255 }
256
257 pub async fn get_balance(
258 &self,
259 address: Address,
260 blocknumber: u64,
261 ) -> Result<U256, TransportError> {
262 trace!(target: "backend::fork", "get_balance={:?}", address);
263 self.provider().get_balance(address).block_id(blocknumber.into()).await
264 }
265
266 pub async fn get_nonce(&self, address: Address, block: u64) -> Result<u64, TransportError> {
267 trace!(target: "backend::fork", "get_nonce={:?}", address);
268 self.provider().get_transaction_count(address).block_id(block.into()).await
269 }
270
271 pub async fn get_account(
272 &self,
273 address: Address,
274 blocknumber: u64,
275 ) -> Result<TrieAccount, TransportError> {
276 trace!(target: "backend::fork", "get_account={:?}", address);
277 self.provider().get_account(address).block_id(blocknumber.into()).await
278 }
279
280 pub async fn trace_transaction(&self, hash: B256) -> Result<Vec<Trace>, TransportError> {
281 if let Some(traces) = self.storage_read().transaction_traces.get(&hash).cloned() {
282 return Ok(traces);
283 }
284
285 let traces = self.provider().trace_transaction(hash).await?.into_iter().collect::<Vec<_>>();
286
287 let mut storage = self.storage_write();
288 storage.transaction_traces.insert(hash, traces.clone());
289
290 Ok(traces)
291 }
292
293 pub async fn trace_transaction_opcode_gas(
294 &self,
295 hash: B256,
296 ) -> Result<Option<TransactionOpcodeGas>, TransportError> {
297 self.provider().raw_request("trace_transactionOpcodeGas".into(), (hash,)).await
298 }
299
300 pub async fn trace_call(
302 &self,
303 request: WithOtherFields<TransactionRequest>,
304 trace_types: HashSet<TraceType>,
305 block: BlockId,
306 ) -> Result<TraceResults, TransportError> {
307 self.provider().raw_request("trace_call".into(), (request, trace_types, block)).await
308 }
309
310 pub async fn trace_get(
312 &self,
313 hash: B256,
314 indices: Vec<Index>,
315 ) -> Result<Option<Trace>, TransportError> {
316 self.provider().raw_request("trace_get".into(), (hash, indices)).await
317 }
318
319 pub async fn debug_trace_transaction(
320 &self,
321 hash: B256,
322 opts: GethDebugTracingOptions,
323 ) -> Result<GethTrace, TransportError> {
324 if let Some(traces) = self.storage_read().geth_transaction_traces.get(&hash).cloned() {
325 return Ok(traces);
326 }
327
328 let trace = self.provider().debug_trace_transaction(hash, opts).await?;
329
330 let mut storage = self.storage_write();
331 storage.geth_transaction_traces.insert(hash, trace.clone());
332
333 Ok(trace)
334 }
335
336 pub async fn debug_trace_call(
337 &self,
338 request: WithOtherFields<TransactionRequest>,
339 block_id: BlockId,
340 opts: GethDebugTracingCallOptions,
341 ) -> Result<GethTrace, TransportError> {
342 self.provider().raw_request("debug_traceCall".into(), (request, block_id, opts)).await
343 }
344
345 pub async fn debug_code_by_hash(
346 &self,
347 code_hash: B256,
348 block_id: Option<BlockId>,
349 ) -> Result<Option<Bytes>, TransportError> {
350 self.provider().debug_code_by_hash(code_hash, block_id).await
351 }
352
353 pub async fn debug_account_info_at(
354 &self,
355 block_id: BlockId,
356 tx_index: Index,
357 address: Address,
358 ) -> Result<Option<AccountInfo>, TransportError> {
359 self.provider()
360 .raw_request("debug_accountInfoAt".into(), (block_id, tx_index, address))
361 .await
362 }
363
364 pub async fn debug_trace_block_by_hash(
365 &self,
366 block_hash: B256,
367 opts: GethDebugTracingOptions,
368 ) -> Result<Vec<TraceResult>, TransportError> {
369 if let Some(traces) = self.storage_read().geth_block_traces.get(&block_hash).cloned() {
370 return Ok(traces);
371 }
372
373 let trace_results = self.provider().debug_trace_block_by_hash(block_hash, opts).await?;
374
375 let mut storage = self.storage_write();
376 storage.geth_block_traces.insert(block_hash, trace_results.clone());
377
378 Ok(trace_results)
379 }
380
381 pub async fn debug_trace_block_by_number(
382 &self,
383 number: u64,
384 opts: GethDebugTracingOptions,
385 ) -> Result<Vec<TraceResult>, TransportError> {
386 if let Ok(Some(block)) = self.provider().get_block_by_number(number.into()).await {
387 let block_hash = block.header().hash();
388 return self.debug_trace_block_by_hash(block_hash, opts).await;
389 }
390
391 self.provider().debug_trace_block_by_number(number.into(), opts).await
392 }
393
394 pub async fn trace_block(&self, number: u64) -> Result<Vec<Trace>, TransportError> {
395 if let Some(traces) = self.storage_read().block_traces.get(&number).cloned() {
396 return Ok(traces);
397 }
398
399 let traces =
400 self.provider().trace_block(number.into()).await?.into_iter().collect::<Vec<_>>();
401
402 let mut storage = self.storage_write();
403 storage.block_traces.insert(number, traces.clone());
404
405 Ok(traces)
406 }
407
408 pub async fn trace_replay_block_transactions(
409 &self,
410 number: u64,
411 trace_types: HashSet<TraceType>,
412 ) -> Result<Vec<TraceResultsWithTransactionHash>, TransportError> {
413 self.provider()
416 .trace_replay_block_transactions(BlockId::number(number))
417 .trace_types(trace_types)
418 .await
419 }
420
421 pub async fn trace_replay_transaction(
422 &self,
423 hash: B256,
424 trace_types: HashSet<TraceType>,
425 ) -> Result<TraceResults, TransportError> {
426 self.provider().raw_request("trace_replayTransaction".into(), (hash, trace_types)).await
427 }
428
429 pub async fn trace_block_opcode_gas(
430 &self,
431 block_id: BlockId,
432 ) -> Result<Option<BlockOpcodeGas>, TransportError> {
433 self.provider().raw_request("trace_blockOpcodeGas".into(), (block_id,)).await
434 }
435
436 pub async fn reset(
438 &self,
439 urls: Vec<String>,
440 block_number: impl Into<BlockId>,
441 ) -> Result<(), BlockchainError> {
442 let block_number = block_number.into();
443 self.prepare_reset(urls.clone(), block_number).await?;
444 {
445 self.database
446 .write()
447 .await
448 .maybe_reset(urls.clone(), block_number)
449 .map_err(BlockchainError::Internal)?;
450 }
451
452 let number = self.block_number();
453 let block_hash = self.block_hash();
454 self.database.write().await.insert_block_hash(U256::from(number), block_hash);
455
456 Ok(())
457 }
458
459 pub(crate) async fn prepare_reset(
461 &self,
462 urls: Vec<String>,
463 block_number: BlockId,
464 ) -> Result<(), BlockchainError> {
465 let (provider, source_chain_id) = if urls.is_empty() {
466 (self.provider(), None)
467 } else {
468 let config = self.config.read().clone();
469 let (provider, source_chain_id) = config.validated_provider_for_urls(&urls).await?;
470 (provider, Some(source_chain_id))
471 };
472 let block =
473 provider.get_block(block_number).await?.ok_or(BlockchainError::BlockNotFound)?;
474 let block_hash = block.header().hash();
475 let timestamp = block.header().timestamp();
476 let base_fee = block.header().base_fee_per_gas();
477 let total_difficulty = block.header().difficulty();
478
479 let number = block.header().number();
480 let mut config = self.config.write();
481 if let Some(source_chain_id) = source_chain_id {
482 if config.override_chain_id.is_none() {
483 config.chain_id = source_chain_id;
484 }
485 config.provider = provider;
486 config.fork_urls = urls;
487 }
488 config.update_block(
489 number,
490 block_hash,
491 timestamp,
492 base_fee.map(|g| g as u128),
493 total_difficulty,
494 );
495
496 self.clear_cached_storage();
497
498 Ok(())
499 }
500
501 pub async fn call(
503 &self,
504 request: &N::TransactionRequest,
505 block: Option<BlockNumber>,
506 ) -> Result<Bytes, TransportError> {
507 let block = block.unwrap_or(BlockNumber::Latest);
508 let res = self.provider().call(request.clone()).block(block.into()).await?;
509
510 Ok(res)
511 }
512
513 pub async fn call_raw(
515 &self,
516 request: &WithOtherFields<TransactionRequest>,
517 block: Option<BlockNumber>,
518 ) -> Result<Bytes, TransportError> {
519 self.provider()
520 .raw_request("eth_call".into(), (request, block.unwrap_or(BlockNumber::Latest)))
521 .await
522 }
523
524 pub async fn call_many(
526 &self,
527 bundles: Vec<Bundle<WithOtherFields<TransactionRequest>>>,
528 state_context: Option<StateContext>,
529 state_override: Option<StateOverride>,
530 ) -> Result<Vec<Vec<EthCallResponse>>, TransportError> {
531 self.provider()
532 .raw_request("eth_callMany".into(), (bundles, state_context, state_override))
533 .await
534 }
535
536 pub async fn call_bundle(
538 &self,
539 bundle: EthCallBundle,
540 ) -> Result<EthCallBundleResponse, TransportError> {
541 self.provider().raw_request("eth_callBundle".into(), (bundle,)).await
542 }
543
544 pub async fn simulate_v1(
546 &self,
547 request: &SimulatePayload<WithOtherFields<TransactionRequest>>,
548 block: Option<BlockId>,
549 ) -> Result<Vec<SimulatedBlock<N::BlockResponse>>, TransportError> {
550 self.provider().raw_request("eth_simulateV1".into(), (request, block)).await
551 }
552
553 pub async fn estimate_gas(
555 &self,
556 request: &N::TransactionRequest,
557 block: Option<BlockNumber>,
558 ) -> Result<u128, TransportError> {
559 let block = block.unwrap_or_default();
560 let res = self.provider().estimate_gas(request.clone()).block(block.into()).await?;
561
562 Ok(res as u128)
563 }
564
565 pub async fn estimate_gas_raw(
567 &self,
568 request: &WithOtherFields<TransactionRequest>,
569 block: Option<BlockNumber>,
570 ) -> Result<u128, TransportError> {
571 let gas: U256 = self
572 .provider()
573 .raw_request("eth_estimateGas".into(), (request, block.unwrap_or_default()))
574 .await?;
575 Ok(gas.saturating_to())
576 }
577
578 pub async fn create_access_list(
580 &self,
581 request: &N::TransactionRequest,
582 block: Option<BlockNumber>,
583 ) -> Result<AccessListResult, TransportError> {
584 self.provider().create_access_list(request).block_id(block.unwrap_or_default().into()).await
585 }
586
587 pub async fn create_access_list_raw(
589 &self,
590 request: &WithOtherFields<TransactionRequest>,
591 block: Option<BlockNumber>,
592 ) -> Result<AccessListResult, TransportError> {
593 self.provider()
594 .raw_request("eth_createAccessList".into(), (request, block.unwrap_or_default()))
595 .await
596 }
597
598 pub async fn transaction_by_block_number_and_index(
599 &self,
600 number: u64,
601 index: usize,
602 ) -> Result<Option<N::TransactionResponse>, TransportError> {
603 let block = self.block_by_number(number).await?;
604 self.transaction_at_block_index(block, index).await
605 }
606
607 pub async fn transaction_by_block_hash_and_index(
608 &self,
609 hash: B256,
610 index: usize,
611 ) -> Result<Option<N::TransactionResponse>, TransportError> {
612 let block = self.block_by_hash(hash).await?;
613 self.transaction_at_block_index(block, index).await
614 }
615
616 async fn transaction_at_block_index(
617 &self,
618 block: Option<N::BlockResponse>,
619 index: usize,
620 ) -> Result<Option<N::TransactionResponse>, TransportError> {
621 if let Some(block) = block {
622 match block.transactions() {
623 BlockTransactions::Full(txs) => {
624 if let Some(tx) = txs.get(index) {
625 return Ok(Some(tx.clone()));
626 }
627 }
628 BlockTransactions::Hashes(hashes) => {
629 if let Some(tx_hash) = hashes.get(index) {
630 return self.transaction_by_hash(*tx_hash).await;
631 }
632 }
633 BlockTransactions::Uncle => {}
634 }
635 }
636 Ok(None)
637 }
638
639 pub async fn transaction_by_hash(
640 &self,
641 hash: B256,
642 ) -> Result<Option<N::TransactionResponse>, TransportError> {
643 trace!(target: "backend::fork", "transaction_by_hash={:?}", hash);
644 if let tx @ Some(_) = self.storage_read().transactions.get(&hash).cloned() {
645 return Ok(tx);
646 }
647
648 let tx = self.provider().get_transaction_by_hash(hash).await?;
649 if let Some(tx) = tx.clone() {
650 let mut storage = self.storage_write();
651 storage.transactions.insert(hash, tx);
652 }
653 Ok(tx)
654 }
655
656 pub async fn block_by_hash(
657 &self,
658 hash: B256,
659 ) -> Result<Option<N::BlockResponse>, TransportError> {
660 if let Some(mut block) = self.storage_read().blocks.get(&hash).cloned() {
661 block.transactions_mut().convert_to_hashes();
662 return Ok(Some(block));
663 }
664
665 Ok(self.fetch_full_block(hash).await?.map(|mut b| {
666 b.transactions_mut().convert_to_hashes();
667 b
668 }))
669 }
670
671 pub async fn block_by_hash_full(
672 &self,
673 hash: B256,
674 ) -> Result<Option<N::BlockResponse>, TransportError> {
675 if let Some(block) = self.storage_read().blocks.get(&hash).cloned()
676 && let Some(block) = self.convert_to_full_block(block)
677 {
678 return Ok(Some(block));
679 }
680 self.fetch_full_block(hash).await
681 }
682
683 pub async fn block_by_number(
684 &self,
685 block_number: u64,
686 ) -> Result<Option<N::BlockResponse>, TransportError> {
687 if let Some(mut block) = self
688 .storage_read()
689 .hashes
690 .get(&block_number)
691 .and_then(|hash| self.storage_read().blocks.get(hash).cloned())
692 {
693 block.transactions_mut().convert_to_hashes();
694 return Ok(Some(block));
695 }
696
697 let mut block = self.fetch_full_block(block_number).await?;
698 if let Some(block) = &mut block {
699 block.transactions_mut().convert_to_hashes();
700 }
701 Ok(block)
702 }
703
704 pub async fn block_by_number_full(
705 &self,
706 block_number: u64,
707 ) -> Result<Option<N::BlockResponse>, TransportError> {
708 if let Some(block) = self
709 .storage_read()
710 .hashes
711 .get(&block_number)
712 .copied()
713 .and_then(|hash| self.storage_read().blocks.get(&hash).cloned())
714 && let Some(block) = self.convert_to_full_block(block)
715 {
716 return Ok(Some(block));
717 }
718
719 self.fetch_full_block(block_number).await
720 }
721
722 pub async fn fetch_block(
724 &self,
725 block_id: BlockId,
726 ) -> Result<Option<N::BlockResponse>, TransportError> {
727 self.fetch_full_block(block_id).await
728 }
729
730 async fn fetch_full_block(
731 &self,
732 block_id: impl Into<BlockId>,
733 ) -> Result<Option<N::BlockResponse>, TransportError> {
734 if let Some(block) = self.provider().get_block(block_id.into()).full().await? {
735 let hash = block.header().hash();
736 let block_number = block.header().number();
737 let mut storage = self.storage_write();
738 let block_txs = match block.transactions() {
740 BlockTransactions::Full(txs) => txs.to_owned(),
741 _ => vec![],
742 };
743 storage.transactions.extend(block_txs.iter().map(|tx| (tx.tx_hash(), tx.clone())));
744 storage.hashes.insert(block_number, hash);
745 storage.blocks.insert(hash, block.clone());
746 return Ok(Some(block));
747 }
748
749 Ok(None)
750 }
751
752 fn convert_to_full_block(&self, mut block: N::BlockResponse) -> Option<N::BlockResponse> {
754 let storage = self.storage.read();
755 let transactions = block
756 .transactions()
757 .hashes()
758 .map(|hash| storage.transactions.get(&hash).cloned())
759 .collect::<Option<Vec<_>>>()?;
760 *block.transactions_mut() = BlockTransactions::Full(transactions);
761 Some(block)
762 }
763}
764
765impl ClientFork {
766 pub async fn transaction_receipt(
767 &self,
768 hash: B256,
769 ) -> Result<Option<FoundryTxReceipt>, BlockchainError> {
770 if let Some(receipt) = self.storage_read().transaction_receipts.get(&hash).cloned() {
771 return Ok(Some(receipt));
772 }
773
774 if let Some(receipt) = self.provider().get_transaction_receipt(hash).await? {
775 let receipt = FoundryTxReceipt::try_from(receipt)
776 .map_err(|_| BlockchainError::FailedToDecodeReceipt)?;
777 let mut storage = self.storage_write();
778 storage.transaction_receipts.insert(hash, receipt.clone());
779 return Ok(Some(receipt));
780 }
781
782 Ok(None)
783 }
784
785 pub async fn block_receipts(
786 &self,
787 number: u64,
788 ) -> Result<Option<Vec<FoundryTxReceipt>>, BlockchainError> {
789 if let receipts @ Some(_) = self.storage_read().block_receipts.get(&number).cloned() {
790 return Ok(receipts);
791 }
792
793 if self.predates_fork_inclusive(number) {
797 let receipts = self.provider().get_block_receipts(BlockId::from(number)).await?;
798 let receipts = receipts
799 .map(|r| {
800 r.into_iter()
801 .map(|r| {
802 FoundryTxReceipt::try_from(r)
803 .map_err(|_| BlockchainError::FailedToDecodeReceipt)
804 })
805 .collect::<Result<Vec<_>, _>>()
806 })
807 .transpose()?;
808
809 if let Some(receipts) = receipts.clone() {
810 let mut storage = self.storage_write();
811 storage.block_receipts.insert(number, receipts);
812 }
813
814 return Ok(receipts);
815 }
816
817 Ok(None)
818 }
819
820 pub async fn uncle_by_block_hash_and_index(
821 &self,
822 hash: B256,
823 index: usize,
824 ) -> Result<Option<AnyRpcBlock>, TransportError> {
825 if let Some(block) = self.block_by_hash(hash).await? {
826 return self.uncles_by_block_and_index(block, index).await;
827 }
828 Ok(None)
829 }
830
831 pub async fn uncle_by_block_number_and_index(
832 &self,
833 number: u64,
834 index: usize,
835 ) -> Result<Option<AnyRpcBlock>, TransportError> {
836 if let Some(block) = self.block_by_number(number).await? {
837 return self.uncles_by_block_and_index(block, index).await;
838 }
839 Ok(None)
840 }
841
842 async fn uncles_by_block_and_index(
843 &self,
844 block: AnyRpcBlock,
845 index: usize,
846 ) -> Result<Option<AnyRpcBlock>, TransportError> {
847 let block_hash = block.header().hash();
848 let block_number = block.header().number();
849 if let Some(uncles) = self.storage_read().uncles.get(&block_hash) {
850 return Ok(uncles.get(index).cloned());
851 }
852
853 let mut uncles = Vec::with_capacity(block.uncles.len());
854 for (uncle_idx, _) in block.uncles.iter().enumerate() {
855 let uncle =
856 match self.provider().get_uncle(block_number.into(), uncle_idx as u64).await? {
857 Some(u) => u,
858 None => return Ok(None),
859 };
860 uncles.push(uncle);
861 }
862 self.storage_write().uncles.insert(block_hash, uncles.clone());
863 Ok(uncles.get(index).cloned())
864 }
865}
866
867#[derive(Clone, Debug)]
869pub struct ClientForkConfig<N: Network = AnyNetwork> {
870 pub fork_urls: Vec<String>,
874 pub block_number: u64,
876 pub block_hash: B256,
878 pub transaction_hash: Option<B256>,
880 pub provider: Arc<RetryProvider<N>>,
881 pub chain_id: u64,
882 pub override_chain_id: Option<u64>,
883 pub hardfork: Option<FoundryHardfork>,
885 pub timestamp: u64,
887 pub base_fee: Option<u128>,
889 pub blob_gas_used: Option<u128>,
891 pub blob_excess_gas_and_price: Option<BlobExcessGasAndPrice>,
893 pub timeout: Duration,
895 pub retries: u32,
897 pub backoff: Duration,
899 pub compute_units_per_second: u64,
901 pub headers: Vec<String>,
903 pub total_difficulty: U256,
905}
906
907impl<N: Network> ClientForkConfig<N> {
908 pub fn eth_rpc_url(&self) -> Option<&str> {
910 self.fork_urls.first().map(|s| s.as_str())
911 }
912
913 pub(crate) fn provider_for_urls(
919 &self,
920 urls: &[String],
921 ) -> Result<Arc<RetryProvider<N>>, BlockchainError> {
922 let primary = urls.first().ok_or_else(|| {
923 BlockchainError::InvalidUrl("at least one fork URL required".to_string())
924 })?;
925
926 let builder = ProviderBuilder::<N>::new(primary.as_str())
927 .timeout(self.timeout)
928 .max_retry(self.retries)
929 .initial_backoff(self.backoff.as_millis() as u64)
930 .compute_units_per_second(self.compute_units_per_second)
931 .headers(self.headers.clone());
932
933 let provider = if urls.len() > 1 {
934 builder
935 .build_fallback(urls.to_vec())
936 .map_err(|e| BlockchainError::InvalidUrl(format!("{primary}: {e}")))?
937 } else {
938 builder.build().map_err(|e| BlockchainError::InvalidUrl(format!("{primary}: {e}")))?
939 };
940 Ok(Arc::new(provider))
941 }
942
943 pub(crate) async fn validated_provider_for_urls(
945 &self,
946 urls: &[String],
947 ) -> Result<(Arc<RetryProvider<N>>, u64), BlockchainError> {
948 let mut source_chain_id = None;
949 for url in urls {
950 let provider = self.provider_for_urls(std::slice::from_ref(url))?;
951 let chain_id = provider.get_chain_id().await?;
952 ensure_fork_network_supported(chain_id)?;
953 if let Some(source_chain_id) = source_chain_id
954 && chain_id != source_chain_id
955 {
956 return Err(BlockchainError::UnsupportedForkNetwork {
957 chain_id,
958 reason: "fork endpoints must use the same chain ID",
959 });
960 }
961 source_chain_id = Some(chain_id);
962 }
963
964 let source_chain_id = source_chain_id.ok_or_else(|| {
965 BlockchainError::InvalidUrl("at least one fork URL required".to_string())
966 })?;
967 Ok((self.provider_for_urls(urls)?, source_chain_id))
968 }
969
970 pub fn update_block(
972 &mut self,
973 block_number: u64,
974 block_hash: B256,
975 timestamp: u64,
976 base_fee: Option<u128>,
977 total_difficulty: U256,
978 ) {
979 self.block_number = block_number;
980 self.block_hash = block_hash;
981 self.timestamp = timestamp;
982 self.base_fee = base_fee;
983 self.total_difficulty = total_difficulty;
984 trace!(target: "fork", "Updated block number={} hash={:?}", block_number, block_hash);
985 }
986}
987
988#[derive(Clone, Debug)]
992pub struct ForkedStorage<N: Network = AnyNetwork> {
993 pub uncles: FbHashMap<32, Vec<N::BlockResponse>>,
994 pub blocks: FbHashMap<32, N::BlockResponse>,
995 pub hashes: HashMap<u64, B256>,
996 pub transactions: FbHashMap<32, N::TransactionResponse>,
997 pub transaction_receipts: FbHashMap<32, FoundryTxReceipt>,
998 pub transaction_traces: FbHashMap<32, Vec<Trace>>,
999 pub logs: HashMap<Filter, Vec<Log>>,
1000 pub geth_transaction_traces: FbHashMap<32, GethTrace>,
1001 pub geth_block_traces: FbHashMap<32, Vec<TraceResult>>,
1002 pub block_traces: HashMap<u64, Vec<Trace>>,
1003 pub block_receipts: HashMap<u64, Vec<FoundryTxReceipt>>,
1004 pub code_at: HashMap<(Address, u64), Bytes>,
1005}
1006
1007impl<N: Network> Default for ForkedStorage<N> {
1008 fn default() -> Self {
1009 Self {
1010 uncles: Default::default(),
1011 blocks: Default::default(),
1012 hashes: Default::default(),
1013 transactions: Default::default(),
1014 transaction_receipts: Default::default(),
1015 transaction_traces: Default::default(),
1016 logs: Default::default(),
1017 geth_transaction_traces: Default::default(),
1018 geth_block_traces: Default::default(),
1019 block_traces: Default::default(),
1020 block_receipts: Default::default(),
1021 code_at: Default::default(),
1022 }
1023 }
1024}
1025
1026impl<N: Network> ForkedStorage<N> {
1027 pub fn clear(&mut self) {
1029 *self = Self::default()
1031 }
1032}