Skip to main content

anvil_core/eth/
serde_helpers.rs

1//! custom serde helper functions
2
3pub mod sequence {
4    use serde::{
5        Deserialize, Deserializer, Serialize, Serializer, de::DeserializeOwned, ser::SerializeSeq,
6    };
7
8    pub fn serialize<S, T>(val: &T, s: S) -> Result<S::Ok, S::Error>
9    where
10        S: Serializer,
11        T: Serialize,
12    {
13        let mut seq = s.serialize_seq(Some(1))?;
14        seq.serialize_element(val)?;
15        seq.end()
16    }
17
18    pub fn deserialize<'de, T, D>(d: D) -> Result<T, D::Error>
19    where
20        D: Deserializer<'de>,
21        T: DeserializeOwned,
22    {
23        let mut seq = Vec::<T>::deserialize(d)?;
24        if seq.len() != 1 {
25            return Err(serde::de::Error::custom(format!(
26                "expected params sequence with length 1 but got {}",
27                seq.len()
28            )));
29        }
30        Ok(seq.remove(0))
31    }
32}
33
34/// A module that deserializes an optional single-item sequence.
35pub mod optional_sequence {
36    use serde::{Deserialize, Deserializer, de::DeserializeOwned};
37
38    pub fn deserialize<'de, T, D>(d: D) -> Result<Option<T>, D::Error>
39    where
40        D: Deserializer<'de>,
41        T: DeserializeOwned,
42    {
43        let mut seq = Option::<Vec<Option<T>>>::deserialize(d)?.unwrap_or_default();
44        if seq.len() > 1 {
45            return Err(serde::de::Error::custom(format!(
46                "expected params sequence with length 0 or 1 but got {}",
47                seq.len()
48            )));
49        }
50        Ok(seq.pop().flatten())
51    }
52}
53
54/// A module that deserializes `[]` optionally
55pub mod empty_params {
56    use serde::{Deserialize, Deserializer};
57
58    pub fn deserialize<'de, D>(d: D) -> Result<(), D::Error>
59    where
60        D: Deserializer<'de>,
61    {
62        let seq = Option::<Vec<()>>::deserialize(d)?.unwrap_or_default();
63        if !seq.is_empty() {
64            return Err(serde::de::Error::custom(format!(
65                "expected params sequence with length 0 but got {}",
66                seq.len()
67            )));
68        }
69        Ok(())
70    }
71}
72
73/// A module that deserializes either a BlockNumberOrTag, or a simple number.
74pub mod lenient_block_number {
75    pub use alloy_eips::eip1898::LenientBlockNumberOrTag;
76    use alloy_rpc_types::BlockNumberOrTag;
77    use serde::{Deserialize, Deserializer};
78
79    /// deserializes either a BlockNumberOrTag, or a simple number.
80    pub use alloy_eips::eip1898::lenient_block_number_or_tag::deserialize as lenient_block_number;
81
82    /// Same as `lenient_block_number` but requires to be `[num; 1]`
83    pub fn lenient_block_number_seq<'de, D>(deserializer: D) -> Result<BlockNumberOrTag, D::Error>
84    where
85        D: Deserializer<'de>,
86    {
87        let num = <[LenientBlockNumberOrTag; 1]>::deserialize(deserializer)?[0].into();
88        Ok(num)
89    }
90}
91
92/// Deserializes a `trace_get` trace address, whose indices must be minimal hex quantities.
93pub mod trace_address {
94    use alloy_rpc_types::Index;
95    use serde::{Deserialize, Deserializer, de::Error};
96
97    pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Index>, D::Error>
98    where
99        D: Deserializer<'de>,
100    {
101        Vec::<String>::deserialize(deserializer)?
102            .iter()
103            .map(|index| {
104                parse(index).ok_or_else(|| {
105                    D::Error::custom(format!(
106                        "invalid trace address index {index:?}, expected a hex quantity"
107                    ))
108                })
109            })
110            .collect()
111    }
112
113    /// Parses `^0x(0|[1-9a-f][0-9a-f]*)$`.
114    fn parse(index: &str) -> Option<Index> {
115        let digits = index.strip_prefix("0x")?;
116        let minimal = digits == "0" || !digits.starts_with('0');
117        if !minimal || !digits.bytes().all(|b| matches!(b, b'0'..=b'9' | b'a'..=b'f')) {
118            return None;
119        }
120        usize::from_str_radix(digits, 16).ok().map(Index::from)
121    }
122}