1use crate::errors::{PolyfillError, Result};
14use crate::types::*;
15use alloy_primitives::{Address, U256};
16use chrono::{DateTime, Utc};
17use rust_decimal::Decimal;
18use serde::{Deserialize, Deserializer};
19use serde_json::Value;
20use std::str::FromStr;
21
22pub mod deserializers {
24 use super::*;
25 use std::fmt::Display;
26
27 pub fn number_from_string<'de, T, D>(deserializer: D) -> std::result::Result<T, D::Error>
32 where
33 D: Deserializer<'de>,
34 T: FromStr + serde::Deserialize<'de> + Clone,
35 <T as FromStr>::Err: Display,
36 {
37 let value = serde_json::Value::deserialize(deserializer)?;
38 match value {
39 serde_json::Value::Number(n) => {
40 if let Some(v) = n.as_u64() {
41 T::deserialize(serde_json::Value::Number(serde_json::Number::from(v)))
42 .map_err(|_| serde::de::Error::custom("Failed to deserialize number"))
43 } else if let Some(v) = n.as_f64() {
44 T::deserialize(serde_json::Value::Number(
45 serde_json::Number::from_f64(v).unwrap(),
46 ))
47 .map_err(|_| serde::de::Error::custom("Failed to deserialize number"))
48 } else {
49 Err(serde::de::Error::custom("Invalid number format"))
50 }
51 },
52 serde_json::Value::String(s) => s.parse::<T>().map_err(serde::de::Error::custom),
53 _ => Err(serde::de::Error::custom("Expected number or string")),
54 }
55 }
56
57 pub fn optional_number_from_string<'de, T, D>(
62 deserializer: D,
63 ) -> std::result::Result<Option<T>, D::Error>
64 where
65 D: Deserializer<'de>,
66 T: FromStr + serde::Deserialize<'de> + Clone,
67 <T as FromStr>::Err: Display,
68 {
69 let value = serde_json::Value::deserialize(deserializer)?;
70 match value {
71 serde_json::Value::Null => Ok(None),
72 serde_json::Value::Number(n) => {
73 if let Some(v) = n.as_u64() {
74 T::deserialize(serde_json::Value::Number(serde_json::Number::from(v)))
75 .map(Some)
76 .map_err(|_| serde::de::Error::custom("Failed to deserialize number"))
77 } else if let Some(v) = n.as_f64() {
78 T::deserialize(serde_json::Value::Number(
79 serde_json::Number::from_f64(v).unwrap(),
80 ))
81 .map(Some)
82 .map_err(|_| serde::de::Error::custom("Failed to deserialize number"))
83 } else {
84 Err(serde::de::Error::custom("Invalid number format"))
85 }
86 },
87 serde_json::Value::String(s) => {
88 if s.is_empty() {
89 Ok(None)
90 } else {
91 s.parse::<T>().map(Some).map_err(serde::de::Error::custom)
92 }
93 },
94 _ => Err(serde::de::Error::custom("Expected number, string, or null")),
95 }
96 }
97
98 pub fn datetime_from_timestamp<'de, D>(
100 deserializer: D,
101 ) -> std::result::Result<DateTime<Utc>, D::Error>
102 where
103 D: Deserializer<'de>,
104 {
105 let timestamp = number_from_string::<u64, D>(deserializer)?;
106 DateTime::from_timestamp(timestamp as i64, 0)
107 .ok_or_else(|| serde::de::Error::custom("Invalid timestamp"))
108 }
109
110 pub fn optional_datetime_from_timestamp<'de, D>(
112 deserializer: D,
113 ) -> std::result::Result<Option<DateTime<Utc>>, D::Error>
114 where
115 D: Deserializer<'de>,
116 {
117 match optional_number_from_string::<u64, D>(deserializer)? {
118 Some(timestamp) => DateTime::from_timestamp(timestamp as i64, 0)
119 .map(Some)
120 .ok_or_else(|| serde::de::Error::custom("Invalid timestamp")),
121 None => Ok(None),
122 }
123 }
124
125 pub fn vec_from_null<'de, D, T>(deserializer: D) -> std::result::Result<Vec<T>, D::Error>
127 where
128 D: Deserializer<'de>,
129 T: serde::Deserialize<'de>,
130 {
131 Ok(Option::<Vec<T>>::deserialize(deserializer)?.unwrap_or_default())
132 }
133
134 pub fn optional_decimal_from_string<'de, D>(
143 deserializer: D,
144 ) -> std::result::Result<Option<Decimal>, D::Error>
145 where
146 D: Deserializer<'de>,
147 {
148 let value = serde_json::Value::deserialize(deserializer)?;
149 match value {
150 serde_json::Value::Null => Ok(None),
151 serde_json::Value::String(s) => {
152 let s = s.trim();
153 if s.is_empty() {
154 Ok(None)
155 } else {
156 s.parse::<Decimal>()
157 .map(Some)
158 .map_err(serde::de::Error::custom)
159 }
160 },
161 serde_json::Value::Number(n) => Decimal::from_str(&n.to_string())
162 .map(Some)
163 .map_err(serde::de::Error::custom),
164 other => Err(serde::de::Error::custom(format!(
165 "Expected decimal as string/number/null, got {other}"
166 ))),
167 }
168 }
169
170 pub fn optional_decimal_from_string_default_on_error<'de, D>(
175 deserializer: D,
176 ) -> std::result::Result<Option<Decimal>, D::Error>
177 where
178 D: Deserializer<'de>,
179 {
180 let value = serde_json::Value::deserialize(deserializer)?;
181 match value {
182 serde_json::Value::Null => Ok(None),
183 serde_json::Value::String(s) => {
184 let s = s.trim();
185 if s.is_empty() {
186 Ok(None)
187 } else {
188 Ok(s.parse::<Decimal>().ok())
189 }
190 },
191 serde_json::Value::Number(n) => Ok(Decimal::from_str(&n.to_string()).ok()),
192 _ => Ok(None),
193 }
194 }
195
196 pub fn decimal_from_string<'de, D>(deserializer: D) -> std::result::Result<Decimal, D::Error>
205 where
206 D: Deserializer<'de>,
207 {
208 optional_decimal_from_string(deserializer)?.ok_or_else(|| {
209 serde::de::Error::custom("Expected decimal as string/number, got null/empty string")
210 })
211 }
212
213 pub fn decimal_from_string_or_zero<'de, D>(
219 deserializer: D,
220 ) -> std::result::Result<Decimal, D::Error>
221 where
222 D: Deserializer<'de>,
223 {
224 Ok(optional_decimal_from_string(deserializer)?.unwrap_or(Decimal::ZERO))
225 }
226}
227
228#[derive(Debug, Deserialize)]
230pub struct RawOrderBookResponse {
231 pub market: String,
232 pub asset_id: String,
233 pub hash: String,
234 #[serde(deserialize_with = "deserializers::number_from_string")]
235 pub timestamp: u64,
236 pub bids: Vec<RawBookLevel>,
237 pub asks: Vec<RawBookLevel>,
238}
239
240#[derive(Debug, Deserialize)]
241pub struct RawBookLevel {
242 #[serde(with = "rust_decimal::serde::str")]
243 pub price: Decimal,
244 #[serde(with = "rust_decimal::serde::str")]
245 pub size: Decimal,
246}
247
248#[derive(Debug, Deserialize)]
249pub struct RawOrderResponse {
250 pub id: String,
251 pub status: String,
252 pub market: String,
253 pub asset_id: String,
254 pub maker_address: String,
255 pub owner: String,
256 pub outcome: String,
257 #[serde(rename = "type")]
258 pub order_type: OrderType,
259 pub side: Side,
260 #[serde(with = "rust_decimal::serde::str")]
261 pub original_size: Decimal,
262 #[serde(with = "rust_decimal::serde::str")]
263 pub price: Decimal,
264 #[serde(with = "rust_decimal::serde::str")]
265 pub size_matched: Decimal,
266 #[serde(deserialize_with = "deserializers::number_from_string")]
267 pub expiration: u64,
268 #[serde(deserialize_with = "deserializers::number_from_string")]
269 pub created_at: u64,
270}
271
272#[derive(Debug, Deserialize)]
273pub struct RawTradeResponse {
274 pub id: String,
275 pub market: String,
276 pub asset_id: String,
277 pub side: Side,
278 #[serde(with = "rust_decimal::serde::str")]
279 pub price: Decimal,
280 #[serde(with = "rust_decimal::serde::str")]
281 pub size: Decimal,
282 pub maker_address: String,
283 pub taker_address: String,
284 #[serde(deserialize_with = "deserializers::number_from_string")]
285 pub timestamp: u64,
286}
287
288#[derive(Debug, Deserialize)]
289pub struct RawMarketResponse {
290 pub condition_id: String,
291 pub tokens: [RawToken; 2],
292 pub active: bool,
293 pub closed: bool,
294 pub question: String,
295 pub description: String,
296 pub category: Option<String>,
297 pub end_date_iso: Option<String>,
298 #[serde(with = "rust_decimal::serde::str")]
299 pub minimum_order_size: Decimal,
300 #[serde(with = "rust_decimal::serde::str")]
301 pub minimum_tick_size: Decimal,
302}
303
304#[derive(Debug, Deserialize)]
305pub struct RawToken {
306 pub token_id: String,
307 pub outcome: String,
308}
309
310pub trait Decoder<T> {
312 fn decode(&self) -> Result<T>;
313}
314
315impl Decoder<OrderBook> for RawOrderBookResponse {
316 fn decode(&self) -> Result<OrderBook> {
317 let timestamp = chrono::DateTime::from_timestamp(self.timestamp as i64, 0)
318 .ok_or_else(|| PolyfillError::parse("Invalid timestamp".to_string(), None))?;
319
320 let bids = self
321 .bids
322 .iter()
323 .map(|level| BookLevel {
324 price: level.price,
325 size: level.size,
326 })
327 .collect();
328
329 let asks = self
330 .asks
331 .iter()
332 .map(|level| BookLevel {
333 price: level.price,
334 size: level.size,
335 })
336 .collect();
337
338 Ok(OrderBook {
339 token_id: self.asset_id.clone(),
340 timestamp,
341 bids,
342 asks,
343 sequence: 0, last_delta_sequence: 0,
345 last_snapshot_timestamp_ms: 0,
346 })
347 }
348}
349
350impl Decoder<Order> for RawOrderResponse {
351 fn decode(&self) -> Result<Order> {
352 let status = match self.status.as_str() {
353 "LIVE" => OrderStatus::Live,
354 "CANCELLED" => OrderStatus::Cancelled,
355 "FILLED" => OrderStatus::Filled,
356 "PARTIAL" => OrderStatus::Partial,
357 "EXPIRED" => OrderStatus::Expired,
358 _ => {
359 return Err(PolyfillError::parse(
360 format!("Unknown order status: {}", self.status),
361 None,
362 ))
363 },
364 };
365
366 let created_at =
367 chrono::DateTime::from_timestamp(self.created_at as i64, 0).ok_or_else(|| {
368 PolyfillError::parse("Invalid created_at timestamp".to_string(), None)
369 })?;
370
371 let expiration = if self.expiration > 0 {
372 Some(
373 chrono::DateTime::from_timestamp(self.expiration as i64, 0).ok_or_else(|| {
374 PolyfillError::parse("Invalid expiration timestamp".to_string(), None)
375 })?,
376 )
377 } else {
378 None
379 };
380
381 Ok(Order {
382 id: self.id.clone(),
383 token_id: self.asset_id.clone(),
384 side: self.side,
385 price: self.price,
386 original_size: self.original_size,
387 filled_size: self.size_matched,
388 remaining_size: self.original_size - self.size_matched,
389 status,
390 order_type: self.order_type,
391 created_at,
392 updated_at: created_at, expiration,
394 client_id: None,
395 })
396 }
397}
398
399impl Decoder<FillEvent> for RawTradeResponse {
400 fn decode(&self) -> Result<FillEvent> {
401 let timestamp = chrono::DateTime::from_timestamp(self.timestamp as i64, 0)
402 .ok_or_else(|| PolyfillError::parse("Invalid trade timestamp".to_string(), None))?;
403
404 let maker_address = Address::from_str(&self.maker_address)
405 .map_err(|e| PolyfillError::parse(format!("Invalid maker address: {}", e), None))?;
406
407 let taker_address = Address::from_str(&self.taker_address)
408 .map_err(|e| PolyfillError::parse(format!("Invalid taker address: {}", e), None))?;
409
410 Ok(FillEvent {
411 id: self.id.clone(),
412 order_id: "".to_string(), token_id: self.asset_id.clone(),
414 side: self.side,
415 price: self.price,
416 size: self.size,
417 timestamp,
418 maker_address,
419 taker_address,
420 fee: Decimal::ZERO, })
422 }
423}
424
425impl Decoder<Market> for RawMarketResponse {
426 fn decode(&self) -> Result<Market> {
427 let tokens = [
428 Token {
429 token_id: self.tokens[0].token_id.clone(),
430 outcome: self.tokens[0].outcome.clone(),
431 price: Decimal::ZERO,
432 winner: false,
433 },
434 Token {
435 token_id: self.tokens[1].token_id.clone(),
436 outcome: self.tokens[1].outcome.clone(),
437 price: Decimal::ZERO,
438 winner: false,
439 },
440 ];
441
442 Ok(Market {
443 condition_id: self.condition_id.clone(),
444 tokens,
445 rewards: crate::types::Rewards {
446 rates: None,
447 min_size: Decimal::ZERO,
448 max_spread: Decimal::ONE,
449 event_start_date: None,
450 event_end_date: None,
451 in_game_multiplier: None,
452 reward_epoch: None,
453 },
454 min_incentive_size: None,
455 max_incentive_spread: None,
456 active: self.active,
457 closed: self.closed,
458 question_id: self.condition_id.clone(), minimum_order_size: self.minimum_order_size,
460 minimum_tick_size: self.minimum_tick_size,
461 description: self.description.clone(),
462 category: self.category.clone(),
463 end_date_iso: self.end_date_iso.clone(),
464 game_start_time: None,
465 question: self.question.clone(),
466 market_slug: format!("market-{}", self.condition_id), seconds_delay: Decimal::ZERO,
468 icon: String::new(),
469 fpmm: String::new(),
470 enable_order_book: false,
472 archived: false,
473 accepting_orders: false,
474 accepting_order_timestamp: None,
475 maker_base_fee: Decimal::ZERO,
476 taker_base_fee: Decimal::ZERO,
477 notifications_enabled: false,
478 neg_risk: false,
479 neg_risk_market_id: String::new(),
480 neg_risk_request_id: String::new(),
481 image: String::new(),
482 is_50_50_outcome: false,
483 })
484 }
485}
486
487pub fn parse_stream_messages(raw: &str) -> Result<Vec<StreamMessage>> {
499 parse_stream_messages_bytes(raw.as_bytes())
500}
501
502pub fn parse_stream_messages_bytes(bytes: &[u8]) -> Result<Vec<StreamMessage>> {
504 let value: Value = serde_json::from_slice(bytes)?;
505
506 match value {
507 Value::Object(map) => {
508 let event_type = map.get("event_type").and_then(Value::as_str);
509 match event_type {
510 None => Ok(vec![]),
511 Some(_) => {
512 let msg: StreamMessage = serde_json::from_value(Value::Object(map))?;
513 match msg {
514 StreamMessage::Unknown => Ok(vec![]),
515 other => Ok(vec![other]),
516 }
517 },
518 }
519 },
520 Value::Array(arr) => Ok(arr
521 .into_iter()
522 .filter_map(|elem| {
523 let Value::Object(map) = elem else {
524 return None;
525 };
526
527 let event_type = map.get("event_type").and_then(Value::as_str)?;
528 match event_type {
530 "book" | "price_change" | "tick_size_change" | "last_trade_price"
531 | "best_bid_ask" | "new_market" | "market_resolved" | "trade" | "order" => {},
532 _ => return None,
533 }
534
535 match serde_json::from_value::<StreamMessage>(Value::Object(map)) {
536 Ok(StreamMessage::Unknown) => None,
537 Ok(msg) => Some(msg),
538 Err(_) => None,
539 }
540 })
541 .collect()),
542 _ => Ok(vec![]),
543 }
544}
545
546pub struct BatchDecoder {
548 buffer: Vec<u8>,
549}
550
551impl BatchDecoder {
552 pub fn new() -> Self {
553 Self {
554 buffer: Vec::with_capacity(8192),
555 }
556 }
557
558 pub fn parse_json_stream<T>(&mut self, data: &[u8]) -> Result<Vec<T>>
560 where
561 T: for<'de> serde::Deserialize<'de>,
562 {
563 self.buffer.extend_from_slice(data);
564 let mut results = Vec::new();
565 let mut start = 0;
566
567 while let Some(end) = self.find_json_boundary(start) {
568 let json_slice = &self.buffer[start..end];
569 if let Ok(obj) = serde_json::from_slice::<T>(json_slice) {
570 results.push(obj);
571 }
572 start = end;
573 }
574
575 if start > 0 {
577 self.buffer.drain(0..start);
578 }
579
580 Ok(results)
581 }
582
583 fn find_json_boundary(&self, start: usize) -> Option<usize> {
585 let mut depth = 0;
586 let mut in_string = false;
587 let mut escaped = false;
588
589 for (i, &byte) in self.buffer[start..].iter().enumerate() {
590 if escaped {
591 escaped = false;
592 continue;
593 }
594
595 match byte {
596 b'\\' if in_string => escaped = true,
597 b'"' => in_string = !in_string,
598 b'{' if !in_string => depth += 1,
599 b'}' if !in_string => {
600 depth -= 1;
601 if depth == 0 {
602 return Some(start + i + 1);
603 }
604 },
605 _ => {},
606 }
607 }
608
609 None
610 }
611}
612
613impl Default for BatchDecoder {
614 fn default() -> Self {
615 Self::new()
616 }
617}
618
619pub mod fast_parse {
621 use super::*;
622
623 #[inline]
625 pub fn parse_decimal(s: &str) -> Result<Decimal> {
626 Decimal::from_str(s)
627 .map_err(|e| PolyfillError::parse(format!("Invalid decimal: {}", e), None))
628 }
629
630 #[inline]
632 pub fn parse_address(s: &str) -> Result<Address> {
633 Address::from_str(s)
634 .map_err(|e| PolyfillError::parse(format!("Invalid address: {}", e), None))
635 }
636
637 #[inline]
641 pub fn parse_json_fast<T>(bytes: &mut [u8]) -> Result<T>
642 where
643 T: for<'de> serde::Deserialize<'de>,
644 {
645 match simd_json::serde::from_slice(bytes) {
647 Ok(val) => Ok(val),
648 Err(_) => {
649 serde_json::from_slice(bytes)
651 .map_err(|e| PolyfillError::parse(format!("JSON parse error: {}", e), None))
652 },
653 }
654 }
655
656 #[inline]
658 pub fn parse_json_fast_owned<T>(bytes: &[u8]) -> Result<T>
659 where
660 T: for<'de> serde::Deserialize<'de>,
661 {
662 let mut data = bytes.to_vec();
664 parse_json_fast(&mut data)
665 }
666
667 #[inline]
669 pub fn parse_u256(s: &str) -> Result<U256> {
670 U256::from_str_radix(s, 10)
671 .map_err(|e| PolyfillError::parse(format!("Invalid U256: {}", e), None))
672 }
673
674 #[inline]
676 pub fn parse_side(s: &str) -> Result<Side> {
677 match s.to_uppercase().as_str() {
678 "BUY" => Ok(Side::BUY),
679 "SELL" => Ok(Side::SELL),
680 _ => Err(PolyfillError::parse(format!("Invalid side: {}", s), None)),
681 }
682 }
683}
684
685#[cfg(test)]
686mod tests {
687 use super::*;
688
689 #[test]
690 fn test_parse_decimal() {
691 let result = fast_parse::parse_decimal("123.456").unwrap();
692 assert_eq!(result, Decimal::from_str("123.456").unwrap());
693 }
694
695 #[test]
696 fn test_parse_side() {
697 assert_eq!(fast_parse::parse_side("BUY").unwrap(), Side::BUY);
698 assert_eq!(fast_parse::parse_side("sell").unwrap(), Side::SELL);
699 assert!(fast_parse::parse_side("invalid").is_err());
700 }
701
702 #[test]
703 fn test_batch_decoder() {
704 let mut decoder = BatchDecoder::new();
705 let data = r#"{"test":1}{"test":2}"#.as_bytes();
706
707 let results: Vec<serde_json::Value> = decoder.parse_json_stream(data).unwrap();
708 assert_eq!(results.len(), 2);
709 }
710
711 #[test]
712 fn stream_book_message_requires_bids_and_asks() {
713 let missing_asks = br#"{"event_type":"book","asset_id":"test_asset_id","market":"0xabc","timestamp":1000,"bids":[]}"#;
714 assert!(parse_stream_messages_bytes(missing_asks).is_err());
715
716 let missing_bids = br#"{"event_type":"book","asset_id":"test_asset_id","market":"0xabc","timestamp":1000,"asks":[]}"#;
717 assert!(parse_stream_messages_bytes(missing_bids).is_err());
718
719 let empty_sides = br#"{"event_type":"book","asset_id":"test_asset_id","market":"0xabc","timestamp":1000,"bids":[],"asks":[]}"#;
720 let messages = parse_stream_messages_bytes(empty_sides).unwrap();
721 assert_eq!(messages.len(), 1);
722 }
723}