alloy_provider/provider/
with_block.rs

1use alloy_eips::BlockId;
2use alloy_json_rpc::{RpcRecv, RpcSend};
3use alloy_primitives::B256;
4use alloy_rpc_client::RpcCall;
5use alloy_transport::TransportResult;
6use std::future::IntoFuture;
7
8use crate::ProviderCall;
9
10/// Helper struct that houses the params along with the BlockId.
11#[derive(Debug, Clone)]
12pub struct ParamsWithBlock<Params: RpcSend> {
13    /// The params to be sent to the RPC call.
14    pub params: Params,
15    /// The block id to be used for the RPC call.
16    pub block_id: BlockId,
17}
18
19impl<Params: RpcSend> ParamsWithBlock<Params> {
20    /// Create a new instance of `ParamsWithBlock`.
21    pub fn new(params: Params, block_id: BlockId) -> Self {
22        Self { params, block_id }
23    }
24}
25
26impl<Params: RpcSend> serde::Serialize for ParamsWithBlock<Params> {
27    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
28    where
29        S: serde::Serializer,
30    {
31        // Serialize params to a Value first
32        let mut ser = serde_json::to_value(&self.params).map_err(serde::ser::Error::custom)?;
33
34        // serialize the block id
35        let block_id = serde_json::to_value(self.block_id).map_err(serde::ser::Error::custom)?;
36
37        if let serde_json::Value::Array(ref mut arr) = ser {
38            arr.push(block_id);
39        } else if ser.is_null() {
40            ser = serde_json::Value::Array(vec![block_id]);
41        } else {
42            ser = serde_json::Value::Array(vec![ser, block_id]);
43        }
44
45        ser.serialize(serializer)
46    }
47}
48
49type ProviderCallProducer<Params, Resp, Output, Map> =
50    Box<dyn Fn(BlockId) -> ProviderCall<ParamsWithBlock<Params>, Resp, Output, Map> + Send>;
51
52/// Container for varous types of calls dependent on a block id.
53enum WithBlockInner<Params, Resp, Output = Resp, Map = fn(Resp) -> Output>
54where
55    Params: RpcSend,
56    Resp: RpcRecv,
57    Map: Fn(Resp) -> Output,
58{
59    /// [RpcCall] which params are getting wrapped into [ParamsWithBlock] once the block id is set.
60    RpcCall(RpcCall<Params, Resp, Output, Map>),
61    /// Closure that produces a [ProviderCall] once the block id is set.
62    ProviderCall(ProviderCallProducer<Params, Resp, Output, Map>),
63}
64
65impl<Params, Resp, Output, Map> core::fmt::Debug for WithBlockInner<Params, Resp, Output, Map>
66where
67    Params: RpcSend,
68    Resp: RpcRecv,
69    Map: Fn(Resp) -> Output,
70{
71    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72        match self {
73            Self::RpcCall(call) => f.debug_tuple("RpcCall").field(call).finish(),
74            Self::ProviderCall(_) => f.debug_struct("ProviderCall").finish(),
75        }
76    }
77}
78
79/// A struct that takes an optional [`BlockId`] parameter.
80///
81/// This resolves to a [`ProviderCall`] that will execute the call on the specified block.
82///
83/// By default this will use "latest".
84#[pin_project::pin_project]
85#[derive(Debug)]
86pub struct RpcWithBlock<Params, Resp, Output = Resp, Map = fn(Resp) -> Output>
87where
88    Params: RpcSend,
89    Resp: RpcRecv,
90    Map: Fn(Resp) -> Output + Clone,
91{
92    inner: WithBlockInner<Params, Resp, Output, Map>,
93    block_id: BlockId,
94}
95
96impl<Params, Resp, Output, Map> RpcWithBlock<Params, Resp, Output, Map>
97where
98    Params: RpcSend,
99    Resp: RpcRecv,
100    Map: Fn(Resp) -> Output + Clone,
101{
102    /// Create a new [`RpcWithBlock`] from a [`RpcCall`].
103    pub fn new_rpc(inner: RpcCall<Params, Resp, Output, Map>) -> Self {
104        Self { inner: WithBlockInner::RpcCall(inner), block_id: Default::default() }
105    }
106
107    /// Create a new [`RpcWithBlock`] from a closure producing a [`ProviderCall`].
108    pub fn new_provider<F>(get_call: F) -> Self
109    where
110        F: Fn(BlockId) -> ProviderCall<ParamsWithBlock<Params>, Resp, Output, Map> + Send + 'static,
111    {
112        let get_call = Box::new(get_call);
113        Self { inner: WithBlockInner::ProviderCall(get_call), block_id: Default::default() }
114    }
115}
116
117impl<Params, Resp, Output, Map> From<RpcCall<Params, Resp, Output, Map>>
118    for RpcWithBlock<Params, Resp, Output, Map>
119where
120    Params: RpcSend,
121    Resp: RpcRecv,
122    Map: Fn(Resp) -> Output + Clone,
123{
124    fn from(inner: RpcCall<Params, Resp, Output, Map>) -> Self {
125        Self::new_rpc(inner)
126    }
127}
128
129impl<F, Params, Resp, Output, Map> From<F> for RpcWithBlock<Params, Resp, Output, Map>
130where
131    Params: RpcSend,
132    Resp: RpcRecv,
133    Map: Fn(Resp) -> Output + Clone,
134    F: Fn(BlockId) -> ProviderCall<ParamsWithBlock<Params>, Resp, Output, Map> + Send + 'static,
135{
136    fn from(inner: F) -> Self {
137        Self::new_provider(inner)
138    }
139}
140
141impl<Params, Resp, Output, Map> RpcWithBlock<Params, Resp, Output, Map>
142where
143    Params: RpcSend,
144    Resp: RpcRecv,
145    Map: Fn(Resp) -> Output + Clone,
146{
147    /// Set the block id.
148    pub const fn block_id(mut self, block_id: BlockId) -> Self {
149        self.block_id = block_id;
150        self
151    }
152
153    /// Set the block id to "pending".
154    pub const fn pending(self) -> Self {
155        self.block_id(BlockId::pending())
156    }
157
158    /// Set the block id to "latest".
159    pub const fn latest(self) -> Self {
160        self.block_id(BlockId::latest())
161    }
162
163    /// Set the block id to "earliest".
164    pub const fn earliest(self) -> Self {
165        self.block_id(BlockId::earliest())
166    }
167
168    /// Set the block id to "finalized".
169    pub const fn finalized(self) -> Self {
170        self.block_id(BlockId::finalized())
171    }
172
173    /// Set the block id to "safe".
174    pub const fn safe(self) -> Self {
175        self.block_id(BlockId::safe())
176    }
177
178    /// Set the block id to a specific height.
179    pub const fn number(self, number: u64) -> Self {
180        self.block_id(BlockId::number(number))
181    }
182
183    /// Set the block id to a specific hash, without requiring the hash be part
184    /// of the canonical chain.
185    pub const fn hash(self, hash: B256) -> Self {
186        self.block_id(BlockId::hash(hash))
187    }
188
189    /// Set the block id to a specific hash and require the hash be part of the
190    /// canonical chain.
191    pub const fn hash_canonical(self, hash: B256) -> Self {
192        self.block_id(BlockId::hash_canonical(hash))
193    }
194}
195
196impl<Params, Resp, Output, Map> IntoFuture for RpcWithBlock<Params, Resp, Output, Map>
197where
198    Params: RpcSend,
199    Resp: RpcRecv,
200    Output: 'static,
201    Map: Fn(Resp) -> Output + Clone,
202{
203    type Output = TransportResult<Output>;
204
205    type IntoFuture = ProviderCall<ParamsWithBlock<Params>, Resp, Output, Map>;
206
207    fn into_future(self) -> Self::IntoFuture {
208        match self.inner {
209            WithBlockInner::RpcCall(rpc_call) => {
210                let block_id = self.block_id;
211                let rpc_call = rpc_call.map_params(|params| ParamsWithBlock::new(params, block_id));
212                ProviderCall::RpcCall(rpc_call)
213            }
214            WithBlockInner::ProviderCall(get_call) => get_call(self.block_id),
215        }
216    }
217}