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,
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
107impl<N> From<alloy_provider::EthCall<N, Bytes>> for EthCall<'static, (), N>
108where
109    N: Network,
110{
111    fn from(inner: alloy_provider::EthCall<N, Bytes>) -> Self {
112        Self { inner, decoder: &RAW_CODER }
113    }
114}
115
116impl<'coder, D, N> std::future::IntoFuture for EthCall<'coder, D, N>
117where
118    D: CallDecoder + Unpin,
119    N: Network,
120{
121    type Output = Result<D::CallOutput>;
122
123    type IntoFuture = EthCallFut<'coder, D, N>;
124
125    fn into_future(self) -> Self::IntoFuture {
126        EthCallFut { inner: self.inner.into_future(), decoder: self.decoder }
127    }
128}
129
130/// Future for the [`EthCall`] type. This future wraps an RPC call with an abi
131/// decoder.
132#[must_use = "futures do nothing unless you `.await` or poll them"]
133#[derive(Debug)]
134#[expect(unnameable_types)]
135pub struct EthCallFut<'coder, D, N>
136where
137    N: Network,
138    D: CallDecoder,
139{
140    inner: <alloy_provider::EthCall<N, Bytes> as IntoFuture>::IntoFuture,
141    decoder: &'coder D,
142}
143
144impl<D, N> std::future::Future for EthCallFut<'_, D, N>
145where
146    D: CallDecoder + Unpin,
147    N: Network,
148{
149    type Output = Result<D::CallOutput>;
150
151    fn poll(
152        self: std::pin::Pin<&mut Self>,
153        cx: &mut std::task::Context<'_>,
154    ) -> std::task::Poll<Self::Output> {
155        let this = self.get_mut();
156        let pin = std::pin::pin!(&mut this.inner);
157        match pin.poll(cx) {
158            std::task::Poll::Ready(Ok(data)) => {
159                std::task::Poll::Ready(this.decoder.abi_decode_output(data))
160            }
161            std::task::Poll::Ready(Err(e)) => std::task::Poll::Ready(Err(e.into())),
162            std::task::Poll::Pending => std::task::Poll::Pending,
163        }
164    }
165}
166
167/// A trait for decoding the output of a contract function.
168///
169/// This trait is sealed and cannot be implemented manually.
170/// It is an implementation detail of [`CallBuilder`].
171///
172/// [`CallBuilder`]: crate::CallBuilder
173pub trait CallDecoder: private::Sealed {
174    // Not public API.
175
176    /// The output type of the contract function.
177    #[doc(hidden)]
178    type CallOutput;
179
180    /// Decodes the output of a contract function.
181    #[doc(hidden)]
182    fn abi_decode_output(&self, data: Bytes) -> Result<Self::CallOutput>;
183
184    #[doc(hidden)]
185    fn as_debug_field(&self) -> impl std::fmt::Debug;
186}
187
188impl CallDecoder for Function {
189    type CallOutput = Vec<DynSolValue>;
190
191    #[inline]
192    fn abi_decode_output(&self, data: Bytes) -> Result<Self::CallOutput> {
193        FunctionExt::abi_decode_output(self, &data).map_err(|e| Error::decode(&self.name, &data, e))
194    }
195
196    #[inline]
197    fn as_debug_field(&self) -> impl std::fmt::Debug {
198        self
199    }
200}
201
202impl<C: SolCall> CallDecoder for PhantomData<C> {
203    type CallOutput = C::Return;
204
205    #[inline]
206    fn abi_decode_output(&self, data: Bytes) -> Result<Self::CallOutput> {
207        C::abi_decode_returns(&data).map_err(|e| Error::decode(C::SIGNATURE, &data, e.into()))
208    }
209
210    #[inline]
211    fn as_debug_field(&self) -> impl std::fmt::Debug {
212        std::any::type_name::<C>()
213    }
214}
215
216impl CallDecoder for () {
217    type CallOutput = Bytes;
218
219    #[inline]
220    fn abi_decode_output(&self, data: Bytes) -> Result<Self::CallOutput> {
221        Ok(data)
222    }
223
224    #[inline]
225    fn as_debug_field(&self) -> impl std::fmt::Debug {
226        format_args!("()")
227    }
228}