bankai_sdk/fetch/clients/
execution_client.rs

1use crate::errors::{SdkError, SdkResult};
2use alloy_primitives::{Address, FixedBytes, U256};
3use alloy_provider::{Provider, ProviderBuilder};
4use alloy_rpc_types_eth::{EIP1186AccountProofResponse, Header as ExecutionHeader};
5use bankai_types::fetch::evm::execution::TxProof;
6use eth_trie_proofs::tx_trie::TxsMptHandler;
7use url::Url;
8
9pub struct ExecutionFetcher {
10    pub rpc_url: String,
11    pub network_id: u64,
12}
13
14impl ExecutionFetcher {
15    pub fn new(rpc_url: String, network_id: u64) -> Self {
16        Self {
17            rpc_url,
18            network_id,
19        }
20    }
21
22    pub async fn fetch_header(&self, block_number: u64) -> SdkResult<ExecutionHeader> {
23        let rpc_url: Url = self
24            .rpc_url
25            .parse()
26            .map_err(|e| SdkError::Provider(format!("invalid rpc url: {e}")))?;
27        let provider = ProviderBuilder::new().connect_http(rpc_url);
28
29        let block = provider
30            .get_block_by_number(block_number.into())
31            .await
32            .map_err(|e| SdkError::Provider(format!("rpc error: {e}")))?;
33
34        let block =
35            block.ok_or_else(|| SdkError::NotFound(format!("block {block_number} not found")))?;
36
37        Ok(block.header)
38    }
39
40    pub async fn fetch_account_proof(
41        &self,
42        address: Address,
43        block_number: u64,
44    ) -> SdkResult<EIP1186AccountProofResponse> {
45        let rpc_url: Url = self
46            .rpc_url
47            .parse()
48            .map_err(|e| SdkError::Provider(format!("invalid rpc url: {e}")))?;
49        let provider = ProviderBuilder::new().connect_http(rpc_url);
50
51        let proof = provider
52            .get_proof(address, vec![])
53            .block_id(block_number.into())
54            .await
55            .map_err(|e| SdkError::Provider(format!("rpc error: {e}")))?;
56
57        Ok(proof)
58    }
59
60    /// Fetches storage slot proofs for one or more slots from the same contract.
61    pub async fn fetch_storage_slot_proof(
62        &self,
63        address: Address,
64        block_number: u64,
65        slot_keys: &[U256],
66    ) -> SdkResult<EIP1186AccountProofResponse> {
67        let rpc_url: Url = self
68            .rpc_url
69            .parse()
70            .map_err(|e| SdkError::Provider(format!("invalid rpc url: {e}")))?;
71        let provider = ProviderBuilder::new().connect_http(rpc_url);
72
73        let keys: Vec<FixedBytes<32>> = slot_keys
74            .iter()
75            .map(|k| FixedBytes::from(k.to_be_bytes::<32>()))
76            .collect();
77
78        let proof = provider
79            .get_proof(address, keys)
80            .block_id(block_number.into())
81            .await
82            .map_err(|e| SdkError::Provider(format!("rpc error: {e}")))?;
83
84        Ok(proof)
85    }
86
87    pub async fn fetch_tx_proof(&self, tx_hash: FixedBytes<32>) -> SdkResult<TxProof> {
88        let rpc_url: Url = self
89            .rpc_url
90            .parse()
91            .map_err(|e| SdkError::Provider(format!("invalid rpc url: {e}")))?;
92
93        let mut txs_mpt_handler = TxsMptHandler::new(rpc_url).unwrap();
94        txs_mpt_handler
95            .build_tx_tree_from_tx_hash(tx_hash)
96            .await
97            .unwrap();
98
99        let tx_index = txs_mpt_handler.tx_hash_to_tx_index(tx_hash).unwrap();
100        let proof = txs_mpt_handler.get_proof(tx_index).unwrap();
101        let encoded_tx = txs_mpt_handler
102            .verify_proof(tx_index, proof.clone())
103            .unwrap();
104
105        let block_number = self.fetch_tx_block_number(tx_hash).await?;
106
107        Ok(TxProof {
108            network_id: self.network_id,
109            block_number,
110            tx_hash,
111            tx_index,
112            proof: proof.into_iter().map(|p| p.into()).collect(),
113            encoded_tx,
114        })
115    }
116
117    pub async fn fetch_tx_block_number(&self, tx_hash: FixedBytes<32>) -> SdkResult<u64> {
118        let rpc_url: Url = self
119            .rpc_url
120            .parse()
121            .map_err(|e| SdkError::Provider(format!("invalid rpc url: {e}")))?;
122        let provider = ProviderBuilder::new().connect_http(rpc_url);
123
124        let receipt = provider
125            .get_transaction_receipt(tx_hash)
126            .await
127            .map_err(|_| SdkError::NotFound("block not found".to_string()))?
128            .ok_or(SdkError::NotFound("block not found".to_string()))?;
129
130        Ok(receipt.block_number.unwrap())
131    }
132}