alloy_provider/provider/
get_block.rs1use std::{fmt::Debug, marker::PhantomData};
2
3use crate::{utils, ProviderCall};
4use alloy_eips::{BlockId, BlockNumberOrTag};
5use alloy_json_rpc::RpcRecv;
6use alloy_network::BlockResponse;
7use alloy_network_primitives::BlockTransactionsKind;
8use alloy_primitives::{Address, BlockHash, B256, B64};
9use alloy_rpc_client::{ClientRef, RpcCall};
10use alloy_transport::{TransportError, TransportResult};
11use serde_json::Value;
12
13#[derive(Clone, Debug, Default)]
17pub struct EthGetBlockParams {
18 block: BlockId,
19 kind: BlockTransactionsKind,
20}
21
22impl serde::Serialize for EthGetBlockParams {
23 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
24 where
25 S: serde::Serializer,
26 {
27 use serde::ser::SerializeTuple;
28
29 let mut tuple = serializer.serialize_tuple(2)?;
30 match self.block {
31 BlockId::Hash(hash) => tuple.serialize_element(&hash.block_hash)?,
32 BlockId::Number(number) => tuple.serialize_element(&number)?,
33 }
34 if self.kind.is_hashes() {
35 tuple.serialize_element(&false)?;
36 } else {
37 tuple.serialize_element(&true)?
38 };
39 tuple.end()
40 }
41}
42
43impl EthGetBlockParams {
44 pub fn new(block: BlockId, kind: BlockTransactionsKind) -> Self {
46 Self { block, kind }
47 }
48}
49
50#[must_use = "EthGetBlockBy must be awaited to execute the request"]
55pub struct EthGetBlock<BlockResp>
57where
58 BlockResp: alloy_network::BlockResponse + RpcRecv,
59{
60 inner: GetBlockInner<BlockResp>,
61 block: BlockId,
62 kind: BlockTransactionsKind,
63 _pd: std::marker::PhantomData<BlockResp>,
64}
65
66impl<BlockResp> EthGetBlock<BlockResp>
67where
68 BlockResp: alloy_network::BlockResponse + RpcRecv,
69{
70 pub fn by_hash(hash: BlockHash, client: ClientRef<'_>) -> Self {
73 let params = EthGetBlockParams::default();
74 let call = client.request("eth_getBlockByHash", params);
75 Self::new_rpc(hash.into(), call)
76 }
77
78 pub fn by_number(number: BlockNumberOrTag, client: ClientRef<'_>) -> Self {
81 let params = EthGetBlockParams::default();
82
83 if number.is_pending() {
84 return Self::new_pending_rpc(client.request("eth_getBlockByNumber", params));
85 }
86
87 Self::new_rpc(number.into(), client.request("eth_getBlockByNumber", params))
88 }
89}
90
91impl<BlockResp> EthGetBlock<BlockResp>
92where
93 BlockResp: alloy_network::BlockResponse + RpcRecv,
94{
95 pub fn new_rpc(block: BlockId, inner: RpcCall<EthGetBlockParams, Option<BlockResp>>) -> Self {
97 Self {
98 block,
99 inner: GetBlockInner::RpcCall(inner),
100 kind: BlockTransactionsKind::Hashes,
101 _pd: PhantomData,
102 }
103 }
104
105 pub fn new_pending_rpc(inner: RpcCall<EthGetBlockParams, Value>) -> Self {
107 Self {
108 block: BlockId::pending(),
109 inner: GetBlockInner::PendingBlock(inner),
110 kind: BlockTransactionsKind::Hashes,
111 _pd: PhantomData,
112 }
113 }
114
115 pub fn new_provider(block: BlockId, producer: ProviderCallProducer<BlockResp>) -> Self {
117 Self {
118 block,
119 inner: GetBlockInner::ProviderCall(producer),
120 kind: BlockTransactionsKind::Hashes,
121 _pd: PhantomData,
122 }
123 }
124
125 pub fn kind(mut self, kind: BlockTransactionsKind) -> Self {
127 self.kind = kind;
128 self
129 }
130
131 pub fn full(mut self) -> Self {
133 self.kind = BlockTransactionsKind::Full;
134 self
135 }
136
137 pub fn hashes(mut self) -> Self {
139 self.kind = BlockTransactionsKind::Hashes;
140 self
141 }
142}
143
144impl<BlockResp> std::future::IntoFuture for EthGetBlock<BlockResp>
145where
146 BlockResp: alloy_network::BlockResponse + RpcRecv,
147{
148 type Output = TransportResult<Option<BlockResp>>;
149
150 type IntoFuture = ProviderCall<EthGetBlockParams, Option<BlockResp>>;
151
152 fn into_future(self) -> Self::IntoFuture {
153 match self.inner {
154 GetBlockInner::RpcCall(call) => {
155 let rpc_call =
156 call.map_params(|_params| EthGetBlockParams::new(self.block, self.kind));
157
158 let fut = async move {
159 let resp = rpc_call.await?;
160 let result =
161 if self.kind.is_hashes() { utils::convert_to_hashes(resp) } else { resp };
162 Ok(result)
163 };
164
165 ProviderCall::BoxedFuture(Box::pin(fut))
166 }
167 GetBlockInner::PendingBlock(call) => {
168 let rpc_call =
169 call.map_params(|_params| EthGetBlockParams::new(self.block, self.kind));
170
171 let map_fut = async move {
172 let mut block = rpc_call.await?;
173
174 if block.is_null() {
175 return Ok(None);
176 }
177
178 tracing::trace!(pending_block = ?block.to_string());
181 if block.get("hash").map_or(true, |v| v.is_null()) {
182 block["hash"] = Value::String(format!("{}", B256::ZERO));
183 }
184
185 if block.get("nonce").map_or(true, |v| v.is_null()) {
186 block["nonce"] = Value::String(format!("{}", B64::ZERO));
187 }
188
189 if block.get("miner").map_or(true, |v| v.is_null())
190 || block.get("beneficiary").map_or(true, |v| v.is_null())
191 {
192 block["miner"] = Value::String(format!("{}", Address::ZERO));
193 }
194
195 let block = serde_json::from_value(block.clone())
196 .map_err(|e| TransportError::deser_err(e, block.to_string()))?;
197
198 let block = if self.kind.is_hashes() {
199 utils::convert_to_hashes(Some(block))
200 } else {
201 Some(block)
202 };
203
204 Ok(block)
205 };
206
207 ProviderCall::BoxedFuture(Box::pin(map_fut))
208 }
209 GetBlockInner::ProviderCall(producer) => producer(self.kind),
210 }
211 }
212}
213
214impl<BlockResp> core::fmt::Debug for EthGetBlock<BlockResp>
215where
216 BlockResp: BlockResponse + RpcRecv,
217{
218 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
219 f.debug_struct("EthGetBlock").field("block", &self.block).field("kind", &self.kind).finish()
220 }
221}
222
223type ProviderCallProducer<BlockResp> =
224 Box<dyn Fn(BlockTransactionsKind) -> ProviderCall<EthGetBlockParams, Option<BlockResp>> + Send>;
225
226enum GetBlockInner<BlockResp>
227where
228 BlockResp: BlockResponse + RpcRecv,
229{
230 RpcCall(RpcCall<EthGetBlockParams, Option<BlockResp>>),
232 PendingBlock(RpcCall<EthGetBlockParams, Value>),
242 ProviderCall(ProviderCallProducer<BlockResp>),
244}
245
246impl<BlockResp> core::fmt::Debug for GetBlockInner<BlockResp>
247where
248 BlockResp: BlockResponse + RpcRecv,
249{
250 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251 match self {
252 Self::RpcCall(call) => f.debug_tuple("RpcCall").field(call).finish(),
253 Self::PendingBlock(call) => f.debug_tuple("PendingBlockCall").field(call).finish(),
254 Self::ProviderCall(_) => f.debug_struct("ProviderCall").finish(),
255 }
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262 use crate::{Provider, ProviderBuilder};
263
264 #[tokio::test]
266 async fn test_pending_block_deser() {
267 let provider =
268 ProviderBuilder::new().on_http("https://binance.llamarpc.com".parse().unwrap());
269
270 let _block = provider.get_block_by_number(BlockNumberOrTag::Pending).await.unwrap();
271 }
272}