starknet_core/serde/
unsigned_field_element.rs1use alloc::{fmt::Formatter, format};
2
3use crypto_bigint::U256;
4use serde::{
5 de::{Error as DeError, Visitor},
6 Deserializer, Serializer,
7};
8use serde_with::{DeserializeAs, SerializeAs};
9
10use starknet_types_core::felt::Felt;
11
12const PRIME: U256 =
13 U256::from_be_hex("0800000000000011000000000000000000000000000000000000000000000001");
14
15#[derive(Debug)]
17pub struct UfeHex;
18
19#[derive(Debug)]
21pub struct UfeHexOption;
22
23#[derive(Debug)]
26pub struct UfePendingBlockHash;
27
28struct UfeHexVisitor;
29struct UfeHexOptionVisitor;
30struct UfePendingBlockHashVisitor;
31
32impl SerializeAs<Felt> for UfeHex {
33 fn serialize_as<S>(value: &Felt, serializer: S) -> Result<S::Ok, S::Error>
34 where
35 S: Serializer,
36 {
37 if serializer.is_human_readable() {
38 serializer.serialize_str(&format!("{value:#x}"))
39 } else {
40 serializer.serialize_bytes(&value.to_bytes_be())
41 }
42 }
43}
44
45impl<'de> DeserializeAs<'de, Felt> for UfeHex {
46 fn deserialize_as<D>(deserializer: D) -> Result<Felt, D::Error>
47 where
48 D: Deserializer<'de>,
49 {
50 if deserializer.is_human_readable() {
51 deserializer.deserialize_any(UfeHexVisitor)
52 } else {
53 deserializer.deserialize_bytes(UfeHexVisitor)
54 }
55 }
56}
57
58impl Visitor<'_> for UfeHexVisitor {
59 type Value = Felt;
60
61 fn expecting(&self, formatter: &mut Formatter<'_>) -> alloc::fmt::Result {
62 write!(formatter, "a hex string, or an array of u8")
63 }
64
65 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
66 where
67 E: DeError,
68 {
69 Felt::from_hex(v).map_err(|err| DeError::custom(format!("invalid hex string: {err}")))
70 }
71
72 fn visit_bytes<E: serde::de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
73 let buf = <[u8; 32]>::try_from(v).map_err(serde::de::Error::custom)?;
74
75 if U256::from_be_slice(&buf) < PRIME {
76 Ok(Felt::from_bytes_be(&buf))
77 } else {
78 Err(serde::de::Error::custom("field element value out of range"))
79 }
80 }
81}
82
83impl SerializeAs<Option<Felt>> for UfeHexOption {
84 fn serialize_as<S>(value: &Option<Felt>, serializer: S) -> Result<S::Ok, S::Error>
85 where
86 S: Serializer,
87 {
88 if serializer.is_human_readable() {
89 match value {
90 Some(value) => serializer.serialize_str(&format!("{value:#064x}")),
91 None => serializer.serialize_none(),
92 }
93 } else {
94 match value {
95 Some(value) => serializer.serialize_bytes(&value.to_bytes_be()),
96 None => serializer.serialize_bytes(&[]),
97 }
98 }
99 }
100}
101
102impl<'de> DeserializeAs<'de, Option<Felt>> for UfeHexOption {
103 fn deserialize_as<D>(deserializer: D) -> Result<Option<Felt>, D::Error>
104 where
105 D: Deserializer<'de>,
106 {
107 if deserializer.is_human_readable() {
108 deserializer.deserialize_any(UfeHexOptionVisitor)
109 } else {
110 deserializer.deserialize_bytes(UfeHexOptionVisitor)
111 }
112 }
113}
114
115impl Visitor<'_> for UfeHexOptionVisitor {
116 type Value = Option<Felt>;
117
118 fn expecting(&self, formatter: &mut Formatter<'_>) -> alloc::fmt::Result {
119 write!(formatter, "string")
120 }
121
122 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
123 where
124 E: DeError,
125 {
126 match v {
127 "" => Ok(None),
128 _ => match Felt::from_hex(v) {
129 Ok(value) => Ok(Some(value)),
130 Err(err) => Err(DeError::custom(format!("invalid hex string: {err}"))),
131 },
132 }
133 }
134
135 fn visit_bytes<E: serde::de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
136 if v.is_empty() {
137 return Ok(None);
138 }
139
140 let buf = <[u8; 32]>::try_from(v).map_err(serde::de::Error::custom)?;
141
142 if U256::from_be_slice(&buf) < PRIME {
143 Ok(Some(Felt::from_bytes_be(&buf)))
144 } else {
145 Err(serde::de::Error::custom("field element value out of range"))
146 }
147 }
148}
149
150impl SerializeAs<Option<Felt>> for UfePendingBlockHash {
151 fn serialize_as<S>(value: &Option<Felt>, serializer: S) -> Result<S::Ok, S::Error>
152 where
153 S: Serializer,
154 {
155 if serializer.is_human_readable() {
156 match value {
157 Some(value) => serializer.serialize_str(&format!("{value:#064x}")),
158 None => serializer.serialize_none(),
160 }
161 } else {
162 match value {
163 Some(value) => serializer.serialize_bytes(&value.to_bytes_be()),
164 None => serializer.serialize_bytes(&[]),
165 }
166 }
167 }
168}
169
170impl<'de> DeserializeAs<'de, Option<Felt>> for UfePendingBlockHash {
171 fn deserialize_as<D>(deserializer: D) -> Result<Option<Felt>, D::Error>
172 where
173 D: Deserializer<'de>,
174 {
175 if deserializer.is_human_readable() {
176 deserializer.deserialize_any(UfePendingBlockHashVisitor)
177 } else {
178 deserializer.deserialize_bytes(UfePendingBlockHashVisitor)
179 }
180 }
181}
182
183impl Visitor<'_> for UfePendingBlockHashVisitor {
184 type Value = Option<Felt>;
185
186 fn expecting(&self, formatter: &mut Formatter<'_>) -> alloc::fmt::Result {
187 write!(formatter, "string")
188 }
189
190 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
191 where
192 E: DeError,
193 {
194 if v.is_empty() || v == "pending" || v == "None" {
195 Ok(None)
196 } else {
197 match Felt::from_hex(v) {
198 Ok(value) => Ok(Some(value)),
199 Err(err) => Err(DeError::custom(format!("invalid hex string: {err}"))),
200 }
201 }
202 }
203
204 fn visit_bytes<E: serde::de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
205 if v.is_empty() {
206 return Ok(None);
207 }
208
209 let buf = <[u8; 32]>::try_from(v).map_err(serde::de::Error::custom)?;
210
211 if U256::from_be_slice(&buf) < PRIME {
212 Ok(Some(Felt::from_bytes_be(&buf)))
213 } else {
214 Err(serde::de::Error::custom("field element value out of range"))
215 }
216 }
217}
218
219#[cfg(test)]
220mod tests {
221 use super::*;
222
223 use hex_literal::hex;
224 use serde::{Deserialize, Serialize};
225 use serde_with::serde_as;
226
227 #[serde_as]
228 #[derive(Serialize, Deserialize)]
229 struct TestStruct(#[serde_as(as = "UfeHex")] pub Felt);
230
231 #[serde_as]
232 #[derive(Serialize, Deserialize)]
233 struct TestOptionStruct(#[serde_as(as = "UfeHexOption")] pub Option<Felt>);
234
235 #[serde_as]
236 #[derive(Serialize, Deserialize)]
237 struct TestBlockHashStruct(#[serde_as(as = "UfePendingBlockHash")] pub Option<Felt>);
238
239 #[test]
240 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
241 fn bin_ser() {
242 let r = bincode::serialize(&TestStruct(Felt::ONE)).unwrap();
243 assert_eq!(
244 r,
245 hex!(
246 "2000000000000000 0000000000000000000000000000000000000000000000000000000000000001"
247 )
248 );
249 }
250
251 #[test]
252 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
253 fn bin_deser() {
254 let r = bincode::deserialize::<TestStruct>(&hex!(
255 "2000000000000000 0000000000000000000000000000000000000000000000000000000000000001"
256 ))
257 .unwrap();
258 assert_eq!(r.0, Felt::ONE);
259 }
260
261 #[test]
262 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
263 fn bin_deser_out_of_range() {
264 if bincode::deserialize::<TestStruct>(&hex!(
265 "2000000000000000 0800000000000011000000000000000000000000000000000000000000000001"
266 ))
267 .is_ok()
268 {
269 panic!("deserialization should fail")
270 }
271 }
272
273 #[test]
274 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
275 fn option_deser_empty_string() {
276 let r = serde_json::from_str::<TestOptionStruct>("\"\"").unwrap();
277 assert_eq!(r.0, None);
278 }
279
280 #[test]
281 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
282 fn option_bin_ser_none() {
283 let r = bincode::serialize(&TestOptionStruct(None)).unwrap();
284 assert_eq!(r, hex!("0000000000000000"));
285 }
286
287 #[test]
288 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
289 fn option_bin_deser_none() {
290 let r = bincode::deserialize::<TestOptionStruct>(&hex!("0000000000000000")).unwrap();
291 assert_eq!(r.0, None);
292 }
293
294 #[test]
295 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
296 fn pending_block_hash_deser_pending() {
297 let r = serde_json::from_str::<TestBlockHashStruct>("\"pending\"").unwrap();
298 assert_eq!(r.0, None);
299 }
300
301 #[test]
302 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
303 fn pending_block_hash_bin_ser_none() {
304 let r = bincode::serialize(&TestBlockHashStruct(None)).unwrap();
305 assert_eq!(r, hex!("0000000000000000"));
306 }
307
308 #[test]
309 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
310 fn pending_block_hash_bin_deser_none() {
311 let r = bincode::deserialize::<TestBlockHashStruct>(&hex!("0000000000000000")).unwrap();
312 assert_eq!(r.0, None);
313 }
314}