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
14const 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#[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 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 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 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 pub fn overrides(mut self, overrides: impl Into<StateOverride>) -> Self {
74 self.inner = self.inner.overrides(overrides);
75 self
76 }
77
78 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 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 pub fn block(mut self, block: BlockId) -> Self {
102 self.inner = self.inner.block(block);
103 self
104 }
105
106 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#[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
173pub trait CallDecoder: private::Sealed {
180 #[doc(hidden)]
184 type CallOutput;
185
186 #[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}