reth_consensus_debug_client/providers/
etherscan.rs1use crate::BlockProvider;
2use alloy_eips::BlockNumberOrTag;
3use alloy_rpc_types_eth::Block;
4use reqwest::Client;
5use reth_tracing::tracing::warn;
6use serde::Deserialize;
7use std::time::Duration;
8use tokio::{sync::mpsc, time::interval};
9
10#[derive(Debug, Clone)]
12pub struct EtherscanBlockProvider {
13 http_client: Client,
14 base_url: String,
15 api_key: String,
16 interval: Duration,
17}
18
19impl EtherscanBlockProvider {
20 pub fn new(base_url: String, api_key: String) -> Self {
22 Self { http_client: Client::new(), base_url, api_key, interval: Duration::from_secs(3) }
23 }
24
25 pub const fn with_interval(mut self, interval: Duration) -> Self {
27 self.interval = interval;
28 self
29 }
30
31 pub async fn load_block(&self, block_number_or_tag: BlockNumberOrTag) -> eyre::Result<Block> {
35 let block: EtherscanBlockResponse = self
36 .http_client
37 .get(&self.base_url)
38 .query(&[
39 ("module", "proxy"),
40 ("action", "eth_getBlockByNumber"),
41 ("tag", &block_number_or_tag.to_string()),
42 ("boolean", "true"),
43 ("apikey", &self.api_key),
44 ])
45 .send()
46 .await?
47 .json()
48 .await?;
49 Ok(block.result)
50 }
51}
52
53impl BlockProvider for EtherscanBlockProvider {
54 async fn subscribe_blocks(&self, tx: mpsc::Sender<Block>) {
55 let mut last_block_number: Option<u64> = None;
56 let mut interval = interval(self.interval);
57 loop {
58 interval.tick().await;
59 let block = match self.load_block(BlockNumberOrTag::Latest).await {
60 Ok(block) => block,
61 Err(err) => {
62 warn!(target: "consensus::debug-client", %err, "failed to fetch a block from Etherscan");
63 continue
64 }
65 };
66 let block_number = block.header.number;
67 if Some(block_number) == last_block_number {
68 continue;
69 }
70
71 if tx.send(block).await.is_err() {
72 break;
74 }
75
76 last_block_number = Some(block_number);
77 }
78 }
79
80 async fn get_block(&self, block_number: u64) -> eyre::Result<Block> {
81 self.load_block(BlockNumberOrTag::Number(block_number)).await
82 }
83}
84
85#[derive(Deserialize, Debug)]
86struct EtherscanBlockResponse {
87 result: Block,
88}