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}