alloy_serde/
quantity.rs

1//! Serde functions for encoding primitive numbers using the Ethereum JSON-RPC "quantity" format.
2//!
3//! This is defined as a "hex encoded unsigned integer", with a special case of 0 being `0x0`.
4//!
5//! A regex for this format is: `^0x([1-9a-f]+[0-9a-f]*|0)$`.
6//!
7//! This is only valid for human-readable [`serde`] implementations.
8//! For non-human-readable implementations, the format is unspecified.
9//! Currently, it uses a fixed-width big-endian byte-array.
10
11use private::ConvertRuint;
12use serde::{Deserialize, Deserializer, Serialize, Serializer};
13
14/// Serializes a primitive number as a "quantity" hex string.
15pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
16where
17    T: ConvertRuint,
18    S: Serializer,
19{
20    value.into_ruint().serialize(serializer)
21}
22
23/// Deserializes a primitive number from a "quantity" hex string.
24pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
25where
26    T: ConvertRuint,
27    D: Deserializer<'de>,
28{
29    T::Ruint::deserialize(deserializer).map(T::from_ruint)
30}
31
32/// Serde functions for encoding optional primitive numbers using the Ethereum "quantity" format.
33///
34/// See [`quantity`](self) for more information.
35pub mod opt {
36    use super::private::ConvertRuint;
37    use serde::{Deserialize, Deserializer, Serializer};
38
39    /// Serializes an optional primitive number as a "quantity" hex string.
40    pub fn serialize<T, S>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
41    where
42        T: ConvertRuint,
43        S: Serializer,
44    {
45        match value {
46            Some(value) => serializer.serialize_some(&value.into_ruint()),
47            None => serializer.serialize_none(),
48        }
49    }
50
51    /// Deserializes an optional primitive number from a "quantity" hex string.
52    pub fn deserialize<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
53    where
54        T: ConvertRuint,
55        D: Deserializer<'de>,
56    {
57        Ok(Option::<T::Ruint>::deserialize(deserializer)?.map(T::from_ruint))
58    }
59}
60
61/// Serde functions for encoding a list of primitive numbers using the Ethereum "quantity" format.
62///
63/// See [`quantity`](self) for more information.
64pub mod vec {
65    use super::private::ConvertRuint;
66    use alloc::vec::Vec;
67    use core::{fmt, marker::PhantomData};
68    use serde::{
69        de::{SeqAccess, Visitor},
70        ser::SerializeSeq,
71        Deserializer, Serializer,
72    };
73
74    /// Serializes a vector of primitive numbers as a "quantity" hex string.
75    pub fn serialize<T, S>(value: &[T], serializer: S) -> Result<S::Ok, S::Error>
76    where
77        T: ConvertRuint,
78        S: Serializer,
79    {
80        let mut seq = serializer.serialize_seq(Some(value.len()))?;
81        for val in value {
82            seq.serialize_element(&val.into_ruint())?;
83        }
84        seq.end()
85    }
86
87    /// Deserializes a vector of primitive numbers from a "quantity" hex string.
88    pub fn deserialize<'de, T, D>(deserializer: D) -> Result<Vec<T>, D::Error>
89    where
90        T: ConvertRuint,
91        D: Deserializer<'de>,
92    {
93        struct VecVisitor<T> {
94            marker: PhantomData<T>,
95        }
96
97        impl<'de, T> Visitor<'de> for VecVisitor<T>
98        where
99            T: ConvertRuint,
100        {
101            type Value = Vec<T>;
102
103            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
104                formatter.write_str("a sequence")
105            }
106
107            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
108            where
109                A: SeqAccess<'de>,
110            {
111                let mut values = Vec::<T>::with_capacity(seq.size_hint().unwrap_or(0));
112
113                while let Some(value) = seq.next_element::<T::Ruint>()? {
114                    values.push(T::from_ruint(value));
115                }
116                Ok(values)
117            }
118        }
119
120        let visitor = VecVisitor { marker: PhantomData };
121        deserializer.deserialize_seq(visitor)
122    }
123}
124
125/// serde functions for handling `Vec<Vec<u128>>` via [U128](alloy_primitives::U128)
126pub mod u128_vec_vec_opt {
127    use alloy_primitives::U128;
128    use serde::{Deserialize, Deserializer, Serializer};
129
130    #[cfg(not(feature = "std"))]
131    use alloc::vec::Vec;
132
133    /// Deserializes an `u128` accepting a hex quantity string with optional 0x prefix or
134    /// a number
135    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Vec<Vec<u128>>>, D::Error>
136    where
137        D: Deserializer<'de>,
138    {
139        Option::<Vec<Vec<U128>>>::deserialize(deserializer)?.map_or_else(
140            || Ok(None),
141            |vec| {
142                Ok(Some(
143                    vec.into_iter().map(|v| v.into_iter().map(|val| val.to()).collect()).collect(),
144                ))
145            },
146        )
147    }
148
149    /// Serializes u128 as hex string
150    pub fn serialize<S: Serializer>(
151        value: &Option<Vec<Vec<u128>>>,
152        s: S,
153    ) -> Result<S::Ok, S::Error> {
154        match value {
155            Some(vec) => {
156                let vec = vec
157                    .iter()
158                    .map(|v| v.iter().map(|val| U128::from(*val)).collect::<Vec<_>>())
159                    .collect::<Vec<_>>();
160                s.serialize_some(&vec)
161            }
162            None => s.serialize_none(),
163        }
164    }
165}
166
167/// Serde functions for encoding a `HashMap` of primitive numbers using the Ethereum "quantity"
168/// format.
169///
170/// See [`quantity`](self) for more information.
171pub mod hashmap {
172    use super::private::ConvertRuint;
173    use alloy_primitives::map::HashMap;
174    use core::{fmt, hash::BuildHasher, marker::PhantomData};
175    use serde::{
176        de::MapAccess, ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer,
177    };
178
179    /// Serializes a `HashMap` of primitive numbers as a "quantity" hex string.
180    pub fn serialize<K, V, S, H>(map: &HashMap<K, V, H>, serializer: S) -> Result<S::Ok, S::Error>
181    where
182        K: ConvertRuint,
183        V: Serialize,
184        S: Serializer,
185        H: BuildHasher,
186    {
187        let mut map_ser = serializer.serialize_map(Some(map.len()))?;
188        for (key, value) in map {
189            map_ser.serialize_entry(&key.into_ruint(), value)?;
190        }
191        map_ser.end()
192    }
193
194    /// Deserializes a `HashMap` of primitive numbers from a "quantity" hex string.
195    pub fn deserialize<'de, K, V, D, H>(deserializer: D) -> Result<HashMap<K, V, H>, D::Error>
196    where
197        K: ConvertRuint + Eq + core::hash::Hash,
198        V: Deserialize<'de>,
199        D: Deserializer<'de>,
200        H: BuildHasher + Default,
201    {
202        struct HashMapVisitor<K, V, H> {
203            marker: PhantomData<(K, V, H)>,
204        }
205
206        impl<'de, K, V, H> serde::de::Visitor<'de> for HashMapVisitor<K, V, H>
207        where
208            K: ConvertRuint + Eq + core::hash::Hash,
209            V: Deserialize<'de>,
210            H: BuildHasher + Default,
211        {
212            type Value = HashMap<K, V, H>;
213
214            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
215                formatter.write_str("a map with quantity hex-encoded keys")
216            }
217
218            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
219            where
220                A: MapAccess<'de>,
221            {
222                let mut values =
223                    HashMap::with_capacity_and_hasher(map.size_hint().unwrap_or(0), H::default());
224
225                while let Some((key, value)) = map.next_entry::<K::Ruint, V>()? {
226                    values.insert(K::from_ruint(key), value);
227                }
228                Ok(values)
229            }
230        }
231
232        let visitor = HashMapVisitor { marker: PhantomData };
233        deserializer.deserialize_map(visitor)
234    }
235}
236
237/// Serde functions for encoding a `BTreeMap` of primitive numbers using the Ethereum "quantity"
238/// format.
239pub mod btreemap {
240    use super::private::ConvertRuint;
241    use alloc::collections::BTreeMap;
242    use core::{fmt, marker::PhantomData};
243    use serde::{
244        de::MapAccess, ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer,
245    };
246
247    /// Serializes a `BTreeMap` of primitive numbers as a "quantity" hex string.
248    pub fn serialize<K, V, S>(value: &BTreeMap<K, V>, serializer: S) -> Result<S::Ok, S::Error>
249    where
250        K: ConvertRuint + Ord,
251        V: Serialize,
252        S: Serializer,
253    {
254        let mut map = serializer.serialize_map(Some(value.len()))?;
255        for (key, val) in value {
256            map.serialize_entry(&key.into_ruint(), val)?;
257        }
258        map.end()
259    }
260
261    /// Deserializes a `BTreeMap` of primitive numbers from a "quantity" hex string.
262    pub fn deserialize<'de, K, V, D>(deserializer: D) -> Result<BTreeMap<K, V>, D::Error>
263    where
264        K: ConvertRuint + Ord,
265        V: Deserialize<'de>,
266        D: Deserializer<'de>,
267    {
268        struct BTreeMapVisitor<K, V> {
269            key_marker: PhantomData<K>,
270            value_marker: PhantomData<V>,
271        }
272
273        impl<'de, K, V> serde::de::Visitor<'de> for BTreeMapVisitor<K, V>
274        where
275            K: ConvertRuint + Ord,
276            V: Deserialize<'de>,
277        {
278            type Value = BTreeMap<K, V>;
279
280            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
281                formatter.write_str("a map with quantity hex-encoded keys")
282            }
283
284            fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
285            where
286                M: MapAccess<'de>,
287            {
288                let mut values = BTreeMap::new();
289
290                while let Some((key, value)) = map.next_entry::<K::Ruint, V>()? {
291                    values.insert(K::from_ruint(key), value);
292                }
293                Ok(values)
294            }
295        }
296
297        let visitor = BTreeMapVisitor { key_marker: PhantomData, value_marker: PhantomData };
298        deserializer.deserialize_map(visitor)
299    }
300}
301
302/// Private implementation details of the [`quantity`](self) module.
303#[expect(unnameable_types)]
304mod private {
305    #[doc(hidden)]
306    pub trait ConvertRuint: Copy + Sized {
307        // We have to use `Try*` traits because `From` is not implemented by ruint types.
308        // They shouldn't ever error.
309        type Ruint: Copy
310            + serde::Serialize
311            + serde::de::DeserializeOwned
312            + TryFrom<Self>
313            + TryInto<Self>;
314
315        #[inline]
316        fn into_ruint(self) -> Self::Ruint {
317            self.try_into().ok().unwrap()
318        }
319
320        #[inline]
321        fn from_ruint(ruint: Self::Ruint) -> Self {
322            ruint.try_into().ok().unwrap()
323        }
324    }
325
326    macro_rules! impl_from_ruint {
327        ($($primitive:ty = $ruint:ty),* $(,)?) => {
328            $(
329                impl ConvertRuint for $primitive {
330                    type Ruint = $ruint;
331                }
332            )*
333        };
334    }
335
336    impl_from_ruint! {
337        bool = alloy_primitives::ruint::aliases::U1,
338        u8   = alloy_primitives::U8,
339        u16  = alloy_primitives::U16,
340        u32  = alloy_primitives::U32,
341        u64  = alloy_primitives::U64,
342        u128 = alloy_primitives::U128,
343    }
344}
345
346#[cfg(test)]
347mod tests {
348    use alloc::{string::ToString, vec, vec::Vec};
349    use serde::{Deserialize, Serialize};
350
351    #[test]
352    fn test_hex_u64() {
353        #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
354        struct Value {
355            #[serde(with = "super")]
356            inner: u64,
357        }
358
359        let val = Value { inner: 1000 };
360        let s = serde_json::to_string(&val).unwrap();
361        assert_eq!(s, "{\"inner\":\"0x3e8\"}");
362
363        let deserialized: Value = serde_json::from_str(&s).unwrap();
364        assert_eq!(val, deserialized);
365    }
366
367    #[test]
368    fn test_u128_via_ruint() {
369        #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
370        struct Value {
371            #[serde(with = "super")]
372            inner: u128,
373        }
374
375        let val = Value { inner: 1000 };
376        let s = serde_json::to_string(&val).unwrap();
377        assert_eq!(s, "{\"inner\":\"0x3e8\"}");
378
379        let deserialized: Value = serde_json::from_str(&s).unwrap();
380        assert_eq!(val, deserialized);
381
382        let s = "{\"inner\":\"1000\"}".to_string();
383        let deserialized: Value = serde_json::from_str(&s).unwrap();
384
385        assert_eq!(val, deserialized);
386    }
387
388    #[test]
389    fn test_u128_opt_via_ruint() {
390        #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
391        struct Value {
392            #[serde(with = "super::opt")]
393            inner: Option<u128>,
394        }
395
396        let val = Value { inner: Some(1000) };
397        let s = serde_json::to_string(&val).unwrap();
398        assert_eq!(s, "{\"inner\":\"0x3e8\"}");
399
400        let deserialized: Value = serde_json::from_str(&s).unwrap();
401        assert_eq!(val, deserialized);
402
403        let s = "{\"inner\":\"1000\"}".to_string();
404        let deserialized: Value = serde_json::from_str(&s).unwrap();
405
406        assert_eq!(val, deserialized);
407
408        let val = Value { inner: None };
409        let s = serde_json::to_string(&val).unwrap();
410        assert_eq!(s, "{\"inner\":null}");
411
412        let deserialized: Value = serde_json::from_str(&s).unwrap();
413        assert_eq!(val, deserialized);
414    }
415
416    #[test]
417    fn test_u128_vec_via_ruint() {
418        #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
419        struct Value {
420            #[serde(with = "super::vec")]
421            inner: Vec<u128>,
422        }
423
424        let val = Value { inner: vec![1000, 2000] };
425        let s = serde_json::to_string(&val).unwrap();
426        assert_eq!(s, "{\"inner\":[\"0x3e8\",\"0x7d0\"]}");
427
428        let deserialized: Value = serde_json::from_str(&s).unwrap();
429        assert_eq!(val, deserialized);
430    }
431
432    #[test]
433    fn test_u128_vec_vec_opt_via_ruint() {
434        #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
435        struct Value {
436            #[serde(with = "super::u128_vec_vec_opt")]
437            inner: Option<Vec<Vec<u128>>>,
438        }
439
440        let val = Value { inner: Some(vec![vec![1000, 2000], vec![3000, 4000]]) };
441        let s = serde_json::to_string(&val).unwrap();
442        assert_eq!(s, "{\"inner\":[[\"0x3e8\",\"0x7d0\"],[\"0xbb8\",\"0xfa0\"]]}");
443
444        let deserialized: Value = serde_json::from_str(&s).unwrap();
445        assert_eq!(val, deserialized);
446
447        let val = Value { inner: None };
448        let s = serde_json::to_string(&val).unwrap();
449        assert_eq!(s, "{\"inner\":null}");
450
451        let deserialized: Value = serde_json::from_str(&s).unwrap();
452        assert_eq!(val, deserialized);
453    }
454
455    #[test]
456    fn test_u128_hashmap_via_ruint() {
457        use alloy_primitives::map::HashMap;
458
459        #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
460        struct Value {
461            #[serde(with = "super::hashmap")]
462            inner: HashMap<u128, u128>,
463        }
464
465        let mut inner_map = HashMap::default();
466        inner_map.insert(1000, 2000);
467        inner_map.insert(3000, 4000);
468
469        let val = Value { inner: inner_map.clone() };
470        let s = serde_json::to_string(&val).unwrap();
471
472        // Deserialize and verify that the original `val` and deserialized version match
473        let deserialized: Value = serde_json::from_str(&s).unwrap();
474        assert_eq!(val.inner, deserialized.inner);
475    }
476
477    #[test]
478    fn test_u128_btreemap_via_ruint() {
479        use alloc::collections::BTreeMap;
480
481        #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
482        struct Value {
483            #[serde(with = "super::btreemap")]
484            inner: BTreeMap<u128, u128>,
485        }
486
487        let mut inner_map = BTreeMap::new();
488        inner_map.insert(1000, 2000);
489        inner_map.insert(3000, 4000);
490
491        let val = Value { inner: inner_map };
492        let s = serde_json::to_string(&val).unwrap();
493        assert_eq!(s, "{\"inner\":{\"0x3e8\":2000,\"0xbb8\":4000}}");
494
495        let deserialized: Value = serde_json::from_str(&s).unwrap();
496        assert_eq!(val, deserialized);
497    }
498}