Skip to main content

endpoint_libs/libs/
types.rs

1use std::fmt::Debug;
2use std::ops::{Deref, DerefMut};
3use std::str::FromStr;
4
5#[doc(hidden)]
6pub use alloy_primitives::{Address, B256 as H256, U256};
7use serde::de::Error;
8use serde::{Deserialize, Deserializer, Serialize, Serializer};
9
10fn parse_blockchain_scalar<E: Error, T: FromStr>(
11    value: &str,
12    prefix: &'static str,
13    digits: usize,
14) -> Result<T, E>
15where
16    T::Err: std::fmt::Display,
17{
18    let bytes = value.as_bytes();
19    if bytes.len() != prefix.len() + digits
20        || !value.starts_with(prefix)
21        || !bytes[prefix.len()..].iter().all(u8::is_ascii_hexdigit)
22    {
23        return Err(E::custom(format!(
24            "expected {prefix} followed by exactly {digits} hexadecimal digits"
25        )));
26    }
27
28    T::from_str(value).map_err(|error| E::custom(error.to_string()))
29}
30
31#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
32pub struct BlockchainAddress(pub Address);
33impl Debug for BlockchainAddress {
34    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35        self.0.fmt(f)
36    }
37}
38#[allow(non_snake_case)]
39pub mod WithBlockchainAddress {
40    use super::*;
41
42    pub fn serialize<S>(this: &Address, serializer: S) -> Result<S::Ok, S::Error>
43    where
44        S: Serializer,
45    {
46        format!("{this:?}").serialize(serializer)
47    }
48    pub fn deserialize<'de, D>(deserializer: D) -> Result<Address, D::Error>
49    where
50        D: Deserializer<'de>,
51    {
52        let value = String::deserialize(deserializer)?;
53        parse_blockchain_scalar::<D::Error, Address>(&value, "0x", 40)
54    }
55}
56impl Serialize for BlockchainAddress {
57    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
58    where
59        S: Serializer,
60    {
61        format!("{:?}", self.0).serialize(serializer)
62    }
63}
64impl<'de> Deserialize<'de> for BlockchainAddress {
65    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
66    where
67        D: Deserializer<'de>,
68    {
69        let value = String::deserialize(deserializer)?;
70        parse_blockchain_scalar::<D::Error, Address>(&value, "0x", 40).map(BlockchainAddress)
71    }
72}
73
74impl From<Address> for BlockchainAddress {
75    fn from(address: Address) -> Self {
76        BlockchainAddress(address)
77    }
78}
79impl From<BlockchainAddress> for Address {
80    fn from(val: BlockchainAddress) -> Self {
81        val.0
82    }
83}
84
85impl Deref for BlockchainAddress {
86    type Target = Address;
87    fn deref(&self) -> &Self::Target {
88        &self.0
89    }
90}
91impl DerefMut for BlockchainAddress {
92    fn deref_mut(&mut self) -> &mut Self::Target {
93        &mut self.0
94    }
95}
96#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
97pub struct BlockchainTransactionHash(pub H256);
98impl Debug for BlockchainTransactionHash {
99    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100        self.0.fmt(f)
101    }
102}
103#[allow(non_snake_case)]
104pub mod WithBlockchainTransactionHash {
105    use super::*;
106
107    pub fn serialize<S>(this: &H256, serializer: S) -> Result<S::Ok, S::Error>
108    where
109        S: Serializer,
110    {
111        format!("{this:?}").serialize(serializer)
112    }
113    pub fn deserialize<'de, D>(deserializer: D) -> Result<H256, D::Error>
114    where
115        D: Deserializer<'de>,
116    {
117        let value = String::deserialize(deserializer)?;
118        parse_blockchain_scalar::<D::Error, H256>(&value, "0x", 64)
119    }
120}
121impl Serialize for BlockchainTransactionHash {
122    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
123    where
124        S: Serializer,
125    {
126        format!("{:?}", self.0).serialize(serializer)
127    }
128}
129impl<'de> Deserialize<'de> for BlockchainTransactionHash {
130    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
131    where
132        D: Deserializer<'de>,
133    {
134        let value = String::deserialize(deserializer)?;
135        parse_blockchain_scalar::<D::Error, H256>(&value, "0x", 64).map(BlockchainTransactionHash)
136    }
137}
138
139impl From<H256> for BlockchainTransactionHash {
140    fn from(hash: H256) -> Self {
141        BlockchainTransactionHash(hash)
142    }
143}
144impl From<BlockchainTransactionHash> for H256 {
145    fn from(val: BlockchainTransactionHash) -> Self {
146        val.0
147    }
148}
149
150impl Deref for BlockchainTransactionHash {
151    type Target = H256;
152    fn deref(&self) -> &Self::Target {
153        &self.0
154    }
155}
156impl DerefMut for BlockchainTransactionHash {
157    fn deref_mut(&mut self) -> &mut Self::Target {
158        &mut self.0
159    }
160}
161
162#[cfg(test)]
163mod scalar_decode_tests {
164    use super::*;
165    use serde::de::value::{Error as DecodeError, StrDeserializer};
166
167    #[test]
168    fn scalar_entry_points_decode_hex_values_and_reject_malformed_input() {
169        let address = format!("0x{}", "aB".repeat(20));
170        let hash = format!("0x{}", "aB".repeat(32));
171        let decode = |value| StrDeserializer::<DecodeError>::new(value);
172        assert_eq!(
173            WithBlockchainAddress::deserialize(decode(&address)).unwrap(),
174            Address::from_str(&address).unwrap()
175        );
176        assert_eq!(
177            BlockchainAddress::deserialize(decode(&address)).unwrap().0,
178            Address::from_str(&address).unwrap()
179        );
180        assert_eq!(
181            WithBlockchainTransactionHash::deserialize(decode(&hash)).unwrap(),
182            H256::from_str(&hash).unwrap()
183        );
184        assert_eq!(
185            BlockchainTransactionHash::deserialize(decode(&hash))
186                .unwrap()
187                .0,
188            H256::from_str(&hash).unwrap()
189        );
190        for invalid in [
191            "",
192            "0x",
193            "é",
194            "0x🦀",
195            &"0".repeat(40),
196            &format!("0x{}", "g".repeat(64)),
197            &format!("0X{}", "a".repeat(40)),
198            &format!("0x{}", "a".repeat(65)),
199        ] {
200            assert!(WithBlockchainAddress::deserialize(decode(invalid)).is_err());
201            assert!(BlockchainAddress::deserialize(decode(invalid)).is_err());
202            assert!(WithBlockchainTransactionHash::deserialize(decode(invalid)).is_err());
203            assert!(BlockchainTransactionHash::deserialize(decode(invalid)).is_err());
204        }
205    }
206}