endpoint_libs/libs/
types.rs1use 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}