1pub mod hex_bytes {
3 use serde::{Deserialize, Deserializer, Serializer};
4
5 pub fn serialize<S, T>(x: T, s: S) -> Result<S::Ok, S::Error>
7 where
8 S: Serializer,
9 T: AsRef<[u8]>,
10 {
11 s.serialize_str(&format!("0x{encoded}", encoded = hex::encode(x.as_ref())))
12 }
13
14 pub fn deserialize<'de, T, D>(d: D) -> Result<T, D::Error>
17 where
18 D: Deserializer<'de>,
19 T: From<Vec<u8>>,
20 {
21 let value = String::deserialize(d)?;
22 if let Some(value) = value.strip_prefix("0x") {
23 hex::decode(value)
24 } else {
25 hex::decode(&value)
26 }
27 .map(Into::into)
28 .map_err(|e| serde::de::Error::custom(e.to_string()))
29 }
30}
31
32pub mod hex_bytes_option {
34 use serde::{Deserialize, Deserializer, Serializer};
35
36 pub fn serialize<S, T>(x: &Option<T>, s: S) -> Result<S::Ok, S::Error>
38 where
39 S: Serializer,
40 T: AsRef<[u8]>,
41 {
42 if let Some(x) = x {
43 s.serialize_str(&format!("0x{encoded}", encoded = hex::encode(x.as_ref())))
44 } else {
45 s.serialize_none()
46 }
47 }
48
49 pub fn deserialize<'de, T, D>(d: D) -> Result<Option<T>, D::Error>
52 where
53 D: Deserializer<'de>,
54 T: From<Vec<u8>>,
55 {
56 let value: Option<String> = Option::deserialize(d)?;
57
58 match value {
59 Some(val) => {
60 let val = if let Some(stripped) = val.strip_prefix("0x") { stripped } else { &val };
61 hex::decode(val)
62 .map(Into::into)
63 .map(Some)
64 .map_err(|e| serde::de::Error::custom(e.to_string()))
65 }
66 None => Ok(None),
67 }
68 }
69}
70
71pub mod protocol_states {
77 use std::collections::HashMap;
78
79 use serde::{ser::SerializeMap, Deserialize, Deserializer, Serializer};
80 use tracing::{debug, warn};
81 use tycho_common::simulation::protocol_sim::ProtocolSim;
82
83 pub fn serialize<S>(
86 states: &HashMap<String, Box<dyn ProtocolSim>>,
87 serializer: S,
88 ) -> Result<S::Ok, S::Error>
89 where
90 S: Serializer,
91 {
92 let mut map = serializer.serialize_map(None)?;
93 let mut skipped = 0u32;
94 for (key, value) in states {
95 match serde_json::to_value(value.as_ref()) {
96 Ok(json_val) => map.serialize_entry(key, &json_val)?,
97 Err(err) => {
98 debug!(key, %err, "skipping non-serializable ProtocolSim entry");
99 skipped += 1;
100 }
101 }
102 }
103 if skipped > 0 {
104 warn!(skipped, total = states.len(), "skipped non-serializable ProtocolSim entries");
105 }
106 map.end()
107 }
108
109 pub fn deserialize<'de, D>(
112 deserializer: D,
113 ) -> Result<HashMap<String, Box<dyn ProtocolSim>>, D::Error>
114 where
115 D: Deserializer<'de>,
116 {
117 HashMap::<String, Box<dyn ProtocolSim>>::deserialize(deserializer)
118 }
119}
120
121#[macro_export]
129macro_rules! impl_non_serializable_protocol {
130 ($type:ty, $msg:expr) => {
131 impl serde::Serialize for $type {
132 fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
133 where
134 S: serde::Serializer,
135 {
136 Err(serde::ser::Error::custom($msg))
137 }
138 }
139
140 impl<'de> serde::Deserialize<'de> for $type {
141 fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
142 where
143 D: serde::Deserializer<'de>,
144 {
145 Err(serde::de::Error::custom($msg))
146 }
147 }
148 };
149}
150
151#[cfg(test)]
152mod tests {
153 use std::collections::HashMap;
154
155 use serde::{Deserialize, Serialize};
156 use serde_json;
157 use tycho_common::simulation::protocol_sim::ProtocolSim;
158
159 use super::*;
160 use crate::protocol::models::Update;
161
162 #[derive(Debug, Serialize, Deserialize)]
163 struct TestStruct {
164 #[serde(with = "hex_bytes")]
165 bytes: Vec<u8>,
166
167 #[serde(with = "hex_bytes_option")]
168 bytes_option: Option<Vec<u8>>,
169 }
170
171 #[test]
172 fn hex_bytes_serialize_deserialize() {
173 let test_struct = TestStruct { bytes: vec![0u8; 10], bytes_option: Some(vec![0u8; 10]) };
174
175 let serialized = serde_json::to_string(&test_struct).unwrap();
177 assert_eq!(
178 serialized,
179 "{\"bytes\":\"0x00000000000000000000\",\"bytes_option\":\"0x00000000000000000000\"}"
180 );
181
182 let deserialized: TestStruct = serde_json::from_str(&serialized).unwrap();
184 assert_eq!(deserialized.bytes, vec![0u8; 10]);
185 assert_eq!(deserialized.bytes_option, Some(vec![0u8; 10]));
186 }
187
188 #[test]
189 fn hex_bytes_option_none() {
190 let test_struct = TestStruct { bytes: vec![0u8; 10], bytes_option: None };
191
192 let serialized = serde_json::to_string(&test_struct).unwrap();
194 assert_eq!(serialized, "{\"bytes\":\"0x00000000000000000000\",\"bytes_option\":null}");
195
196 let deserialized: TestStruct = serde_json::from_str(&serialized).unwrap();
198 assert_eq!(deserialized.bytes, vec![0u8; 10]);
199 assert_eq!(deserialized.bytes_option, None);
200 }
201
202 #[cfg(feature = "evm")]
203 #[test]
204 fn update_roundtrip_with_serializable_state() {
205 use alloy::primitives::U256;
206
207 use crate::evm::protocol::uniswap_v2::state::UniswapV2State;
208
209 let mut states: HashMap<String, Box<dyn ProtocolSim>> = HashMap::new();
210 states.insert(
211 "pool_a".to_string(),
212 Box::new(UniswapV2State::new(U256::from(1000), U256::from(2000))),
213 );
214
215 let update = Update::new(12345, states, HashMap::new());
216 let json = serde_json::to_string(&update).unwrap();
217
218 assert!(json.contains("pool_a"));
219
220 let roundtripped: Update = serde_json::from_str(&json).unwrap();
221 assert_eq!(roundtripped.block_number_or_timestamp, 12345);
222 assert_eq!(roundtripped.states.len(), 1);
223 assert!(roundtripped
224 .states
225 .contains_key("pool_a"));
226 }
227
228 #[cfg(feature = "evm")]
229 #[test]
230 fn protocol_states_skips_non_serializable_entries() {
231 use alloy::primitives::U256;
232
233 use crate::evm::protocol::{
234 uniswap_v2::state::UniswapV2State,
235 uniswap_v4::state::{UniswapV4Fees, UniswapV4State},
236 };
237
238 let mut states: HashMap<String, Box<dyn ProtocolSim>> = HashMap::new();
239 states.insert(
240 "serializable".to_string(),
241 Box::new(UniswapV2State::new(U256::from(1000), U256::from(2000))),
242 );
243 states.insert(
244 "non_serializable".to_string(),
245 Box::new(
246 UniswapV4State::new(0, U256::from(1), UniswapV4Fees::new(0, 0, 3000), 0, 1, vec![])
247 .expect("valid state"),
248 ),
249 );
250
251 let update = Update::new(42, states, HashMap::new());
252 let json = serde_json::to_string(&update).expect("serialization should succeed");
253 let parsed: serde_json::Value = serde_json::from_str(&json).expect("valid JSON");
254
255 let states_map = parsed["states"]
256 .as_object()
257 .expect("states is a map");
258 assert_eq!(states_map.len(), 1, "only the serializable entry should survive");
259 assert!(states_map.contains_key("serializable"));
260 assert!(!states_map.contains_key("non_serializable"));
261 }
262
263 #[cfg(feature = "evm")]
264 #[test]
265 fn protocol_states_serialize_produces_valid_json() {
266 use alloy::primitives::U256;
267
268 use crate::evm::protocol::uniswap_v2::state::UniswapV2State;
269
270 let mut states: HashMap<String, Box<dyn ProtocolSim>> = HashMap::new();
271 states.insert(
272 "pool_x".to_string(),
273 Box::new(UniswapV2State::new(U256::from(100), U256::from(200))),
274 );
275 states.insert(
276 "pool_y".to_string(),
277 Box::new(UniswapV2State::new(U256::from(300), U256::from(400))),
278 );
279
280 #[derive(Serialize)]
281 struct Wrapper {
282 #[serde(with = "protocol_states")]
283 states: HashMap<String, Box<dyn ProtocolSim>>,
284 }
285
286 let wrapper = Wrapper { states };
287 let json = serde_json::to_value(&wrapper).unwrap();
288 let map = json["states"].as_object().unwrap();
289 assert_eq!(map.len(), 2);
290 assert!(map.contains_key("pool_x"));
291 assert!(map.contains_key("pool_y"));
292 }
293
294 #[test]
295 fn update_roundtrip_empty() {
296 let update = Update::new(99999, HashMap::new(), HashMap::new());
297 let json = serde_json::to_string(&update).unwrap();
298 let roundtripped: Update = serde_json::from_str(&json).unwrap();
299 assert_eq!(roundtripped.block_number_or_timestamp, 99999);
300 assert!(roundtripped.states.is_empty());
301 assert!(roundtripped.new_pairs.is_empty());
302 }
303}