bankai_sdk/fetch/evm/execution.rs
1use alloy_primitives::{Address, FixedBytes, U256};
2use alloy_rpc_types_eth::EIP1186AccountProofResponse;
3pub use alloy_rpc_types_eth::Header as ExecutionHeader;
4use bankai_types::api::proofs::{HashingFunctionDto, MmrProofRequestDto};
5
6use crate::errors::SdkResult;
7use crate::fetch::{
8 bankai,
9 clients::{bankai_api::ApiClient, execution_client::ExecutionFetcher},
10};
11use alloy_rpc_types_eth::Account as AlloyAccount;
12use bankai_types::fetch::evm::execution::{
13 ExecutionHeaderProof, StorageSlotEntry, StorageSlotProof, TxProof,
14};
15
16/// Fetcher for EVM execution layer data with MMR proofs
17///
18/// This fetcher retrieves execution layer (EL) blockchain data such as headers, accounts,
19/// and transactions, along with the MMR proofs needed to decommit headers from STWO proofs.
20///
21/// The typical flow is:
22/// 1. Fetch a header with its MMR proof
23/// 2. Use the MMR proof to decommit and verify the header from the STWO block proof
24/// 3. Use the verified header to verify accounts/transactions via standard Merkle proofs
25pub struct ExecutionChainFetcher {
26 api_client: ApiClient,
27 rpc_url: String,
28 network_id: u64,
29}
30
31impl ExecutionChainFetcher {
32 /// Creates a new execution chain fetcher
33 ///
34 /// # Arguments
35 ///
36 /// * `api_client` - The Bankai API client for fetching MMR proofs
37 /// * `rpc_url` - The EVM RPC endpoint URL
38 /// * `network_id` - The network ID for this chain
39 pub fn new(api_client: ApiClient, rpc_url: String, network_id: u64) -> Self {
40 Self {
41 api_client,
42 rpc_url,
43 network_id,
44 }
45 }
46
47 /// Fetches an execution header with its MMR proof
48 ///
49 /// This retrieves the execution layer header from the RPC and generates an MMR proof
50 /// that can be used to decommit this header from the STWO block proof's execution MMR.
51 ///
52 /// # Arguments
53 ///
54 /// * `block_number` - The block number to fetch
55 /// * `hashing_function` - The hash function to use for the MMR proof
56 /// * `bankai_block_number` - The Bankai block number containing the MMR
57 ///
58 /// # Returns
59 ///
60 /// An `ExecutionHeaderProof` containing the header and MMR proof for decommitment
61 pub async fn header(
62 &self,
63 block_number: u64,
64 hashing_function: HashingFunctionDto,
65 bankai_block_number: u64,
66 ) -> SdkResult<ExecutionHeaderProof> {
67 let header = ExecutionFetcher::new(self.rpc_url.clone(), self.network_id)
68 .fetch_header(block_number)
69 .await?;
70 let mmr_proof = bankai::mmr::fetch_mmr_proof(
71 &self.api_client,
72 &MmrProofRequestDto {
73 network_id: self.network_id,
74 block_number: bankai_block_number,
75 hashing_function,
76 header_hash: header.hash.to_string(),
77 },
78 )
79 .await?;
80 Ok(ExecutionHeaderProof {
81 header,
82 mmr_proof: mmr_proof.into(),
83 })
84 }
85
86 /// Fetches an execution header without an MMR proof
87 ///
88 /// Used internally by the batch builder. For verification purposes, use `header()` instead
89 /// to get the header with its MMR proof.
90 pub async fn header_only(&self, block_number: u64) -> SdkResult<ExecutionHeader> {
91 let header = ExecutionFetcher::new(self.rpc_url.clone(), self.network_id)
92 .fetch_header(block_number)
93 .await?;
94 Ok(header)
95 }
96
97 /// Returns the network ID for this fetcher
98 pub fn network_id(&self) -> u64 {
99 self.network_id
100 }
101
102 /// Fetches an account proof for a specific address at a given block
103 ///
104 /// Returns a Merkle proof that can verify the account's state (balance, nonce, code hash,
105 /// storage root) against the state root in the block header. The header itself must be
106 /// verified first using an MMR proof.
107 ///
108 /// # Arguments
109 ///
110 /// * `block_number` - The block number to query
111 /// * `address` - The account address
112 /// * `_hashing_function` - Reserved for future use
113 /// * `_bankai_block_number` - Reserved for future use
114 ///
115 /// # Returns
116 ///
117 /// An EIP-1186 account proof that can be verified against the header's state root
118 pub async fn account(
119 &self,
120 block_number: u64,
121 address: Address,
122 _hashing_function: HashingFunctionDto,
123 _bankai_block_number: u64,
124 ) -> SdkResult<EIP1186AccountProofResponse> {
125 let proof = ExecutionFetcher::new(self.rpc_url.clone(), self.network_id)
126 .fetch_account_proof(address, block_number)
127 .await?;
128 Ok(proof)
129 }
130
131 /// Fetches a transaction proof for a specific transaction hash
132 ///
133 /// Returns the transaction data along with a Merkle proof that can verify the transaction
134 /// against the transactions root in the block header. The header itself must be verified
135 /// first using an MMR proof.
136 ///
137 /// # Arguments
138 ///
139 /// * `tx_hash` - The transaction hash
140 ///
141 /// # Returns
142 ///
143 /// A transaction proof containing the transaction and its Merkle proof
144 pub async fn tx_proof(&self, tx_hash: FixedBytes<32>) -> SdkResult<TxProof> {
145 let proof = ExecutionFetcher::new(self.rpc_url.clone(), self.network_id)
146 .fetch_tx_proof(tx_hash)
147 .await?;
148 Ok(proof)
149 }
150
151 /// Fetches storage slot proofs for one or more slots from a contract at a given block.
152 ///
153 /// This calls the EIP-1186 `eth_getProof` RPC with the provided storage keys and returns
154 /// the slot values along with the MPT proofs needed to verify them against the contract's
155 /// storage root (which itself can be verified against the header's state root).
156 ///
157 /// # Arguments
158 ///
159 /// * `block_number` - The block number to query
160 /// * `address` - The contract address
161 /// * `slot_keys` - The storage keys (uint256) to query
162 /// * `_hashing_function` - Reserved for future use
163 /// * `_bankai_block_number` - Reserved for future use
164 pub async fn storage_slot_proof(
165 &self,
166 block_number: u64,
167 address: Address,
168 slot_keys: &[U256],
169 _hashing_function: HashingFunctionDto,
170 _bankai_block_number: u64,
171 ) -> SdkResult<StorageSlotProof> {
172 let proof = ExecutionFetcher::new(self.rpc_url.clone(), self.network_id)
173 .fetch_storage_slot_proof(address, block_number, slot_keys)
174 .await?;
175
176 let header = self.header_only(block_number).await?;
177
178 let slots: Vec<StorageSlotEntry> = proof
179 .storage_proof
180 .into_iter()
181 .map(|s| StorageSlotEntry {
182 slot_key: s.key.as_b256().into(),
183 slot_value: s.value,
184 storage_mpt_proof: s.proof,
185 })
186 .collect();
187
188 let account_state: AlloyAccount = AlloyAccount {
189 balance: proof.balance,
190 nonce: proof.nonce,
191 code_hash: proof.code_hash,
192 storage_root: proof.storage_hash,
193 };
194
195 Ok(StorageSlotProof {
196 account: account_state,
197 address,
198 network_id: self.network_id,
199 block_number,
200 state_root: header.state_root,
201 account_mpt_proof: proof.account_proof,
202 slots,
203 })
204 }
205}