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
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
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#[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
167pub trait CallDecoder: private::Sealed {
174 #[doc(hidden)]
178 type CallOutput;
179
180 #[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}