foundry_block_explorers/
serde_helpers.rs1use crate::block_number::BlockNumber;
2use alloy_primitives::{U64, U256};
3use serde::{Deserialize, Deserializer};
4use std::str::FromStr;
5
6#[derive(Deserialize, Debug, Clone)]
8#[serde(untagged)]
9pub enum StringifiedNumeric {
10 String(String),
11 U256(U256),
12 Num(serde_json::Number),
13}
14
15impl TryFrom<StringifiedNumeric> for U256 {
16 type Error = String;
17
18 fn try_from(value: StringifiedNumeric) -> Result<Self, Self::Error> {
19 match value {
20 StringifiedNumeric::U256(n) => Ok(n),
21 StringifiedNumeric::Num(n) => {
22 Ok(U256::from_str(&n.to_string()).map_err(|err| err.to_string())?)
23 }
24 StringifiedNumeric::String(s) => {
25 if let Ok(val) = s.parse::<u128>() {
26 Ok(U256::from(val))
27 } else if s.starts_with("0x") {
28 U256::from_str_radix(s.strip_prefix("0x").unwrap(), 16)
31 .map_err(|err| err.to_string())
32 } else {
33 U256::from_str(&s).map_err(|err| err.to_string())
34 }
35 }
36 }
37 }
38}
39
40impl TryFrom<StringifiedNumeric> for U64 {
41 type Error = String;
42
43 fn try_from(value: StringifiedNumeric) -> Result<Self, Self::Error> {
44 let value = U256::try_from(value)?;
45 Ok(value.wrapping_to::<U64>())
46 }
47}
48
49#[derive(Deserialize)]
50#[serde(untagged)]
51enum BoolOrU64 {
52 #[serde(deserialize_with = "deserialize_stringified_u64")]
53 U64(u64),
54 Bool(String),
55}
56
57pub fn deserialize_stringified_bool_or_u64<'de, D>(deserializer: D) -> Result<u64, D::Error>
62where
63 D: Deserializer<'de>,
64{
65 let num = BoolOrU64::deserialize(deserializer)?;
66 match num {
67 BoolOrU64::Bool(b) => {
68 let b = b.parse::<bool>().map_err(serde::de::Error::custom)?;
69 let u = if b { 1 } else { 0 };
70 Ok(u)
71 }
72 BoolOrU64::U64(u) => Ok(u),
73 }
74}
75
76pub fn deserialize_stringified_u64<'de, D>(deserializer: D) -> Result<u64, D::Error>
80where
81 D: Deserializer<'de>,
82{
83 let num = StringifiedNumeric::deserialize(deserializer)?;
84 let num: U256 = num.try_into().map_err(serde::de::Error::custom)?;
85 num.try_into().map_err(serde::de::Error::custom)
86}
87
88pub fn deserialize_stringified_numeric<'de, D>(deserializer: D) -> Result<U256, D::Error>
92where
93 D: Deserializer<'de>,
94{
95 let num = StringifiedNumeric::deserialize(deserializer)?;
96 num.try_into().map_err(serde::de::Error::custom)
97}
98
99pub fn deserialize_stringified_numeric_opt<'de, D>(
103 deserializer: D,
104) -> Result<Option<U256>, D::Error>
105where
106 D: Deserializer<'de>,
107{
108 if let Some(num) = Option::<StringifiedNumeric>::deserialize(deserializer)? {
109 num.try_into().map(Some).map_err(serde::de::Error::custom)
110 } else {
111 Ok(None)
112 }
113}
114
115pub fn deserialize_stringified_u64_opt<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
119where
120 D: Deserializer<'de>,
121{
122 if let Some(num) = Option::<StringifiedNumeric>::deserialize(deserializer)? {
123 let num: U256 = num.try_into().map_err(serde::de::Error::custom)?;
124 let num: u64 = num.try_into().map_err(serde::de::Error::custom)?;
125 Ok(Some(num))
126 } else {
127 Ok(None)
128 }
129}
130
131#[derive(Deserialize, Debug, Clone)]
133#[serde(untagged)]
134pub enum StringifiedBlockNumber {
135 Numeric(StringifiedNumeric),
136 BlockNumber(BlockNumber),
137}
138
139impl TryFrom<StringifiedBlockNumber> for BlockNumber {
140 type Error = String;
141
142 fn try_from(value: StringifiedBlockNumber) -> Result<Self, Self::Error> {
143 match value {
144 StringifiedBlockNumber::BlockNumber(b) => Ok(b),
145 StringifiedBlockNumber::Numeric(num) => match num {
146 StringifiedNumeric::String(s) => BlockNumber::from_str(&s),
147 other => {
148 let u256 = U256::try_from(other)?;
149 let n = u64::try_from(u256).map_err(|e| e.to_string())?;
150 Ok(BlockNumber::Number(U64::from(n)))
151 }
152 },
153 }
154 }
155}
156
157pub fn deserialize_stringified_block_number<'de, D>(
161 deserializer: D,
162) -> Result<BlockNumber, D::Error>
163where
164 D: Deserializer<'de>,
165{
166 let num = StringifiedBlockNumber::deserialize(deserializer)?;
167 num.try_into().map_err(serde::de::Error::custom)
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173 use serde::{Deserialize, Serialize};
174
175 #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
176 struct Txn {
177 #[serde(deserialize_with = "deserialize_stringified_block_number")]
178 bn: BlockNumber,
179 }
180
181 #[test]
182 fn deserializes_hex_with_0x_prefix() {
183 let json = r#"{ "bn": "0x1" }"#;
184 let tx: Txn = serde_json::from_str(json).unwrap();
185 assert_eq!(tx.bn, BlockNumber::Number(U64::from(1)));
186 }
187
188 #[test]
189 fn deserializes_decimal_string() {
190 let json = r#"{ "bn": "42" }"#;
191 let tx: Txn = serde_json::from_str(json).unwrap();
192 assert_eq!(tx.bn, BlockNumber::Number(U64::from(42)));
193 }
194
195 #[test]
196 fn deserializes_tag_latest() {
197 let json = r#"{ "bn": "latest" }"#;
198 let tx: Txn = serde_json::from_str(json).unwrap();
199 assert_eq!(tx.bn, BlockNumber::Latest);
200 }
201 #[test]
202 fn deserializes_large_hex_u64_max() {
203 let json = r#"{ "bn": "0xffffffffffffffff" }"#;
204 let tx: Txn = serde_json::from_str(json).unwrap();
205 assert_eq!(tx.bn, BlockNumber::Number(U64::MAX));
206 }
207
208 #[test]
209 fn roundtrip_serialized_hex_still_deserializes() {
210 let tx = Txn { bn: BlockNumber::Number(U64::from(42)) };
211 let s = serde_json::to_string(&tx).unwrap();
212 let de: Txn = serde_json::from_str(&s).unwrap();
213 assert_eq!(de, tx);
214 }
215}