use alloy::primitives::BlockNumber;
use itertools::Itertools;
#[cfg(feature = "test_utils")]
use reqwest::Url;
#[cfg(feature = "test_utils")]
use std::str::FromStr;
use std::{collections::HashMap, time::Instant};
use starknet_types_core::felt::Felt;
use tracing::info;
use crate::provider::{
config::ProviderConfig,
error::ProviderError,
indexer::Indexer,
traits::{AsyncResult, FetchProofsFromKeysResult, FetchProofsResult, ProofProvider},
};
use super::{rpc::RpcProvider, types::GetProofOutput};
type StorageProofsResult = Result<HashMap<BlockNumber, GetProofOutput>, ProviderError>;
pub struct StarknetProvider {
pub(crate) rpc_provider: RpcProvider,
pub(crate) header_provider: Indexer,
}
#[cfg(feature = "test_utils")]
impl Default for StarknetProvider {
fn default() -> Self {
Self::new(&ProviderConfig {
provider_url: Url::from_str("https://pathfinder.sepolia.iosis.tech/").unwrap(),
chain_id: crate::primitives::ChainId::StarknetSepolia,
deployed_on_chain_id: crate::primitives::ChainId::EthereumSepolia,
max_requests: 100,
})
}
}
impl StarknetProvider {
pub fn new(config: &ProviderConfig) -> Self {
let rpc_provider = RpcProvider::new(config.provider_url.to_owned(), config.max_requests);
let indexer = Indexer::new(config.chain_id, config.deployed_on_chain_id).staging();
Self {
rpc_provider,
header_provider: indexer,
}
}
pub async fn get_range_of_storage_proofs(
&self,
from_block: BlockNumber,
to_block: BlockNumber,
increment: u64,
address: Felt,
storage_slot: Felt,
) -> StorageProofsResult {
let start_fetch = Instant::now();
let target_blocks_batch: Vec<Vec<BlockNumber>> =
self._chunk_block_range(from_block, to_block, increment);
let mut processed_accounts = HashMap::new();
for target_blocks in target_blocks_batch {
processed_accounts.extend(
self.rpc_provider
.get_storage_proofs(target_blocks, address, vec![storage_slot])
.await?,
);
}
let duration = start_fetch.elapsed();
info!("time taken (Storage Proofs Fetch): {:?}", duration);
Ok(processed_accounts)
}
pub(crate) fn _chunk_block_range(
&self,
from_block: BlockNumber,
to_block: BlockNumber,
increment: u64,
) -> Vec<Vec<BlockNumber>> {
(from_block..=to_block)
.step_by(increment as usize)
.chunks(800)
.into_iter()
.map(|chunk| chunk.collect())
.collect()
}
pub(crate) fn _chunk_vec_blocks_keys(
&self,
blocks: Vec<(BlockNumber, Vec<Felt>)>,
) -> Vec<Vec<(BlockNumber, Vec<Felt>)>> {
blocks.chunks(800).map(|chunk| chunk.to_vec()).collect()
}
pub(crate) fn _chunk_vec_blocks_for_indexer(
&self,
blocks: Vec<BlockNumber>,
) -> Vec<Vec<BlockNumber>> {
let mut sorted_blocks = blocks.clone();
sorted_blocks.sort();
let mut result: Vec<Vec<BlockNumber>> = Vec::new();
let mut current_chunk: Vec<BlockNumber> = Vec::new();
for &block in sorted_blocks.iter() {
if current_chunk.is_empty() || block - current_chunk[0] <= 800 {
current_chunk.push(block);
} else {
result.push(current_chunk);
current_chunk = vec![block];
}
}
if !current_chunk.is_empty() {
result.push(current_chunk);
}
result
}
}
impl ProofProvider for StarknetProvider {
fn fetch_proofs<'a>(
&'a self,
_datalake: &'a crate::primitives::task::datalake::DatalakeCompute,
) -> AsyncResult<FetchProofsResult> {
unimplemented!("fetch_proofs is not implemented for StarknetProvider");
}
fn fetch_proofs_from_keys(
&self,
keys: crate::provider::key::CategorizedFetchKeys,
) -> AsyncResult<FetchProofsFromKeysResult> {
Box::pin(async move { self.fetch_proofs_from_keys(keys).await })
}
}