alloy_contract/
eth_call.rs

1use std::{future::IntoFuture, marker::PhantomData};
2
3use crate::{Error, Result};
4use alloy_dyn_abi::{DynSolValue, FunctionExt};
5use alloy_json_abi::Function;
6use alloy_network::Network;
7use alloy_primitives::{Address, Bytes};
8use alloy_rpc_types_eth::{
9    state::{AccountOverride, StateOverride},
10    BlockId, BlockOverrides,
11};
12use alloy_sol_types::SolCall;
13
14/// Raw coder.
15const RAW_CODER: () = ();
16
17#[expect(unnameable_types)]
18mod private {
19    pub trait Sealed {}
20    impl Sealed for super::Function {}
21    impl<C: super::SolCall> Sealed for super::PhantomData<C> {}
22    impl Sealed for () {}
23}
24
25/// An [`alloy_provider::EthCall`] with an abi decoder.
26#[must_use = "EthCall must be awaited to execute the call"]
27#[derive(Clone, Debug)]
28pub struct EthCall<'coder, D, N>
29where
30    N: Network,
31    D: CallDecoder,
32{
33    inner: alloy_provider::EthCall<N, Bytes>,
34
35    decoder: &'coder D,
36}
37
38impl<'coder, D, N> EthCall<'coder, D, N>
39where
40    N: Network,
41    D: CallDecoder,
42{
43    /// Create a new [`EthCall`].
44    pub const fn new(inner: alloy_provider::EthCall<N, Bytes>, decoder: &'coder D) -> Self {
45        Self { inner, decoder }
46    }
47}
48
49impl<N> EthCall<'static, (), N>
50where
51    N: Network,
52{
53    /// Create a new [`EthCall`].
54    pub const fn new_raw(inner: alloy_provider::EthCall<N, Bytes>) -> Self {
55        Self::new(inner, &RAW_CODER)
56    }
57}
58
59impl<D, N> EthCall<'_, D, N>
60where
61    N: Network,
62    D: CallDecoder,
63{
64    /// Swap the decoder for this call.
65    pub fn with_decoder<E>(self, decoder: &E) -> EthCall<'_, E, N>
66    where
67        E: CallDecoder,
68    {
69        EthCall { inner: self.inner, decoder }
70    }
71
72    /// Set the state overrides for this call.
73    pub fn overrides(mut self, overrides: impl Into<StateOverride>) -> Self {
74        self.inner = self.inner.overrides(overrides);
75        self
76    }
77
78    /// Appends a single [AccountOverride] to the state override.
79    ///
80    /// Creates a new [`StateOverride`] if none has been set yet.
81    pub fn account_override(
82        mut self,
83        address: Address,
84        account_overrides: AccountOverride,
85    ) -> Self {
86        self.inner = self.inner.account_override(address, account_overrides);
87        self
88    }
89    /// Extends the given [AccountOverride] to the state override.
90    ///
91    /// Creates a new [`StateOverride`] if none has been set yet.
92    pub fn account_overrides(
93        mut self,
94        overrides: impl IntoIterator<Item = (Address, AccountOverride)>,
95    ) -> Self {
96        self.inner = self.inner.account_overrides(overrides);
97        self
98    }
99
100    /// Set the block to use for this call.
101    pub fn block(mut self, block: BlockId) -> Self {
102        self.inner = self.inner.block(block);
103        self
104    }
105
106    /// Sets the block overrides for this call.
107    pub fn with_block_overrides(mut self, overrides: BlockOverrides) -> Self {
108        self.inner = self.inner.with_block_overrides(overrides);
109        self
110    }
111}
112
113impl<N> From<alloy_provider::EthCall<N, Bytes>> for EthCall<'static, (), N>
114where
115    N: Network,
116{
117    fn from(inner: alloy_provider::EthCall<N, Bytes>) -> Self {
118        Self { inner, decoder: &RAW_CODER }
119    }
120}
121
122impl<'coder, D, N> std::future::IntoFuture for EthCall<'coder, D, N>
123where
124    D: CallDecoder,
125    N: Network,
126{
127    type Output = Result<D::CallOutput>;
128
129    type IntoFuture = EthCallFut<'coder, D, N>;
130
131    fn into_future(self) -> Self::IntoFuture {
132        EthCallFut { inner: self.inner.into_future(), decoder: self.decoder }
133    }
134}
135
136/// Future for the [`EthCall`] type. This future wraps an RPC call with an abi
137/// decoder.
138#[must_use = "futures do nothing unless you `.await` or poll them"]
139#[derive(Debug)]
140#[expect(unnameable_types)]
141pub struct EthCallFut<'coder, D, N>
142where
143    N: Network,
144    D: CallDecoder,
145{
146    inner: <alloy_provider::EthCall<N, Bytes> as IntoFuture>::IntoFuture,
147    decoder: &'coder D,
148}
149
150impl<D, N> std::future::Future for EthCallFut<'_, D, N>
151where
152    D: CallDecoder,
153    N: Network,
154{
155    type Output = Result<D::CallOutput>;
156
157    fn poll(
158        self: std::pin::Pin<&mut Self>,
159        cx: &mut std::task::Context<'_>,
160    ) -> std::task::Poll<Self::Output> {
161        let this = self.get_mut();
162        let pin = std::pin::pin!(&mut this.inner);
163        match pin.poll(cx) {
164            std::task::Poll::Ready(Ok(data)) => {
165                std::task::Poll::Ready(this.decoder.abi_decode_output(data))
166            }
167            std::task::Poll::Ready(Err(e)) => std::task::Poll::Ready(Err(e.into())),
168            std::task::Poll::Pending => std::task::Poll::Pending,
169        }
170    }
171}
172
173/// A trait for decoding the output of a contract function.
174///
175/// This trait is sealed and cannot be implemented manually.
176/// It is an implementation detail of [`CallBuilder`].
177///
178/// [`CallBuilder`]: crate::CallBuilder
179pub trait CallDecoder: private::Sealed {
180    // Not public API.
181
182    /// The output type of the contract function.
183    #[doc(hidden)]
184    type CallOutput;
185
186    /// Decodes the output of a contract function.
187    #[doc(hidden)]
188    fn abi_decode_output(&self, data: Bytes) -> Result<Self::CallOutput>;
189
190    #[doc(hidden)]
191    fn as_debug_field(&self) -> impl std::fmt::Debug;
192}
193
194impl CallDecoder for Function {
195    type CallOutput = Vec<DynSolValue>;
196
197    #[inline]
198    fn abi_decode_output(&self, data: Bytes) -> Result<Self::CallOutput> {
199        FunctionExt::abi_decode_output(self, &data).map_err(|e| Error::decode(&self.name, &data, e))
200    }
201
202    #[inline]
203    fn as_debug_field(&self) -> impl std::fmt::Debug {
204        self
205    }
206}
207
208impl<C: SolCall> CallDecoder for PhantomData<C> {
209    type CallOutput = C::Return;
210
211    #[inline]
212    fn abi_decode_output(&self, data: Bytes) -> Result<Self::CallOutput> {
213        C::abi_decode_returns(&data).map_err(|e| Error::decode(C::SIGNATURE, &data, e.into()))
214    }
215
216    #[inline]
217    fn as_debug_field(&self) -> impl std::fmt::Debug {
218        std::any::type_name::<C>()
219    }
220}
221
222impl CallDecoder for () {
223    type CallOutput = Bytes;
224
225    #[inline]
226    fn abi_decode_output(&self, data: Bytes) -> Result<Self::CallOutput> {
227        Ok(data)
228    }
229
230    #[inline]
231    fn as_debug_field(&self) -> impl std::fmt::Debug {
232        format_args!("()")
233    }
234}