use std::{future::IntoFuture, marker::PhantomData};
use crate::{Error, Result};
use alloy_dyn_abi::{DynSolValue, FunctionExt};
use alloy_json_abi::Function;
use alloy_network::Network;
use alloy_primitives::{Address, Bytes};
use alloy_rpc_types_eth::{
state::{AccountOverride, StateOverride},
BlockId, BlockOverrides,
};
use alloy_sol_types::SolCall;
const RAW_CODER: () = ();
#[expect(unnameable_types)]
mod private {
pub trait Sealed {}
impl Sealed for super::Function {}
impl<C: super::SolCall> Sealed for super::PhantomData<C> {}
impl Sealed for () {}
}
#[must_use = "EthCall must be awaited to execute the call"]
#[derive(Clone, Debug)]
pub struct EthCall<'coder, D, N>
where
N: Network,
D: CallDecoder,
{
inner: alloy_provider::EthCall<N, Bytes>,
decoder: &'coder D,
}
impl<'coder, D, N> EthCall<'coder, D, N>
where
N: Network,
D: CallDecoder,
{
pub const fn new(inner: alloy_provider::EthCall<N, Bytes>, decoder: &'coder D) -> Self {
Self { inner, decoder }
}
}
impl<N> EthCall<'static, (), N>
where
N: Network,
{
pub const fn new_raw(inner: alloy_provider::EthCall<N, Bytes>) -> Self {
Self::new(inner, &RAW_CODER)
}
}
impl<D, N> EthCall<'_, D, N>
where
N: Network,
D: CallDecoder,
{
pub fn with_decoder<E>(self, decoder: &E) -> EthCall<'_, E, N>
where
E: CallDecoder,
{
EthCall { inner: self.inner, decoder }
}
pub fn overrides(mut self, overrides: impl Into<StateOverride>) -> Self {
self.inner = self.inner.overrides(overrides);
self
}
pub fn account_override(
mut self,
address: Address,
account_overrides: AccountOverride,
) -> Self {
self.inner = self.inner.account_override(address, account_overrides);
self
}
pub fn account_overrides(
mut self,
overrides: impl IntoIterator<Item = (Address, AccountOverride)>,
) -> Self {
self.inner = self.inner.account_overrides(overrides);
self
}
pub fn block(mut self, block: BlockId) -> Self {
self.inner = self.inner.block(block);
self
}
pub fn with_block_overrides(mut self, overrides: BlockOverrides) -> Self {
self.inner = self.inner.with_block_overrides(overrides);
self
}
}
impl<N> From<alloy_provider::EthCall<N, Bytes>> for EthCall<'static, (), N>
where
N: Network,
{
fn from(inner: alloy_provider::EthCall<N, Bytes>) -> Self {
Self { inner, decoder: &RAW_CODER }
}
}
impl<'coder, D, N> std::future::IntoFuture for EthCall<'coder, D, N>
where
D: CallDecoder,
N: Network,
{
type Output = Result<D::CallOutput>;
type IntoFuture = EthCallFut<'coder, D, N>;
fn into_future(self) -> Self::IntoFuture {
EthCallFut { inner: self.inner.into_future(), decoder: self.decoder }
}
}
#[must_use = "futures do nothing unless you `.await` or poll them"]
#[derive(Debug)]
#[expect(unnameable_types)]
pub struct EthCallFut<'coder, D, N>
where
N: Network,
D: CallDecoder,
{
inner: <alloy_provider::EthCall<N, Bytes> as IntoFuture>::IntoFuture,
decoder: &'coder D,
}
impl<D, N> std::future::Future for EthCallFut<'_, D, N>
where
D: CallDecoder,
N: Network,
{
type Output = Result<D::CallOutput>;
fn poll(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
let this = self.get_mut();
let pin = std::pin::pin!(&mut this.inner);
match pin.poll(cx) {
std::task::Poll::Ready(Ok(data)) => {
std::task::Poll::Ready(this.decoder.abi_decode_output(data))
}
std::task::Poll::Ready(Err(e)) => std::task::Poll::Ready(Err(e.into())),
std::task::Poll::Pending => std::task::Poll::Pending,
}
}
}
pub trait CallDecoder: private::Sealed {
#[doc(hidden)]
type CallOutput;
#[doc(hidden)]
fn abi_decode_output(&self, data: Bytes) -> Result<Self::CallOutput>;
#[doc(hidden)]
fn as_debug_field(&self) -> impl std::fmt::Debug;
}
impl CallDecoder for Function {
type CallOutput = Vec<DynSolValue>;
#[inline]
fn abi_decode_output(&self, data: Bytes) -> Result<Self::CallOutput> {
FunctionExt::abi_decode_output(self, &data).map_err(|e| Error::decode(&self.name, &data, e))
}
#[inline]
fn as_debug_field(&self) -> impl std::fmt::Debug {
self
}
}
impl<C: SolCall> CallDecoder for PhantomData<C> {
type CallOutput = C::Return;
#[inline]
fn abi_decode_output(&self, data: Bytes) -> Result<Self::CallOutput> {
C::abi_decode_returns(&data).map_err(|e| Error::decode(C::SIGNATURE, &data, e.into()))
}
#[inline]
fn as_debug_field(&self) -> impl std::fmt::Debug {
std::any::type_name::<C>()
}
}
impl CallDecoder for () {
type CallOutput = Bytes;
#[inline]
fn abi_decode_output(&self, data: Bytes) -> Result<Self::CallOutput> {
Ok(data)
}
#[inline]
fn as_debug_field(&self) -> impl std::fmt::Debug {
format_args!("()")
}
}