kona_derive_alloy/
beacon_client.rsuse alloy_eips::eip4844::IndexedBlobHash;
use alloy_rpc_types_beacon::sidecar::{BeaconBlobBundle, BlobData};
use async_trait::async_trait;
use reqwest::Client;
pub(crate) const SPEC_METHOD: &str = "eth/v1/config/spec";
pub(crate) const GENESIS_METHOD: &str = "eth/v1/beacon/genesis";
pub(crate) const SIDECARS_METHOD_PREFIX: &str = "eth/v1/beacon/blob_sidecars";
#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ReducedGenesisData {
#[serde(rename = "genesis_time")]
#[serde(with = "alloy_serde::quantity")]
pub genesis_time: u64,
}
#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct APIGenesisResponse {
pub data: ReducedGenesisData,
}
#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ReducedConfigData {
#[serde(rename = "SECONDS_PER_SLOT")]
#[serde(with = "alloy_serde::quantity")]
pub seconds_per_slot: u64,
}
#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct APIConfigResponse {
pub data: ReducedConfigData,
}
impl APIConfigResponse {
pub const fn new(seconds_per_slot: u64) -> Self {
Self { data: ReducedConfigData { seconds_per_slot } }
}
}
impl APIGenesisResponse {
pub const fn new(genesis_time: u64) -> Self {
Self { data: ReducedGenesisData { genesis_time } }
}
}
#[async_trait]
pub trait BeaconClient {
type Error: std::fmt::Display + ToString;
async fn config_spec(&self) -> Result<APIConfigResponse, Self::Error>;
async fn beacon_genesis(&self) -> Result<APIGenesisResponse, Self::Error>;
async fn beacon_blob_side_cars(
&self,
slot: u64,
hashes: &[IndexedBlobHash],
) -> Result<Vec<BlobData>, Self::Error>;
}
#[derive(Debug, Clone)]
pub struct OnlineBeaconClient {
base: String,
inner: Client,
}
impl OnlineBeaconClient {
pub fn new_http(base: String) -> Self {
Self { base, inner: Client::new() }
}
}
#[async_trait]
impl BeaconClient for OnlineBeaconClient {
type Error = reqwest::Error;
async fn config_spec(&self) -> Result<APIConfigResponse, Self::Error> {
let first = self.inner.get(format!("{}/{}", self.base, SPEC_METHOD)).send().await?;
first.json::<APIConfigResponse>().await
}
async fn beacon_genesis(&self) -> Result<APIGenesisResponse, Self::Error> {
let first = self.inner.get(format!("{}/{}", self.base, GENESIS_METHOD)).send().await?;
first.json::<APIGenesisResponse>().await
}
async fn beacon_blob_side_cars(
&self,
slot: u64,
hashes: &[IndexedBlobHash],
) -> Result<Vec<BlobData>, Self::Error> {
let raw_response = self
.inner
.get(format!("{}/{}/{}", self.base, SIDECARS_METHOD_PREFIX, slot))
.send()
.await?;
let raw_response = raw_response.json::<BeaconBlobBundle>().await?;
let mut sidecars = Vec::with_capacity(hashes.len());
hashes.iter().for_each(|hash| {
if let Some(sidecar) =
raw_response.data.iter().find(|sidecar| sidecar.index == hash.index)
{
sidecars.push(sidecar.clone());
}
});
Ok(sidecars)
}
}