foundry_block_explorers/
block_number.rs

1use alloy_primitives::U64;
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use std::{fmt, str::FromStr};
4
5/// A block number or tag.
6#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
7pub enum BlockNumber {
8    /// Latest block
9    #[default]
10    Latest,
11    /// Finalized block accepted as canonical
12    Finalized,
13    /// Safe head block
14    Safe,
15    /// Earliest block (genesis)
16    Earliest,
17    /// Pending block (not yet part of the blockchain)
18    Pending,
19    /// Block by number from canon chain
20    Number(U64),
21}
22
23impl BlockNumber {
24    /// Returns the numeric block number if explicitly set
25    pub fn as_number(&self) -> Option<U64> {
26        match *self {
27            BlockNumber::Number(num) => Some(num),
28            _ => None,
29        }
30    }
31
32    /// Returns `true` if a numeric block number is set
33    pub fn is_number(&self) -> bool {
34        matches!(self, BlockNumber::Number(_))
35    }
36
37    /// Returns `true` if it's "latest"
38    pub fn is_latest(&self) -> bool {
39        matches!(self, BlockNumber::Latest)
40    }
41
42    /// Returns `true` if it's "finalized"
43    pub fn is_finalized(&self) -> bool {
44        matches!(self, BlockNumber::Finalized)
45    }
46
47    /// Returns `true` if it's "safe"
48    pub fn is_safe(&self) -> bool {
49        matches!(self, BlockNumber::Safe)
50    }
51
52    /// Returns `true` if it's "pending"
53    pub fn is_pending(&self) -> bool {
54        matches!(self, BlockNumber::Pending)
55    }
56
57    /// Returns `true` if it's "earliest"
58    pub fn is_earliest(&self) -> bool {
59        matches!(self, BlockNumber::Earliest)
60    }
61}
62
63impl<T: Into<U64>> From<T> for BlockNumber {
64    fn from(num: T) -> Self {
65        BlockNumber::Number(num.into())
66    }
67}
68
69impl Serialize for BlockNumber {
70    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
71    where
72        S: Serializer,
73    {
74        match *self {
75            BlockNumber::Number(ref x) => serializer.serialize_str(&format!("0x{x:x}")),
76            BlockNumber::Latest => serializer.serialize_str("latest"),
77            BlockNumber::Finalized => serializer.serialize_str("finalized"),
78            BlockNumber::Safe => serializer.serialize_str("safe"),
79            BlockNumber::Earliest => serializer.serialize_str("earliest"),
80            BlockNumber::Pending => serializer.serialize_str("pending"),
81        }
82    }
83}
84
85impl<'de> Deserialize<'de> for BlockNumber {
86    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
87    where
88        D: Deserializer<'de>,
89    {
90        let s = String::deserialize(deserializer)?.to_lowercase();
91        s.parse().map_err(serde::de::Error::custom)
92    }
93}
94
95impl FromStr for BlockNumber {
96    type Err = String;
97
98    fn from_str(s: &str) -> Result<Self, Self::Err> {
99        match s {
100            "latest" => Ok(Self::Latest),
101            "finalized" => Ok(Self::Finalized),
102            "safe" => Ok(Self::Safe),
103            "earliest" => Ok(Self::Earliest),
104            "pending" => Ok(Self::Pending),
105            // hex
106            n if n.starts_with("0x") => n.parse().map(Self::Number).map_err(|e| e.to_string()),
107            // decimal
108            n => n.parse::<u64>().map(|n| Self::Number(U64::from(n))).map_err(|e| e.to_string()),
109        }
110    }
111}
112
113impl fmt::Display for BlockNumber {
114    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        match self {
116            BlockNumber::Number(ref x) => format!("0x{x:x}").fmt(f),
117            BlockNumber::Latest => f.write_str("latest"),
118            BlockNumber::Finalized => f.write_str("finalized"),
119            BlockNumber::Safe => f.write_str("safe"),
120            BlockNumber::Earliest => f.write_str("earliest"),
121            BlockNumber::Pending => f.write_str("pending"),
122        }
123    }
124}