alloy_provider/provider/eth_call/
call_many.rs1use std::{marker::PhantomData, sync::Arc, task::Poll};
2
3use alloy_eips::BlockId;
4use alloy_json_rpc::RpcRecv;
5use alloy_network::Network;
6use alloy_rpc_types_eth::{state::StateOverride, Bundle, StateContext, TransactionIndex};
7use alloy_transport::TransportResult;
8use futures::{future, FutureExt};
9
10use crate::ProviderCall;
11
12use super::{Caller, EthCallManyParams};
13
14#[derive(Clone)]
16pub struct EthCallMany<'req, N, Resp: RpcRecv, Output = Resp, Map = fn(Resp) -> Output>
17where
18 N: Network,
19 Resp: RpcRecv,
20 Map: Fn(Resp) -> Output,
21{
22 caller: Arc<dyn Caller<N, Resp>>,
23 params: EthCallManyParams<'req>,
24 map: Map,
25 _pd: PhantomData<fn() -> (Resp, Output)>,
26}
27
28impl<N, Resp, Output, Map> std::fmt::Debug for EthCallMany<'_, N, Resp, Output, Map>
29where
30 N: Network,
31 Resp: RpcRecv,
32 Map: Fn(Resp) -> Output,
33{
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 f.debug_struct("EthCallMany")
36 .field("params", &self.params)
37 .field("method", &"eth_callMany")
38 .finish()
39 }
40}
41
42impl<'req, N, Resp> EthCallMany<'req, N, Resp>
43where
44 N: Network,
45 Resp: RpcRecv,
46{
47 pub fn new(caller: impl Caller<N, Resp> + 'static, bundles: &'req [Bundle]) -> Self {
49 Self {
50 caller: Arc::new(caller),
51 params: EthCallManyParams::new(bundles),
52 map: std::convert::identity,
53 _pd: PhantomData,
54 }
55 }
56}
57
58impl<'req, N, Resp, Output, Map> EthCallMany<'req, N, Resp, Output, Map>
59where
60 N: Network,
61 Resp: RpcRecv,
62 Map: Fn(Resp) -> Output,
63{
64 pub fn map<NewOutput, NewMap>(
66 self,
67 map: NewMap,
68 ) -> EthCallMany<'req, N, Resp, NewOutput, NewMap>
69 where
70 NewMap: Fn(Resp) -> NewOutput,
71 {
72 EthCallMany { caller: self.caller, params: self.params, map, _pd: PhantomData }
73 }
74
75 pub fn block(mut self, block: BlockId) -> Self {
77 self.params = self.params.with_block(block);
78 self
79 }
80
81 pub fn pending(self) -> Self {
83 self.block(BlockId::pending())
84 }
85
86 pub fn latest(self) -> Self {
88 self.block(BlockId::latest())
89 }
90
91 pub fn earliest(self) -> Self {
93 self.block(BlockId::earliest())
94 }
95
96 pub fn finalized(self) -> Self {
98 self.block(BlockId::finalized())
99 }
100
101 pub fn safe(self) -> Self {
103 self.block(BlockId::safe())
104 }
105
106 pub fn number(self, number: u64) -> Self {
108 self.block(BlockId::number(number))
109 }
110
111 pub fn hash(self, hash: alloy_primitives::B256) -> Self {
114 self.block(BlockId::hash(hash))
115 }
116
117 pub fn hash_canonical(self, hash: alloy_primitives::B256) -> Self {
120 self.block(BlockId::hash_canonical(hash))
121 }
122
123 pub fn transaction_index(mut self, tx_index: TransactionIndex) -> Self {
125 self.params = self.params.with_transaction_index(tx_index);
126 self
127 }
128
129 pub fn context(mut self, context: &'req StateContext) -> Self {
131 self.params = self.params.with_context(*context);
132 self
133 }
134
135 pub fn overrides(mut self, overrides: &'req StateOverride) -> Self {
137 self.params = self.params.with_overrides(overrides);
138 self
139 }
140
141 pub fn extend_bundles(mut self, bundles: &'req [Bundle]) -> Self {
143 self.params.bundles_mut().extend_from_slice(bundles);
144 self
145 }
146}
147
148impl<'req, N, Resp, Output, Map> std::future::IntoFuture for EthCallMany<'req, N, Resp, Output, Map>
149where
150 N: Network,
151 Resp: RpcRecv,
152 Map: Fn(Resp) -> Output,
153{
154 type Output = TransportResult<Output>;
155
156 type IntoFuture = CallManyFut<'req, N, Resp, Output, Map>;
157
158 fn into_future(self) -> Self::IntoFuture {
159 CallManyFut {
160 inner: CallManyInnerFut::Preparing {
161 caller: self.caller,
162 params: self.params,
163 map: self.map,
164 },
165 }
166 }
167}
168
169#[derive(Debug)]
171#[doc(hidden)] #[expect(unnameable_types)]
173#[pin_project::pin_project]
174pub struct CallManyFut<'req, N: Network, Resp: RpcRecv, Output, Map: Fn(Resp) -> Output> {
175 inner: CallManyInnerFut<'req, N, Resp, Output, Map>,
176}
177
178impl<N, Resp, Output, Map> CallManyFut<'_, N, Resp, Output, Map>
179where
180 N: Network,
181 Resp: RpcRecv,
182 Map: Fn(Resp) -> Output,
183{
184 const fn is_preparing(&self) -> bool {
185 matches!(self.inner, CallManyInnerFut::Preparing { .. })
186 }
187
188 const fn is_running(&self) -> bool {
189 matches!(self.inner, CallManyInnerFut::Running { .. })
190 }
191
192 fn poll_preparing(&mut self, cx: &mut std::task::Context<'_>) -> Poll<TransportResult<Output>> {
193 let CallManyInnerFut::Preparing { caller, params, map } =
194 std::mem::replace(&mut self.inner, CallManyInnerFut::Polling)
195 else {
196 unreachable!("bad state");
197 };
198
199 let fut = caller.call_many(params)?;
200 self.inner = CallManyInnerFut::Running { fut, map };
201 self.poll_running(cx)
202 }
203
204 fn poll_running(&mut self, cx: &mut std::task::Context<'_>) -> Poll<TransportResult<Output>> {
205 let CallManyInnerFut::Running { ref mut fut, ref map } = self.inner else {
206 unreachable!("bad state");
207 };
208
209 fut.poll_unpin(cx).map(|res| res.map(map))
210 }
211}
212
213impl<N, Resp, Output, Map> future::Future for CallManyFut<'_, N, Resp, Output, Map>
214where
215 N: Network,
216 Resp: RpcRecv,
217 Map: Fn(Resp) -> Output,
218{
219 type Output = TransportResult<Output>;
220
221 fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
222 let this = self.get_mut();
223
224 if this.is_preparing() {
225 this.poll_preparing(cx)
226 } else if this.is_running() {
227 this.poll_running(cx)
228 } else {
229 panic!("bad state");
230 }
231 }
232}
233
234enum CallManyInnerFut<'req, N: Network, Resp: RpcRecv, Output, Map: Fn(Resp) -> Output> {
235 Preparing { caller: Arc<dyn Caller<N, Resp>>, params: EthCallManyParams<'req>, map: Map },
236 Running { fut: ProviderCall<EthCallManyParams<'static>, Resp>, map: Map },
237 Polling,
238}
239
240impl<N, Resp, Output, Map> std::fmt::Debug for CallManyInnerFut<'_, N, Resp, Output, Map>
241where
242 N: Network,
243 Resp: RpcRecv,
244 Map: Fn(Resp) -> Output,
245{
246 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
247 match self {
248 CallManyInnerFut::Preparing { params, .. } => {
249 f.debug_tuple("Preparing").field(¶ms).finish()
250 }
251 CallManyInnerFut::Running { .. } => f.debug_tuple("Running").finish(),
252 CallManyInnerFut::Polling => f.debug_tuple("Polling").finish(),
253 }
254 }
255}