use alloy::{
primitives::{Address, U256},
providers::Provider,
transports::TransportError,
};
use core::future::Future;
use trevm::revm::database_interface::async_db::DatabaseAsyncRef;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AcctInfo {
pub nonce: u64,
pub balance: U256,
pub has_code: bool,
}
pub trait StateSource: Send + Sync {
type Error: core::error::Error + Send + 'static;
fn account_details(
&self,
address: &Address,
) -> impl Future<Output = Result<AcctInfo, Self::Error>> + Send;
fn nonce(&self, address: &Address) -> impl Future<Output = Result<u64, Self::Error>> + Send {
async { self.account_details(address).await.map(|info| info.nonce) }
}
fn balance(&self, address: &Address) -> impl Future<Output = Result<U256, Self::Error>> + Send {
async { self.account_details(address).await.map(|info| info.balance) }
}
fn map<T: Send, F: FnOnce(&AcctInfo) -> T + Send>(
&self,
address: &Address,
f: F,
) -> impl Future<Output = Result<T, Self::Error>> + Send {
async { self.account_details(address).await.map(|info| f(&info)) }
}
}
impl<Db> StateSource for Db
where
Db: DatabaseAsyncRef + Send + Sync,
Db::Error: Send + 'static,
{
type Error = Db::Error;
async fn account_details(&self, address: &Address) -> Result<AcctInfo, Self::Error> {
let info = self.basic_async_ref(*address).await?.unwrap_or_default();
let has_code = info.code_hash() != trevm::revm::primitives::KECCAK_EMPTY;
Ok(AcctInfo { nonce: info.nonce, balance: info.balance, has_code })
}
}
#[derive(Debug, Clone)]
pub struct ProviderStateSource<P>(pub P);
impl<P: Provider> StateSource for ProviderStateSource<P> {
type Error = TransportError;
async fn account_details(&self, address: &Address) -> Result<AcctInfo, Self::Error> {
let (nonce, balance, code) = tokio::join!(
self.0.get_transaction_count(*address),
self.0.get_balance(*address),
self.0.get_code_at(*address),
);
Ok(AcctInfo { nonce: nonce?, balance: balance?, has_code: !code?.is_empty() })
}
}