alloy_provider/provider/eth_call/
call_many.rs

1use 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/// A builder for an `"eth_callMany"` RPC request.
15#[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    /// Instantiates a new `EthCallMany` with the given parameters.
48    pub fn new(caller: impl Caller<N, Resp> + 'static, bundles: &'req Vec<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    /// Set a mapping function to transform the response.
65    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    /// Set the [`BlockId`] in the [`StateContext`].
76    pub fn block(mut self, block: BlockId) -> Self {
77        self.params = self.params.with_block(block);
78        self
79    }
80
81    /// Set the [`TransactionIndex`] in the [`StateContext`].
82    pub fn transaction_index(mut self, tx_index: TransactionIndex) -> Self {
83        self.params = self.params.with_transaction_index(tx_index);
84        self
85    }
86
87    /// Set the [`StateContext`] for the call.
88    pub fn context(mut self, context: &'req StateContext) -> Self {
89        self.params = self.params.with_context(*context);
90        self
91    }
92
93    /// Set the [`StateOverride`] for the call.
94    pub fn overrides(mut self, overrides: &'req StateOverride) -> Self {
95        self.params = self.params.with_overrides(overrides);
96        self
97    }
98
99    /// Extend the bundles for the call.
100    pub fn extend_bundles(mut self, bundles: &'req [Bundle]) -> Self {
101        self.params.bundles_mut().extend_from_slice(bundles);
102        self
103    }
104}
105
106impl<'req, N, Resp, Output, Map> std::future::IntoFuture for EthCallMany<'req, N, Resp, Output, Map>
107where
108    N: Network,
109    Resp: RpcRecv,
110    Map: Fn(Resp) -> Output,
111{
112    type Output = TransportResult<Output>;
113
114    type IntoFuture = CallManyFut<'req, N, Resp, Output, Map>;
115
116    fn into_future(self) -> Self::IntoFuture {
117        CallManyFut {
118            inner: CallManyInnerFut::Preparing {
119                caller: self.caller,
120                params: self.params,
121                map: self.map,
122            },
123        }
124    }
125}
126
127/// Intermediate future for `"eth_callMany"` requests.
128#[derive(Debug)]
129#[doc(hidden)] // Not public API.
130#[allow(unnameable_types)]
131#[pin_project::pin_project]
132pub struct CallManyFut<'req, N: Network, Resp: RpcRecv, Output, Map: Fn(Resp) -> Output> {
133    inner: CallManyInnerFut<'req, N, Resp, Output, Map>,
134}
135
136impl<N, Resp, Output, Map> CallManyFut<'_, N, Resp, Output, Map>
137where
138    N: Network,
139    Resp: RpcRecv,
140    Map: Fn(Resp) -> Output,
141{
142    const fn is_preparing(&self) -> bool {
143        matches!(self.inner, CallManyInnerFut::Preparing { .. })
144    }
145
146    const fn is_running(&self) -> bool {
147        matches!(self.inner, CallManyInnerFut::Running { .. })
148    }
149
150    fn poll_preparing(&mut self, cx: &mut std::task::Context<'_>) -> Poll<TransportResult<Output>> {
151        let CallManyInnerFut::Preparing { caller, params, map } =
152            std::mem::replace(&mut self.inner, CallManyInnerFut::Polling)
153        else {
154            unreachable!("bad state");
155        };
156
157        let fut = caller.call_many(params)?;
158        self.inner = CallManyInnerFut::Running { fut, map };
159        self.poll_running(cx)
160    }
161
162    fn poll_running(&mut self, cx: &mut std::task::Context<'_>) -> Poll<TransportResult<Output>> {
163        let CallManyInnerFut::Running { ref mut fut, ref map } = self.inner else {
164            unreachable!("bad state");
165        };
166
167        fut.poll_unpin(cx).map(|res| res.map(map))
168    }
169}
170
171impl<N, Resp, Output, Map> future::Future for CallManyFut<'_, N, Resp, Output, Map>
172where
173    N: Network,
174    Resp: RpcRecv,
175    Map: Fn(Resp) -> Output,
176{
177    type Output = TransportResult<Output>;
178
179    fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
180        let this = self.get_mut();
181
182        if this.is_preparing() {
183            this.poll_preparing(cx)
184        } else if this.is_running() {
185            this.poll_running(cx)
186        } else {
187            panic!("bad state");
188        }
189    }
190}
191
192enum CallManyInnerFut<'req, N: Network, Resp: RpcRecv, Output, Map: Fn(Resp) -> Output> {
193    Preparing { caller: Arc<dyn Caller<N, Resp>>, params: EthCallManyParams<'req>, map: Map },
194    Running { fut: ProviderCall<EthCallManyParams<'static>, Resp>, map: Map },
195    Polling,
196}
197
198impl<N, Resp, Output, Map> std::fmt::Debug for CallManyInnerFut<'_, N, Resp, Output, Map>
199where
200    N: Network,
201    Resp: RpcRecv,
202    Map: Fn(Resp) -> Output,
203{
204    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205        match self {
206            CallManyInnerFut::Preparing { params, .. } => {
207                f.debug_tuple("Preparing").field(&params).finish()
208            }
209            CallManyInnerFut::Running { .. } => f.debug_tuple("Running").finish(),
210            CallManyInnerFut::Polling => f.debug_tuple("Polling").finish(),
211        }
212    }
213}