ethers_core/types/
block.rs

1// Modified from <https://github.com/tomusdrw/rust-web3/blob/master/src/types/block.rs>
2
3#[cfg(not(feature = "celo"))]
4use crate::types::Withdrawal;
5use crate::types::{Address, Bloom, Bytes, Transaction, TxHash, H256, U256, U64};
6use chrono::{DateTime, TimeZone, Utc};
7use serde::{
8    de::{MapAccess, Visitor},
9    ser::SerializeStruct,
10    Deserialize, Deserializer, Serialize, Serializer,
11};
12use std::{fmt, fmt::Formatter, str::FromStr};
13use thiserror::Error;
14
15/// The block type returned from RPC calls.
16///
17/// This is generic over a `TX` type which will be either the hash or the full transaction,
18/// i.e. `Block<TxHash>` or `Block<Transaction>`.
19#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
20pub struct Block<TX> {
21    /// Hash of the block
22    pub hash: Option<H256>,
23    /// Hash of the parent
24    #[serde(default, rename = "parentHash")]
25    pub parent_hash: H256,
26    /// Hash of the uncles
27    #[cfg(not(feature = "celo"))]
28    #[serde(default, rename = "sha3Uncles")]
29    pub uncles_hash: H256,
30    /// Miner/author's address. None if pending.
31    #[serde(default, rename = "miner")]
32    pub author: Option<Address>,
33    /// State root hash
34    #[serde(default, rename = "stateRoot")]
35    pub state_root: H256,
36    /// Transactions root hash
37    #[serde(default, rename = "transactionsRoot")]
38    pub transactions_root: H256,
39    /// Transactions receipts root hash
40    #[serde(default, rename = "receiptsRoot")]
41    pub receipts_root: H256,
42    /// Block number. None if pending.
43    pub number: Option<U64>,
44    /// Gas Used
45    #[serde(default, rename = "gasUsed")]
46    pub gas_used: U256,
47    /// Gas Limit
48    #[cfg(not(feature = "celo"))]
49    #[serde(default, rename = "gasLimit")]
50    pub gas_limit: U256,
51    /// Extra data
52    #[serde(default, rename = "extraData")]
53    pub extra_data: Bytes,
54    /// Logs bloom
55    #[serde(rename = "logsBloom")]
56    pub logs_bloom: Option<Bloom>,
57    /// Timestamp
58    #[serde(default)]
59    pub timestamp: U256,
60    /// Difficulty
61    #[cfg(not(feature = "celo"))]
62    #[serde(default)]
63    pub difficulty: U256,
64    /// Total difficulty
65    #[serde(rename = "totalDifficulty")]
66    pub total_difficulty: Option<U256>,
67    /// Seal fields
68    #[serde(default, rename = "sealFields", deserialize_with = "deserialize_null_default")]
69    pub seal_fields: Vec<Bytes>,
70    /// Uncles' hashes
71    #[cfg(not(feature = "celo"))]
72    #[serde(default)]
73    pub uncles: Vec<H256>,
74    /// Transactions
75    #[serde(bound = "TX: Serialize + serde::de::DeserializeOwned", default)]
76    pub transactions: Vec<TX>,
77    /// Size in bytes
78    pub size: Option<U256>,
79    /// Mix Hash
80    #[serde(rename = "mixHash")]
81    #[cfg(not(feature = "celo"))]
82    pub mix_hash: Option<H256>,
83    /// Nonce
84    #[cfg(not(feature = "celo"))]
85    pub nonce: Option<crate::types::H64>,
86    /// Base fee per unit of gas (if past London)
87    #[serde(rename = "baseFeePerGas")]
88    pub base_fee_per_gas: Option<U256>,
89    /// Blob gas used (if past Cancun)
90    #[serde(default, skip_serializing_if = "Option::is_none", rename = "blobGasUsed")]
91    #[cfg(not(feature = "celo"))]
92    pub blob_gas_used: Option<U256>,
93    /// Excess blob gas (if past Cancun)
94    #[serde(default, skip_serializing_if = "Option::is_none", rename = "excessBlobGas")]
95    #[cfg(not(feature = "celo"))]
96    pub excess_blob_gas: Option<U256>,
97    /// Withdrawals root hash (if past Shanghai)
98    #[serde(default, skip_serializing_if = "Option::is_none", rename = "withdrawalsRoot")]
99    #[cfg(not(feature = "celo"))]
100    pub withdrawals_root: Option<H256>,
101    /// Withdrawals (if past Shanghai)
102    #[serde(default, skip_serializing_if = "Option::is_none")]
103    #[cfg(not(feature = "celo"))]
104    pub withdrawals: Option<Vec<Withdrawal>>,
105    /// Parent beacon block root (if past Cancun)
106    #[serde(default, skip_serializing_if = "Option::is_none", rename = "parentBeaconBlockRoot")]
107    #[cfg(not(feature = "celo"))]
108    pub parent_beacon_block_root: Option<H256>,
109
110    #[cfg(feature = "celo")]
111    #[cfg_attr(docsrs, doc(cfg(feature = "celo")))]
112    /// The block's randomness
113    pub randomness: Randomness,
114
115    /// BLS signatures with a SNARK-friendly hash function
116    #[cfg(feature = "celo")]
117    #[cfg_attr(docsrs, doc(cfg(feature = "celo")))]
118    #[serde(rename = "epochSnarkData", default)]
119    pub epoch_snark_data: Option<EpochSnarkData>,
120
121    /// Captures unknown fields such as additional fields used by L2s
122    #[cfg(not(feature = "celo"))]
123    #[serde(flatten)]
124    pub other: crate::types::OtherFields,
125}
126
127fn deserialize_null_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
128where
129    T: Default + Deserialize<'de>,
130    D: Deserializer<'de>,
131{
132    let opt = Option::deserialize(deserializer)?;
133    Ok(opt.unwrap_or_default())
134}
135
136/// Error returned by [`Block::time`].
137#[derive(Clone, Copy, Debug, Error)]
138pub enum TimeError {
139    /// Timestamp is zero.
140    #[error("timestamp is zero")]
141    TimestampZero,
142
143    /// Timestamp is too large for [`DateTime<Utc>`].
144    #[error("timestamp is too large")]
145    TimestampOverflow,
146}
147
148// ref <https://eips.ethereum.org/EIPS/eip-1559>
149#[cfg(not(feature = "celo"))]
150pub const ELASTICITY_MULTIPLIER: U256 = U256([2u64, 0, 0, 0]);
151// max base fee delta is 12.5%
152#[cfg(not(feature = "celo"))]
153pub const BASE_FEE_MAX_CHANGE_DENOMINATOR: U256 = U256([8u64, 0, 0, 0]);
154
155impl<TX> Block<TX> {
156    /// The target gas usage as per EIP-1559
157    #[cfg(not(feature = "celo"))]
158    pub fn gas_target(&self) -> U256 {
159        self.gas_limit / ELASTICITY_MULTIPLIER
160    }
161
162    /// The next block's base fee, it is a function of parent block's base fee and gas usage.
163    /// Reference: <https://eips.ethereum.org/EIPS/eip-1559>
164    #[cfg(not(feature = "celo"))]
165    pub fn next_block_base_fee(&self) -> Option<U256> {
166        use core::cmp::Ordering;
167
168        let target_usage = self.gas_target();
169        let base_fee_per_gas = self.base_fee_per_gas?;
170
171        match self.gas_used.cmp(&target_usage) {
172            Ordering::Greater => {
173                let gas_used_delta = self.gas_used - self.gas_target();
174                let base_fee_per_gas_delta = U256::max(
175                    base_fee_per_gas * gas_used_delta /
176                        target_usage /
177                        BASE_FEE_MAX_CHANGE_DENOMINATOR,
178                    U256::from(1u32),
179                );
180                let expected_base_fee_per_gas = base_fee_per_gas + base_fee_per_gas_delta;
181                Some(expected_base_fee_per_gas)
182            }
183            Ordering::Less => {
184                let gas_used_delta = self.gas_target() - self.gas_used;
185                let base_fee_per_gas_delta = base_fee_per_gas * gas_used_delta /
186                    target_usage /
187                    BASE_FEE_MAX_CHANGE_DENOMINATOR;
188                let expected_base_fee_per_gas = base_fee_per_gas - base_fee_per_gas_delta;
189                Some(expected_base_fee_per_gas)
190            }
191            Ordering::Equal => self.base_fee_per_gas,
192        }
193    }
194
195    /// Parse [`Self::timestamp`] into a [`DateTime<Utc>`].
196    ///
197    /// # Errors
198    ///
199    /// * [`TimeError::TimestampZero`] if the timestamp is zero, or
200    /// * [`TimeError::TimestampOverflow`] if the timestamp is too large to be represented as a
201    ///   [`DateTime<Utc>`].
202    pub fn time(&self) -> Result<DateTime<Utc>, TimeError> {
203        if self.timestamp.is_zero() {
204            return Err(TimeError::TimestampZero)
205        }
206        if self.timestamp.bits() > 63 {
207            return Err(TimeError::TimestampOverflow)
208        }
209        // Casting to i64 is safe because the timestamp is guaranteed to be less than 2^63.
210        // TODO: It would be nice if there was `TryInto<i64> for U256`.
211        let secs = self.timestamp.as_u64() as i64;
212        Ok(Utc.timestamp_opt(secs, 0).unwrap())
213    }
214}
215
216impl Block<TxHash> {
217    /// Converts this block that only holds transaction hashes into a full block with `Transaction`
218    pub fn into_full_block(self, transactions: Vec<Transaction>) -> Block<Transaction> {
219        #[cfg(not(feature = "celo"))]
220        {
221            let Block {
222                hash,
223                parent_hash,
224                uncles_hash,
225                author,
226                state_root,
227                transactions_root,
228                receipts_root,
229                number,
230                gas_used,
231                gas_limit,
232                extra_data,
233                logs_bloom,
234                timestamp,
235                difficulty,
236                total_difficulty,
237                seal_fields,
238                uncles,
239                size,
240                mix_hash,
241                nonce,
242                base_fee_per_gas,
243                withdrawals_root,
244                withdrawals,
245                blob_gas_used,
246                excess_blob_gas,
247                parent_beacon_block_root,
248                other,
249                ..
250            } = self;
251            Block {
252                hash,
253                parent_hash,
254                uncles_hash,
255                author,
256                state_root,
257                transactions_root,
258                receipts_root,
259                number,
260                gas_used,
261                gas_limit,
262                extra_data,
263                logs_bloom,
264                timestamp,
265                difficulty,
266                total_difficulty,
267                seal_fields,
268                uncles,
269                size,
270                mix_hash,
271                nonce,
272                base_fee_per_gas,
273                withdrawals_root,
274                withdrawals,
275                transactions,
276                blob_gas_used,
277                excess_blob_gas,
278                parent_beacon_block_root,
279                other,
280            }
281        }
282
283        #[cfg(feature = "celo")]
284        {
285            let Block {
286                hash,
287                parent_hash,
288                author,
289                state_root,
290                transactions_root,
291                receipts_root,
292                number,
293                gas_used,
294                extra_data,
295                logs_bloom,
296                timestamp,
297                total_difficulty,
298                seal_fields,
299                size,
300                base_fee_per_gas,
301                randomness,
302                epoch_snark_data,
303                ..
304            } = self;
305
306            Block {
307                hash,
308                parent_hash,
309                author,
310                state_root,
311                transactions_root,
312                receipts_root,
313                number,
314                gas_used,
315                extra_data,
316                logs_bloom,
317                timestamp,
318                total_difficulty,
319                seal_fields,
320                size,
321                base_fee_per_gas,
322                randomness,
323                epoch_snark_data,
324                transactions,
325            }
326        }
327    }
328}
329
330impl From<Block<Transaction>> for Block<TxHash> {
331    fn from(full: Block<Transaction>) -> Self {
332        #[cfg(not(feature = "celo"))]
333        {
334            let Block {
335                hash,
336                parent_hash,
337                uncles_hash,
338                author,
339                state_root,
340                transactions_root,
341                receipts_root,
342                number,
343                gas_used,
344                gas_limit,
345                extra_data,
346                logs_bloom,
347                timestamp,
348                difficulty,
349                total_difficulty,
350                seal_fields,
351                uncles,
352                transactions,
353                size,
354                mix_hash,
355                nonce,
356                base_fee_per_gas,
357                withdrawals_root,
358                withdrawals,
359                excess_blob_gas,
360                blob_gas_used,
361                parent_beacon_block_root,
362                other,
363            } = full;
364            Block {
365                hash,
366                parent_hash,
367                uncles_hash,
368                author,
369                state_root,
370                transactions_root,
371                receipts_root,
372                number,
373                gas_used,
374                gas_limit,
375                extra_data,
376                logs_bloom,
377                timestamp,
378                difficulty,
379                total_difficulty,
380                seal_fields,
381                uncles,
382                size,
383                mix_hash,
384                nonce,
385                base_fee_per_gas,
386                withdrawals_root,
387                withdrawals,
388                excess_blob_gas,
389                blob_gas_used,
390                parent_beacon_block_root,
391                transactions: transactions.iter().map(|tx| tx.hash).collect(),
392                other,
393            }
394        }
395
396        #[cfg(feature = "celo")]
397        {
398            let Block {
399                hash,
400                parent_hash,
401                author,
402                state_root,
403                transactions_root,
404                receipts_root,
405                number,
406                gas_used,
407                extra_data,
408                logs_bloom,
409                timestamp,
410                total_difficulty,
411                seal_fields,
412                transactions,
413                size,
414                base_fee_per_gas,
415                randomness,
416                epoch_snark_data,
417            } = full;
418
419            Block {
420                hash,
421                parent_hash,
422                author,
423                state_root,
424                transactions_root,
425                receipts_root,
426                number,
427                gas_used,
428                extra_data,
429                logs_bloom,
430                timestamp,
431                total_difficulty,
432                seal_fields,
433                size,
434                base_fee_per_gas,
435                randomness,
436                epoch_snark_data,
437                transactions: transactions.iter().map(|tx| tx.hash).collect(),
438            }
439        }
440    }
441}
442
443/// Commit-reveal data for generating randomness in the
444/// [Celo protocol](https://docs.celo.org/celo-codebase/protocol/identity/randomness)
445#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
446#[cfg(feature = "celo")]
447pub struct Randomness {
448    /// The committed randomness for that block
449    pub committed: Bytes,
450    /// The revealed randomness for that block
451    pub revealed: Bytes,
452}
453
454/// SNARK-friendly epoch block signature and bitmap
455#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
456#[cfg(feature = "celo")]
457pub struct EpochSnarkData {
458    /// The bitmap showing which validators signed on the epoch block
459    pub bitmap: Bytes,
460    /// Signature using a SNARK-friendly hash
461    pub signature: Bytes,
462}
463
464/// A [block hash](H256) or [block number](BlockNumber).
465#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
466pub enum BlockId {
467    // TODO: May want to expand this to include the requireCanonical field
468    // <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1898.md>
469    /// A block hash
470    Hash(H256),
471    /// A block number
472    Number(BlockNumber),
473}
474
475impl From<u64> for BlockId {
476    fn from(num: u64) -> Self {
477        BlockNumber::Number(num.into()).into()
478    }
479}
480
481impl From<U64> for BlockId {
482    fn from(num: U64) -> Self {
483        BlockNumber::Number(num).into()
484    }
485}
486
487impl From<BlockNumber> for BlockId {
488    fn from(num: BlockNumber) -> Self {
489        BlockId::Number(num)
490    }
491}
492
493impl From<H256> for BlockId {
494    fn from(hash: H256) -> Self {
495        BlockId::Hash(hash)
496    }
497}
498
499impl Serialize for BlockId {
500    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
501    where
502        S: Serializer,
503    {
504        match *self {
505            BlockId::Hash(ref x) => {
506                let mut s = serializer.serialize_struct("BlockIdEip1898", 1)?;
507                s.serialize_field("blockHash", &format!("{x:?}"))?;
508                s.end()
509            }
510            BlockId::Number(ref num) => num.serialize(serializer),
511        }
512    }
513}
514
515impl<'de> Deserialize<'de> for BlockId {
516    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
517    where
518        D: Deserializer<'de>,
519    {
520        struct BlockIdVisitor;
521
522        impl<'de> Visitor<'de> for BlockIdVisitor {
523            type Value = BlockId;
524
525            fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
526                formatter.write_str("Block identifier following EIP-1898")
527            }
528
529            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
530            where
531                E: serde::de::Error,
532            {
533                Ok(BlockId::Number(v.parse().map_err(serde::de::Error::custom)?))
534            }
535
536            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
537            where
538                A: MapAccess<'de>,
539            {
540                let mut number = None;
541                let mut hash = None;
542
543                while let Some(key) = map.next_key::<String>()? {
544                    match key.as_str() {
545                        "blockNumber" => {
546                            if number.is_some() || hash.is_some() {
547                                return Err(serde::de::Error::duplicate_field("blockNumber"))
548                            }
549                            number = Some(BlockId::Number(map.next_value::<BlockNumber>()?))
550                        }
551                        "blockHash" => {
552                            if number.is_some() || hash.is_some() {
553                                return Err(serde::de::Error::duplicate_field("blockHash"))
554                            }
555                            hash = Some(BlockId::Hash(map.next_value::<H256>()?))
556                        }
557                        key => {
558                            return Err(serde::de::Error::unknown_field(
559                                key,
560                                &["blockNumber", "blockHash"],
561                            ))
562                        }
563                    }
564                }
565
566                number.or(hash).ok_or_else(|| {
567                    serde::de::Error::custom("Expected `blockNumber` or `blockHash`")
568                })
569            }
570        }
571
572        deserializer.deserialize_any(BlockIdVisitor)
573    }
574}
575
576impl FromStr for BlockId {
577    type Err = String;
578
579    fn from_str(s: &str) -> Result<Self, Self::Err> {
580        if s.starts_with("0x") && s.len() == 66 {
581            let hash = s.parse::<H256>().map_err(|e| e.to_string());
582            hash.map(Self::Hash)
583        } else {
584            s.parse().map(Self::Number)
585        }
586    }
587}
588
589/// A block number or tag.
590#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
591pub enum BlockNumber {
592    /// Latest block
593    #[default]
594    Latest,
595    /// Finalized block accepted as canonical
596    Finalized,
597    /// Safe head block
598    Safe,
599    /// Earliest block (genesis)
600    Earliest,
601    /// Pending block (not yet part of the blockchain)
602    Pending,
603    /// Block by number from canon chain
604    Number(U64),
605}
606
607impl BlockNumber {
608    /// Returns the numeric block number if explicitly set
609    pub fn as_number(&self) -> Option<U64> {
610        match *self {
611            BlockNumber::Number(num) => Some(num),
612            _ => None,
613        }
614    }
615
616    /// Returns `true` if a numeric block number is set
617    pub fn is_number(&self) -> bool {
618        matches!(self, BlockNumber::Number(_))
619    }
620
621    /// Returns `true` if it's "latest"
622    pub fn is_latest(&self) -> bool {
623        matches!(self, BlockNumber::Latest)
624    }
625
626    /// Returns `true` if it's "finalized"
627    pub fn is_finalized(&self) -> bool {
628        matches!(self, BlockNumber::Finalized)
629    }
630
631    /// Returns `true` if it's "safe"
632    pub fn is_safe(&self) -> bool {
633        matches!(self, BlockNumber::Safe)
634    }
635
636    /// Returns `true` if it's "pending"
637    pub fn is_pending(&self) -> bool {
638        matches!(self, BlockNumber::Pending)
639    }
640
641    /// Returns `true` if it's "earliest"
642    pub fn is_earliest(&self) -> bool {
643        matches!(self, BlockNumber::Earliest)
644    }
645}
646
647impl<T: Into<U64>> From<T> for BlockNumber {
648    fn from(num: T) -> Self {
649        BlockNumber::Number(num.into())
650    }
651}
652
653impl Serialize for BlockNumber {
654    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
655    where
656        S: Serializer,
657    {
658        match *self {
659            BlockNumber::Number(ref x) => serializer.serialize_str(&format!("0x{x:x}")),
660            BlockNumber::Latest => serializer.serialize_str("latest"),
661            BlockNumber::Finalized => serializer.serialize_str("finalized"),
662            BlockNumber::Safe => serializer.serialize_str("safe"),
663            BlockNumber::Earliest => serializer.serialize_str("earliest"),
664            BlockNumber::Pending => serializer.serialize_str("pending"),
665        }
666    }
667}
668
669impl<'de> Deserialize<'de> for BlockNumber {
670    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
671    where
672        D: Deserializer<'de>,
673    {
674        let s = String::deserialize(deserializer)?.to_lowercase();
675        s.parse().map_err(serde::de::Error::custom)
676    }
677}
678
679impl FromStr for BlockNumber {
680    type Err = String;
681
682    fn from_str(s: &str) -> Result<Self, Self::Err> {
683        match s {
684            "latest" => Ok(Self::Latest),
685            "finalized" => Ok(Self::Finalized),
686            "safe" => Ok(Self::Safe),
687            "earliest" => Ok(Self::Earliest),
688            "pending" => Ok(Self::Pending),
689            // hex
690            n if n.starts_with("0x") => n.parse().map(Self::Number).map_err(|e| e.to_string()),
691            // decimal
692            n => n.parse::<u64>().map(|n| Self::Number(n.into())).map_err(|e| e.to_string()),
693        }
694    }
695}
696
697impl fmt::Display for BlockNumber {
698    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
699        match self {
700            BlockNumber::Number(ref x) => format!("0x{x:x}").fmt(f),
701            BlockNumber::Latest => f.write_str("latest"),
702            BlockNumber::Finalized => f.write_str("finalized"),
703            BlockNumber::Safe => f.write_str("safe"),
704            BlockNumber::Earliest => f.write_str("earliest"),
705            BlockNumber::Pending => f.write_str("pending"),
706        }
707    }
708}
709
710#[cfg(test)]
711#[cfg(not(feature = "celo"))]
712mod tests {
713    use super::*;
714    use crate::types::{Transaction, TxHash};
715
716    #[test]
717    fn can_parse_eip1898_block_ids() {
718        let num = serde_json::json!(
719            { "blockNumber": "0x0" }
720        );
721        let id = serde_json::from_value::<BlockId>(num).unwrap();
722        assert_eq!(id, BlockId::Number(BlockNumber::Number(0u64.into())));
723
724        let num = serde_json::json!(
725            { "blockNumber": "pending" }
726        );
727        let id = serde_json::from_value::<BlockId>(num).unwrap();
728        assert_eq!(id, BlockId::Number(BlockNumber::Pending));
729
730        let num = serde_json::json!(
731            { "blockNumber": "latest" }
732        );
733        let id = serde_json::from_value::<BlockId>(num).unwrap();
734        assert_eq!(id, BlockId::Number(BlockNumber::Latest));
735
736        let num = serde_json::json!(
737            { "blockNumber": "finalized" }
738        );
739        let id = serde_json::from_value::<BlockId>(num).unwrap();
740        assert_eq!(id, BlockId::Number(BlockNumber::Finalized));
741
742        let num = serde_json::json!(
743            { "blockNumber": "safe" }
744        );
745        let id = serde_json::from_value::<BlockId>(num).unwrap();
746        assert_eq!(id, BlockId::Number(BlockNumber::Safe));
747
748        let num = serde_json::json!(
749            { "blockNumber": "earliest" }
750        );
751        let id = serde_json::from_value::<BlockId>(num).unwrap();
752        assert_eq!(id, BlockId::Number(BlockNumber::Earliest));
753
754        let num = serde_json::json!("0x0");
755        let id = serde_json::from_value::<BlockId>(num).unwrap();
756        assert_eq!(id, BlockId::Number(BlockNumber::Number(0u64.into())));
757
758        let num = serde_json::json!("pending");
759        let id = serde_json::from_value::<BlockId>(num).unwrap();
760        assert_eq!(id, BlockId::Number(BlockNumber::Pending));
761
762        let num = serde_json::json!("latest");
763        let id = serde_json::from_value::<BlockId>(num).unwrap();
764        assert_eq!(id, BlockId::Number(BlockNumber::Latest));
765
766        let num = serde_json::json!("finalized");
767        let id = serde_json::from_value::<BlockId>(num).unwrap();
768        assert_eq!(id, BlockId::Number(BlockNumber::Finalized));
769
770        let num = serde_json::json!("safe");
771        let id = serde_json::from_value::<BlockId>(num).unwrap();
772        assert_eq!(id, BlockId::Number(BlockNumber::Safe));
773
774        let num = serde_json::json!("earliest");
775        let id = serde_json::from_value::<BlockId>(num).unwrap();
776        assert_eq!(id, BlockId::Number(BlockNumber::Earliest));
777
778        let num = serde_json::json!(
779            { "blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" }
780        );
781        let id = serde_json::from_value::<BlockId>(num).unwrap();
782        assert_eq!(
783            id,
784            BlockId::Hash(
785                "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
786                    .parse()
787                    .unwrap()
788            )
789        );
790    }
791
792    #[test]
793    fn serde_block_number() {
794        for b in &[
795            BlockNumber::Latest,
796            BlockNumber::Finalized,
797            BlockNumber::Safe,
798            BlockNumber::Earliest,
799            BlockNumber::Pending,
800        ] {
801            let b_ser = serde_json::to_string(&b).unwrap();
802            let b_de: BlockNumber = serde_json::from_str(&b_ser).unwrap();
803            assert_eq!(b_de, *b);
804        }
805
806        let b = BlockNumber::Number(1042u64.into());
807        let b_ser = serde_json::to_string(&b).unwrap();
808        let b_de: BlockNumber = serde_json::from_str(&b_ser).unwrap();
809        assert_eq!(b_ser, "\"0x412\"");
810        assert_eq!(b_de, b);
811    }
812
813    #[test]
814    fn deserialize_blk_no_txs() {
815        let block = r#"{"number":"0x3","hash":"0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972","parentHash":"0x689c70c080ca22bc0e681694fa803c1aba16a69c8b6368fed5311d279eb9de90","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x7270c1c4440180f2bd5215809ee3d545df042b67329499e1ab97eb759d31610d","stateRoot":"0x29f32984517a7d25607da485b23cefabfd443751422ca7e603395e1de9bc8a4b","receiptsRoot":"0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2","miner":"0x0000000000000000000000000000000000000000","difficulty":"0x0","totalDifficulty":"0x0","extraData":"0x","size":"0x3e8","gasLimit":"0x6691b7","gasUsed":"0x5208","timestamp":"0x5ecedbb9","transactions":["0xc3c5f700243de37ae986082fd2af88d2a7c2752a0c0f7b9d6ac47c729d45e067"],"uncles":[]}"#;
816        let _block: Block<TxHash> = serde_json::from_str(block).unwrap();
817    }
818
819    #[test]
820    fn deserialize_blk_with_txs() {
821        let block = r#"{"number":"0x3","hash":"0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972","parentHash":"0x689c70c080ca22bc0e681694fa803c1aba16a69c8b6368fed5311d279eb9de90","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x7270c1c4440180f2bd5215809ee3d545df042b67329499e1ab97eb759d31610d","stateRoot":"0x29f32984517a7d25607da485b23cefabfd443751422ca7e603395e1de9bc8a4b","receiptsRoot":"0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2","miner":"0x0000000000000000000000000000000000000000","difficulty":"0x0","totalDifficulty":"0x0","extraData":"0x","size":"0x3e8","gasLimit":"0x6691b7","gasUsed":"0x5208","timestamp":"0x5ecedbb9","transactions":[{"hash":"0xc3c5f700243de37ae986082fd2af88d2a7c2752a0c0f7b9d6ac47c729d45e067","nonce":"0x2","blockHash":"0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972","blockNumber":"0x3","transactionIndex":"0x0","from":"0xfdcedc3bfca10ecb0890337fbdd1977aba84807a","to":"0xdca8ce283150ab773bcbeb8d38289bdb5661de1e","value":"0x0","gas":"0x15f90","gasPrice":"0x4a817c800","input":"0x","v":"0x25","r":"0x19f2694eb9113656dbea0b925e2e7ceb43df83e601c4116aee9c0dd99130be88","s":"0x73e5764b324a4f7679d890a198ba658ba1c8cd36983ff9797e10b1b89dbb448e"}],"uncles":[]}"#;
822        let _block: Block<Transaction> = serde_json::from_str(block).unwrap();
823    }
824
825    #[test]
826    // <https://github.com/tomusdrw/rust-web3/commit/3a32ee962c0f2f8d50a5e25be9f2dfec7ae0750d>
827    fn post_london_block() {
828        let json = serde_json::json!(
829        {
830            "baseFeePerGas": "0x7",
831            "miner": "0x0000000000000000000000000000000000000001",
832            "number": "0x1b4",
833            "hash": "0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331",
834            "parentHash": "0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5",
835            "mixHash": "0x1010101010101010101010101010101010101010101010101010101010101010",
836            "nonce": "0x0000000000000000",
837            "sealFields": [
838              "0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2",
839              "0x0000000000000042"
840            ],
841            "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
842            "logsBloom":  "0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331",
843            "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
844            "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
845            "stateRoot": "0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff",
846            "difficulty": "0x27f07",
847            "totalDifficulty": "0x27f07",
848            "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000",
849            "size": "0x27f07",
850            "gasLimit": "0x9f759",
851            "minGasPrice": "0x9f759",
852            "gasUsed": "0x9f759",
853            "timestamp": "0x54e34e8e",
854            "transactions": [],
855            "uncles": []
856          }
857        );
858
859        let block: Block<()> = serde_json::from_value(json).unwrap();
860        assert_eq!(block.base_fee_per_gas, Some(U256::from(7)));
861    }
862
863    #[test]
864    fn test_next_block_base_fee() {
865        // <https://etherscan.io/block/14402566>
866        let block_14402566: Block<TxHash> = Block {
867            number: Some(U64::from(14402566u64)),
868            base_fee_per_gas: Some(U256::from(36_803_013_756u128)),
869            gas_limit: U256::from(30_087_887u128),
870            gas_used: U256::from(2_023_848u128),
871            ..Default::default()
872        };
873
874        assert_eq!(block_14402566.base_fee_per_gas, Some(U256::from(36_803_013_756u128)));
875        assert_eq!(block_14402566.gas_target(), U256::from(15_043_943u128));
876        // next block decreasing base fee https://etherscan.io/block/14402567
877        assert_eq!(block_14402566.next_block_base_fee(), Some(U256::from(32_821_521_542u128)));
878
879        // https://etherscan.io/block/14402712
880        let block_14402712: Block<TxHash> = Block {
881            number: Some(U64::from(14402712u64)),
882            base_fee_per_gas: Some(U256::from(24_870_031_149u128)),
883            gas_limit: U256::from(30_000_000u128),
884            gas_used: U256::from(29_999_374u128),
885            ..Default::default()
886        };
887
888        assert_eq!(block_14402712.base_fee_per_gas, Some(U256::from(24_870_031_149u128)));
889        assert_eq!(block_14402712.gas_target(), U256::from(15_000_000u128));
890        // next block increasing base fee https://etherscan.io/block/14402713
891        assert_eq!(block_14402712.next_block_base_fee(), Some(U256::from(27_978_655_303u128)));
892    }
893
894    #[test]
895    fn pending_block() {
896        let json = serde_json::json!(
897        {
898          "baseFeePerGas": "0x3a460775a",
899          "difficulty": "0x329b1f81605a4a",
900          "extraData": "0xd983010a0d846765746889676f312e31362e3130856c696e7578",
901          "gasLimit": "0x1c950d9",
902          "gasUsed": "0x1386f81",
903          "hash": null,
904          "logsBloom": "0x5a3fc3425505bf83b1ebe6ffead1bbfdfcd6f9cfbd1e5fb7fbc9c96b1bbc2f2f6bfef959b511e4f0c7d3fbc60194fbff8bcff8e7b8f6ba9a9a956fe36473ed4deec3f1bc67f7dabe48f71afb377bdaa47f8f9bb1cd56930c7dfcbfddf283f9697fb1db7f3bedfa3e4dfd9fae4fb59df8ac5d9c369bff14efcee59997df8bb16d47d22f0bfbafb29fbfff6e1e41bca61e37e7bdfde1fe27b9fd3a7adfcb74fe98e6dbcc5f5bb3bd4d4bb6ccd29fd3bd446c7f38dcaf7ff78fb3f3aa668cbffe56291d7fbbebd2549fdfd9f223b3ba61dee9e92ebeb5dc967f711d039ff1cb3c3a8fb3b7cbdb29e6d1e79e6b95c596dfe2be36fd65a4f6fdeebe7efbe6e38037d7",
905          "miner": null,
906          "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
907          "nonce": null,
908          "number": "0xe1a6ee",
909          "parentHash": "0xff1a940068dfe1f9e3f5514e6b7ff5092098d21d706396a9b19602f0f2b11d44",
910          "receiptsRoot": "0xe7df36675953a2d2dd22ec0e44ff59818891170875ebfba5a39f6c85084a6f10",
911          "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
912          "size": "0xd8d6",
913          "stateRoot": "0x2ed13b153d1467deb397a789aabccb287590579cf231bf4f1ff34ac3b4905ce2",
914          "timestamp": "0x6282b31e",
915          "totalDifficulty": null,
916          "transactions": [
917            "0xaad0d53ae350dd06481b42cd097e3f3a4a31e0ac980fac4663b7dd913af20d9b"
918          ],
919          "transactionsRoot": "0xfc5c28cb82d5878172cd1b97429d44980d1cee03c782950ee73e83f1fa8bfb49",
920          "uncles": []
921        }
922        );
923        let block: Block<H256> = serde_json::from_value(json).unwrap();
924        assert!(block.author.is_none());
925    }
926
927    #[test]
928    fn can_deserialize_with_sealed_fields() {
929        let json = serde_json::json!({
930          "number": "0x1b4",
931          "hash": "0xdc0818cf78f21a8e70579cb46a43643f78291264dda342ae31049421c82d21ae",
932          "parentHash": "0xe99e022112df268087ea7eafaf4790497fd21dbeeb6bd7a1721df161a6657a54",
933          "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
934          "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
935          "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
936          "stateRoot": "0xddc8b0234c2e0cad087c8b389aa7ef01f7d79b2570bccb77ce48648aa61c904d",
937          "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
938          "miner": "0xbb7b8287f3f0a933474a79eae42cbca977791171",
939          "difficulty": "0x4ea3f27bc",
940          "totalDifficulty": "0x78ed983323d",
941          "sealFields": null,
942          "nonce": "0x689056015818adbe",
943          "mixHash": "0x4fffe9ae21f1c9e15207b1f472d5bbdd68c9595d461666602f2be20daf5e7843",
944          "extraData": "0x476574682f4c5649562f76312e302e302f6c696e75782f676f312e342e32",
945          "size": "0x0",
946          "gasLimit": "0x1388",
947          "gasUsed": "0x0",
948          "timestamp": "0x55ba467c",
949          "transactions": [],
950          "uncles": []
951        }
952              );
953        let _block: Block<TxHash> = serde_json::from_value(json).unwrap();
954    }
955}
956
957#[cfg(test)]
958#[cfg(feature = "celo")]
959mod celo_tests {
960    use super::*;
961
962    #[test]
963    fn block_without_snark_data() {
964        let block = r#"{"extraData":"0xd983010000846765746889676f312e31332e3130856c696e7578000000000000f8b2c0c080b841cfa11585812ec794c4baa46178690971b3c72e367211d68a9ea318ff500c5aeb7099cafc965240e3b57cf7355341cf76bdca74530334658370d2df7b2e030ab200f582027db017810fa05b4f35927f968f6be1a61e322d4ace3563feb8a489690a91c031fda640c55c216f6712a7bdde994338a5610080f58203ffb093cd643f5154979952791ff714eb885df0f18012f2211fb8c29a8947130dc3adf4ecb48a3c4a142a0faa51e5c60b048180","gasUsed":"0xbef6","hash":"0x37ac2818e50e61f0566caea102ed98677f2552fa86fed53443315ed11fe0eaad","logsBloom":"0x00000800000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000100000000000000000000000000000000000000000000000000000000000000080000000000001020000400000000000000000000000000000000000000000000000000000000000080000000000000000000000000400000000000000000000000000000000000000100000040004000000000000800000000000000000084000000000000000000000000000000000020000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000","miner":"0xcda518f6b5a797c3ec45d37c65b83e0b0748edca","number":"0x1b4","parentHash":"0xa6b4775f600c2981f9142cbc1361db02c7ba8c185a1110537b255356876301a2","randomness":{"committed":"0x049e84c89f1aa0e3a770b2545b05a30eb814dae322e7247fd2bf27e6cacb1f51","revealed":"0x5a8826bf59a7ed1ee86a9d6464fa9c1fcece78ffa7cf32b11a03ad251ddcefe6"},"receiptsRoot":"0x1724dc3e7c2bfa03974c1deedf5ea20ad30b72e25f3c62fbb5fd06fc107068d7","size":"0x3a0","stateRoot":"0xc45fa03e69dccb54b4981d23d77328ab8906ddd7a0d8238b9c54ae1a14df4d1c","timestamp":"0x5e90166d","totalDifficulty":"0x1b5","transactions":[{"blockHash":"0x37ac2818e50e61f0566caea102ed98677f2552fa86fed53443315ed11fe0eaad","blockNumber":"0x1b4","from":"0x456f41406b32c45d59e539e4bba3d7898c3584da","gas":"0x1312d00","gasPrice":"0x174876e800","feeCurrency":null,"gatewayFeeRecipient":null,"gatewayFee":"0x0","hash":"0xf7b1b588b1fc03305f556805812273d80fb61fc0ba7f812de27189e95c5ecfc5","input":"0xed385274000000000000000000000000b9ff7ab50a2f0fd3e2fb2814b016ac90c91df98f03386ba30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000be951906eba2aa800000","nonce":"0x147","to":"0xa12a699c641cc875a7ca57495861c79c33d293b4","transactionIndex":"0x0","value":"0x0","v":"0x15e08","r":"0x5787d040d09a34cb2b9ffcd096be7fe66aa6a3ed0632f182d1f3045640a9ef8b","s":"0x7897f58740f2a1c645826579106a620c306fc56381520ae2f28880bb284c4abd"}],"transactionsRoot":"0xbc8cb40b809914b9cd735b12e9b1802cf5d85de5223a22bbdb249a7e8b45ec93"}"#;
965        let block: Block<Transaction> = serde_json::from_str(block).unwrap();
966        assert_eq!(block.epoch_snark_data, None);
967    }
968
969    #[test]
970    fn block_with_snark_data() {
971        let block = r#"{"extraData":"0xd983010000846765746889676f312e31332e3130856c696e7578000000000000f8b2c0c080b841cfa11585812ec794c4baa46178690971b3c72e367211d68a9ea318ff500c5aeb7099cafc965240e3b57cf7355341cf76bdca74530334658370d2df7b2e030ab200f582027db017810fa05b4f35927f968f6be1a61e322d4ace3563feb8a489690a91c031fda640c55c216f6712a7bdde994338a5610080f58203ffb093cd643f5154979952791ff714eb885df0f18012f2211fb8c29a8947130dc3adf4ecb48a3c4a142a0faa51e5c60b048180","gasUsed":"0xbef6","hash":"0x37ac2818e50e61f0566caea102ed98677f2552fa86fed53443315ed11fe0eaad","logsBloom":"0x00000800000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000100000000000000000000000000000000000000000000000000000000000000080000000000001020000400000000000000000000000000000000000000000000000000000000000080000000000000000000000000400000000000000000000000000000000000000100000040004000000000000800000000000000000084000000000000000000000000000000000020000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000","miner":"0xcda518f6b5a797c3ec45d37c65b83e0b0748edca","number":"0x1b4","parentHash":"0xa6b4775f600c2981f9142cbc1361db02c7ba8c185a1110537b255356876301a2","randomness":{"committed":"0x049e84c89f1aa0e3a770b2545b05a30eb814dae322e7247fd2bf27e6cacb1f51","revealed":"0x5a8826bf59a7ed1ee86a9d6464fa9c1fcece78ffa7cf32b11a03ad251ddcefe6"},"receiptsRoot":"0x1724dc3e7c2bfa03974c1deedf5ea20ad30b72e25f3c62fbb5fd06fc107068d7","size":"0x3a0","stateRoot":"0xc45fa03e69dccb54b4981d23d77328ab8906ddd7a0d8238b9c54ae1a14df4d1c","timestamp":"0x5e90166d","totalDifficulty":"0x1b5","transactions":[{"blockHash":"0x37ac2818e50e61f0566caea102ed98677f2552fa86fed53443315ed11fe0eaad","blockNumber":"0x1b4","from":"0x456f41406b32c45d59e539e4bba3d7898c3584da","gas":"0x1312d00","gasPrice":"0x174876e800","feeCurrency":null,"gatewayFeeRecipient":null,"gatewayFee":"0x0","hash":"0xf7b1b588b1fc03305f556805812273d80fb61fc0ba7f812de27189e95c5ecfc5","input":"0xed385274000000000000000000000000b9ff7ab50a2f0fd3e2fb2814b016ac90c91df98f03386ba30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000be951906eba2aa800000","nonce":"0x147","to":"0xa12a699c641cc875a7ca57495861c79c33d293b4","transactionIndex":"0x0","value":"0x0","v":"0x15e08","r":"0x5787d040d09a34cb2b9ffcd096be7fe66aa6a3ed0632f182d1f3045640a9ef8b","s":"0x7897f58740f2a1c645826579106a620c306fc56381520ae2f28880bb284c4abd"}],"transactionsRoot":"0xbc8cb40b809914b9cd735b12e9b1802cf5d85de5223a22bbdb249a7e8b45ec93","epochSnarkData":{"bitmap": "0x01a72267ae3fe9fffb","signature": "0xcd803565d415c14b42d3aee51c5de1f6fd7d33cd036f03178c104c787a6ceafb8dd2b357d5fb5992fc2a23706625c800"}}"#;
972        let _block: Block<Transaction> = serde_json::from_str(block).unwrap();
973    }
974}