1use alloy_primitives::{Address, B256, U256};
7use chrono::{DateTime, Utc};
8use rust_decimal::prelude::ToPrimitive;
9use rust_decimal::Decimal;
10use serde::{Deserialize, Serialize};
11
12pub type Price = u32;
43
44pub type Qty = i64;
56
57pub const SCALE_FACTOR: i64 = 10_000;
63
64pub const MAX_PRICE_TICKS: Price = Price::MAX;
67
68pub const MIN_PRICE_TICKS: Price = 1;
70
71pub const MAX_QTY: Qty = Qty::MAX / 2; pub fn decimal_to_price(decimal: Decimal) -> std::result::Result<Price, &'static str> {
93 let scaled = decimal * Decimal::from(SCALE_FACTOR);
95
96 let rounded = scaled.round();
98
99 let as_u64 = rounded.to_u64().ok_or("Price too large or negative")?;
101
102 if as_u64 < MIN_PRICE_TICKS as u64 {
104 return Ok(MIN_PRICE_TICKS); }
106 if as_u64 > MAX_PRICE_TICKS as u64 {
107 return Err("Price exceeds maximum");
108 }
109
110 Ok(as_u64 as Price)
111}
112
113pub fn price_to_decimal(ticks: Price) -> Decimal {
122 Decimal::from(ticks) / Decimal::from(SCALE_FACTOR)
123}
124
125pub fn decimal_to_qty(decimal: Decimal) -> std::result::Result<Qty, &'static str> {
134 let scaled = decimal * Decimal::from(SCALE_FACTOR);
135 let rounded = scaled.round();
136
137 let as_i64 = rounded.to_i64().ok_or("Quantity too large")?;
138
139 if as_i64.abs() > MAX_QTY {
140 return Err("Quantity exceeds maximum");
141 }
142
143 Ok(as_i64)
144}
145
146pub fn qty_to_decimal(units: Qty) -> Decimal {
152 Decimal::from(units) / Decimal::from(SCALE_FACTOR)
153}
154
155pub fn is_price_tick_aligned(decimal: Decimal, tick_size_decimal: Decimal) -> bool {
165 let tick_size_ticks = match decimal_to_price(tick_size_decimal) {
167 Ok(ticks) => ticks,
168 Err(_) => return false,
169 };
170
171 let price_ticks = match decimal_to_price(decimal) {
173 Ok(ticks) => ticks,
174 Err(_) => return false,
175 };
176
177 if tick_size_ticks == 0 {
180 return true;
181 }
182
183 price_ticks % tick_size_ticks == 0
184}
185
186#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
188#[allow(clippy::upper_case_acronyms)]
189pub enum Side {
190 BUY = 0,
191 SELL = 1,
192}
193
194#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
195#[serde(rename_all = "UPPERCASE")]
196pub enum TraderSide {
197 Taker,
198 Maker,
199 #[serde(untagged)]
200 Unknown(String),
201}
202
203impl Default for TraderSide {
204 fn default() -> Self {
205 Self::Unknown("UNKNOWN".to_string())
206 }
207}
208
209#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
211pub enum TradeMessageStatus {
212 #[serde(alias = "matched", alias = "MATCHED")]
213 Matched,
214 #[serde(alias = "mined", alias = "MINED")]
215 Mined,
216 #[serde(alias = "confirmed", alias = "CONFIRMED")]
217 Confirmed,
218 #[serde(alias = "retrying", alias = "RETRYING")]
219 Retrying,
220 #[serde(alias = "failed", alias = "FAILED")]
221 Failed,
222 #[serde(untagged)]
224 Unknown(String),
225}
226
227impl Default for TradeMessageStatus {
228 fn default() -> Self {
229 Self::Unknown("UNKNOWN".to_string())
230 }
231}
232
233#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
235pub enum TradeMessageType {
236 #[serde(alias = "trade", alias = "TRADE")]
237 Trade,
238 #[serde(untagged)]
240 Unknown(String),
241}
242
243impl Side {
244 pub fn as_str(&self) -> &'static str {
245 match self {
246 Side::BUY => "BUY",
247 Side::SELL => "SELL",
248 }
249 }
250
251 pub fn opposite(&self) -> Self {
252 match self {
253 Side::BUY => Side::SELL,
254 Side::SELL => Side::BUY,
255 }
256 }
257}
258
259#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
261#[allow(clippy::upper_case_acronyms)]
262pub enum OrderType {
263 #[default]
264 GTC,
265 FOK,
266 FAK,
267 GTD,
268}
269
270impl OrderType {
271 pub fn as_str(&self) -> &'static str {
272 match self {
273 OrderType::GTC => "GTC",
274 OrderType::FOK => "FOK",
275 OrderType::FAK => "FAK",
276 OrderType::GTD => "GTD",
277 }
278 }
279}
280
281#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
283pub enum OrderStatus {
284 #[serde(rename = "LIVE")]
285 Live,
286 #[serde(rename = "CANCELLED")]
287 Cancelled,
288 #[serde(rename = "FILLED")]
289 Filled,
290 #[serde(rename = "PARTIAL")]
291 Partial,
292 #[serde(rename = "EXPIRED")]
293 Expired,
294}
295
296#[derive(Debug, Clone, Serialize, Deserialize)]
298pub struct MarketSnapshot {
299 pub token_id: String,
300 pub market_id: String,
301 pub timestamp: DateTime<Utc>,
302 pub bid: Option<Decimal>,
303 pub ask: Option<Decimal>,
304 pub mid: Option<Decimal>,
305 pub spread: Option<Decimal>,
306 pub last_price: Option<Decimal>,
307 pub volume_24h: Option<Decimal>,
308}
309
310#[derive(Debug, Clone, Serialize, Deserialize)]
315pub struct BookLevel {
316 #[serde(with = "rust_decimal::serde::str")]
317 pub price: Decimal,
318 #[serde(with = "rust_decimal::serde::str")]
319 pub size: Decimal,
320}
321
322#[derive(Debug, Clone, Copy, PartialEq, Eq)]
333pub struct FastBookLevel {
334 pub price: Price, pub size: Qty, }
337
338impl FastBookLevel {
339 pub fn new(price: Price, size: Qty) -> Self {
341 Self { price, size }
342 }
343
344 pub fn to_book_level(self) -> BookLevel {
347 BookLevel {
348 price: price_to_decimal(self.price),
349 size: qty_to_decimal(self.size),
350 }
351 }
352
353 pub fn from_book_level(level: &BookLevel) -> std::result::Result<Self, &'static str> {
356 let price = decimal_to_price(level.price)?;
357 let size = decimal_to_qty(level.size)?;
358 Ok(Self::new(price, size))
359 }
360
361 pub fn notional(self) -> i64 {
368 let price_i64 = self.price as i64;
370 (price_i64 * self.size) / SCALE_FACTOR
372 }
373}
374
375#[derive(Debug, Clone, Serialize, Deserialize)]
377pub struct OrderBook {
378 pub token_id: String,
380 pub timestamp: DateTime<Utc>,
382 pub bids: Vec<BookLevel>,
384 pub asks: Vec<BookLevel>,
386 pub sequence: u64,
388}
389
390#[derive(Debug, Clone, Serialize, Deserialize)]
395pub struct OrderDelta {
396 pub token_id: String,
397 pub timestamp: DateTime<Utc>,
398 pub side: Side,
399 pub price: Decimal,
400 pub size: Decimal, pub sequence: u64,
402}
403
404#[derive(Debug, Clone, Copy, PartialEq, Eq)]
415pub struct FastOrderDelta {
416 pub token_id_hash: u64, pub timestamp: DateTime<Utc>,
418 pub side: Side,
419 pub price: Price, pub size: Qty, pub sequence: u64,
422}
423
424impl FastOrderDelta {
425 pub fn from_order_delta(
431 delta: &OrderDelta,
432 tick_size: Option<Decimal>,
433 ) -> std::result::Result<Self, &'static str> {
434 if let Some(tick_size) = tick_size {
436 if !is_price_tick_aligned(delta.price, tick_size) {
437 return Err("Price not aligned to tick size");
438 }
439 }
440
441 let price = decimal_to_price(delta.price)?;
443 let size = decimal_to_qty(delta.size)?;
444
445 let token_id_hash = {
448 use std::collections::hash_map::DefaultHasher;
449 use std::hash::{Hash, Hasher};
450 let mut hasher = DefaultHasher::new();
451 delta.token_id.hash(&mut hasher);
452 hasher.finish()
453 };
454
455 Ok(Self {
456 token_id_hash,
457 timestamp: delta.timestamp,
458 side: delta.side,
459 price,
460 size,
461 sequence: delta.sequence,
462 })
463 }
464
465 pub fn to_order_delta(self, token_id: String) -> OrderDelta {
468 OrderDelta {
469 token_id,
470 timestamp: self.timestamp,
471 side: self.side,
472 price: price_to_decimal(self.price),
473 size: qty_to_decimal(self.size),
474 sequence: self.sequence,
475 }
476 }
477
478 pub fn is_removal(self) -> bool {
480 self.size == 0
481 }
482}
483
484#[derive(Debug, Clone, Serialize, Deserialize)]
486pub struct FillEvent {
487 pub id: String,
488 pub order_id: String,
489 pub token_id: String,
490 pub side: Side,
491 pub price: Decimal,
492 pub size: Decimal,
493 pub timestamp: DateTime<Utc>,
494 pub maker_address: Address,
495 pub taker_address: Address,
496 pub fee: Decimal,
497}
498
499#[derive(Debug, Clone)]
501pub struct OrderRequest {
502 pub token_id: String,
503 pub side: Side,
504 pub price: Decimal,
505 pub size: Decimal,
506 pub order_type: OrderType,
507 pub expiration: Option<DateTime<Utc>>,
508 pub client_id: Option<String>,
509}
510
511#[derive(Debug, Clone)]
513pub struct MarketOrderRequest {
514 pub token_id: String,
515 pub side: Side,
516 pub amount: Decimal, pub slippage_tolerance: Option<Decimal>,
518 pub client_id: Option<String>,
519}
520
521#[derive(Debug, Clone, Serialize, Deserialize)]
523pub struct Order {
524 pub id: String,
525 pub token_id: String,
526 pub side: Side,
527 pub price: Decimal,
528 pub original_size: Decimal,
529 pub filled_size: Decimal,
530 pub remaining_size: Decimal,
531 pub status: OrderStatus,
532 pub order_type: OrderType,
533 pub created_at: DateTime<Utc>,
534 pub updated_at: DateTime<Utc>,
535 pub expiration: Option<DateTime<Utc>>,
536 pub client_id: Option<String>,
537}
538
539#[derive(Debug, Clone, Serialize, Deserialize, Default)]
541pub struct ApiCredentials {
542 #[serde(rename = "apiKey")]
543 pub api_key: String,
544 pub secret: String,
545 pub passphrase: String,
546}
547
548#[derive(Debug, Clone)]
550pub struct OrderOptions {
551 pub tick_size: Option<Decimal>,
552 pub neg_risk: Option<bool>,
553 pub fee_rate_bps: Option<u32>,
554}
555
556#[derive(Debug, Clone)]
558pub struct ExtraOrderArgs {
559 pub metadata: B256,
561 pub builder: B256,
563}
564
565impl Default for ExtraOrderArgs {
566 fn default() -> Self {
567 Self {
568 metadata: B256::ZERO,
569 builder: B256::ZERO,
570 }
571 }
572}
573
574#[derive(Debug, Clone)]
576pub struct ExtraOrderArgsV1 {
577 pub fee_rate_bps: u32,
578 pub nonce: U256,
579 pub taker: String,
580}
581
582impl Default for ExtraOrderArgsV1 {
583 fn default() -> Self {
584 Self {
585 fee_rate_bps: 0,
586 nonce: U256::ZERO,
587 taker: "0x0000000000000000000000000000000000000000".to_string(),
588 }
589 }
590}
591
592#[derive(Debug, Clone)]
594pub struct MarketOrderArgs {
595 pub token_id: String,
596 pub side: Side,
597 pub amount: Decimal,
599}
600
601#[derive(Debug, Clone, Serialize, Deserialize)]
603#[serde(rename_all = "camelCase")]
604pub struct SignedOrderRequest {
605 pub salt: u64,
606 pub maker: String,
607 pub signer: String,
608 pub taker: String,
610 pub token_id: String,
611 pub maker_amount: String,
612 pub taker_amount: String,
613 pub side: String,
614 pub signature_type: u8,
615 pub timestamp: String,
617 pub expiration: String,
619 pub metadata: String,
621 pub builder: String,
623 pub signature: String,
624}
625
626#[derive(Debug, Serialize)]
628#[serde(rename_all = "camelCase")]
629pub struct PostOrder {
630 pub order: SignedOrderRequest,
631 pub owner: String,
632 pub order_type: OrderType,
633 pub defer_exec: bool,
635 pub post_only: bool,
637}
638
639impl PostOrder {
640 pub fn new(order: SignedOrderRequest, owner: String, order_type: OrderType) -> Self {
641 Self {
642 order,
643 owner,
644 order_type,
645 defer_exec: false,
646 post_only: false,
647 }
648 }
649
650 #[must_use]
651 pub fn with_flags(mut self, post_only: bool, defer_exec: bool) -> Self {
652 self.post_only = post_only;
653 self.defer_exec = defer_exec;
654 self
655 }
656}
657
658#[derive(Debug, Clone, Serialize, Deserialize)]
660pub struct Market {
661 pub condition_id: String,
662 pub tokens: [Token; 2],
663 pub rewards: Rewards,
664 pub min_incentive_size: Option<String>,
665 pub max_incentive_spread: Option<String>,
666 pub active: bool,
667 pub closed: bool,
668 pub question_id: String,
669 pub minimum_order_size: Decimal,
670 pub minimum_tick_size: Decimal,
671 pub description: String,
672 pub category: Option<String>,
673 pub end_date_iso: Option<String>,
674 pub game_start_time: Option<String>,
675 pub question: String,
676 pub market_slug: String,
677 pub seconds_delay: Decimal,
678 pub icon: String,
679 pub fpmm: String,
680 #[serde(default)]
682 pub enable_order_book: bool,
683 #[serde(default)]
684 pub archived: bool,
685 #[serde(default)]
686 pub accepting_orders: bool,
687 #[serde(default)]
688 pub accepting_order_timestamp: Option<String>,
689 #[serde(default)]
690 pub maker_base_fee: Decimal,
691 #[serde(default)]
692 pub taker_base_fee: Decimal,
693 #[serde(default)]
694 pub notifications_enabled: bool,
695 #[serde(default)]
696 pub neg_risk: bool,
697 #[serde(default)]
698 pub neg_risk_market_id: String,
699 #[serde(default)]
700 pub neg_risk_request_id: String,
701 #[serde(default)]
702 pub image: String,
703 #[serde(default)]
704 pub is_50_50_outcome: bool,
705}
706
707#[derive(Debug, Clone, Serialize, Deserialize)]
709pub struct Token {
710 pub token_id: String,
711 pub outcome: String,
712 pub price: Decimal,
713 #[serde(default)]
714 pub winner: bool,
715}
716
717#[derive(Debug, Clone, Serialize, Deserialize)]
719pub struct ClientConfig {
720 pub base_url: String,
722 pub chain_id: u64,
724 pub private_key: Option<String>,
726 pub api_credentials: Option<ApiCredentials>,
728 pub max_slippage: Option<Decimal>,
730 pub fee_rate: Option<Decimal>,
732 pub timeout: Option<std::time::Duration>,
734 pub max_connections: Option<usize>,
736}
737
738impl Default for ClientConfig {
739 fn default() -> Self {
740 Self {
741 base_url: "https://clob.polymarket.com".to_string(),
742 chain_id: 137, private_key: None,
744 api_credentials: None,
745 timeout: Some(std::time::Duration::from_secs(30)),
746 max_connections: Some(100),
747 max_slippage: None,
748 fee_rate: None,
749 }
750 }
751}
752
753pub type WssAuth = ApiCredentials;
758
759#[derive(Debug, Clone, Serialize, Deserialize)]
761pub struct WssSubscription {
762 #[serde(rename = "type")]
764 pub channel_type: String,
765 #[serde(skip_serializing_if = "Option::is_none")]
767 pub operation: Option<String>,
768 #[serde(default)]
770 pub markets: Vec<String>,
771 #[serde(rename = "assets_ids", default)]
774 pub asset_ids: Vec<String>,
775 #[serde(skip_serializing_if = "Option::is_none")]
777 pub initial_dump: Option<bool>,
778 #[serde(skip_serializing_if = "Option::is_none")]
780 pub custom_feature_enabled: Option<bool>,
781 #[serde(skip_serializing_if = "Option::is_none")]
783 pub auth: Option<WssAuth>,
784}
785
786#[derive(Debug, Clone, Serialize, Deserialize)]
788#[serde(tag = "event_type")]
789pub enum StreamMessage {
790 #[serde(rename = "book")]
792 Book(BookUpdate),
793 #[serde(rename = "price_change")]
795 PriceChange(PriceChange),
796 #[serde(rename = "tick_size_change")]
798 TickSizeChange(TickSizeChange),
799 #[serde(rename = "last_trade_price")]
801 LastTradePrice(LastTradePrice),
802 #[serde(rename = "best_bid_ask")]
804 BestBidAsk(BestBidAsk),
805 #[serde(rename = "new_market")]
807 NewMarket(NewMarket),
808 #[serde(rename = "market_resolved")]
810 MarketResolved(MarketResolved),
811 #[serde(rename = "trade")]
813 Trade(TradeMessage),
814 #[serde(rename = "order")]
816 Order(OrderMessage),
817 #[serde(other)]
819 Unknown,
820}
821
822#[derive(Debug, Clone, Serialize, Deserialize)]
824pub struct BookUpdate {
825 pub asset_id: String,
826 pub market: String,
827 #[serde(deserialize_with = "crate::decode::deserializers::number_from_string")]
828 pub timestamp: u64,
829 #[serde(
830 default,
831 deserialize_with = "crate::decode::deserializers::vec_from_null"
832 )]
833 pub bids: Vec<OrderSummary>,
834 #[serde(
835 default,
836 deserialize_with = "crate::decode::deserializers::vec_from_null"
837 )]
838 pub asks: Vec<OrderSummary>,
839 #[serde(default)]
840 pub hash: Option<String>,
841}
842
843#[derive(Debug, Clone, Serialize, Deserialize)]
845pub struct PriceChange {
846 pub market: String,
847 #[serde(deserialize_with = "crate::decode::deserializers::number_from_string")]
848 pub timestamp: u64,
849 #[serde(
850 default,
851 deserialize_with = "crate::decode::deserializers::vec_from_null"
852 )]
853 pub price_changes: Vec<PriceChangeEntry>,
854}
855
856#[derive(Debug, Clone, Serialize, Deserialize)]
857pub struct PriceChangeEntry {
858 pub asset_id: String,
859 pub price: Decimal,
860 #[serde(
861 default,
862 deserialize_with = "crate::decode::deserializers::optional_decimal_from_string"
863 )]
864 pub size: Option<Decimal>,
865 pub side: Side,
866 #[serde(default)]
867 pub hash: Option<String>,
868 #[serde(
869 default,
870 deserialize_with = "crate::decode::deserializers::optional_decimal_from_string"
871 )]
872 pub best_bid: Option<Decimal>,
873 #[serde(
874 default,
875 deserialize_with = "crate::decode::deserializers::optional_decimal_from_string"
876 )]
877 pub best_ask: Option<Decimal>,
878}
879
880#[derive(Debug, Clone, Serialize, Deserialize)]
882pub struct TickSizeChange {
883 pub asset_id: String,
884 pub market: String,
885 pub old_tick_size: Decimal,
886 pub new_tick_size: Decimal,
887 #[serde(deserialize_with = "crate::decode::deserializers::number_from_string")]
888 pub timestamp: u64,
889}
890
891#[derive(Debug, Clone, Serialize, Deserialize)]
893pub struct LastTradePrice {
894 pub asset_id: String,
895 pub market: String,
896 pub price: Decimal,
897 #[serde(default)]
898 pub side: Option<Side>,
899 #[serde(
900 default,
901 deserialize_with = "crate::decode::deserializers::optional_decimal_from_string"
902 )]
903 pub size: Option<Decimal>,
904 #[serde(
905 default,
906 deserialize_with = "crate::decode::deserializers::optional_decimal_from_string"
907 )]
908 pub fee_rate_bps: Option<Decimal>,
909 #[serde(deserialize_with = "crate::decode::deserializers::number_from_string")]
910 pub timestamp: u64,
911}
912
913#[derive(Debug, Clone, Serialize, Deserialize)]
915pub struct BestBidAsk {
916 pub market: String,
917 pub asset_id: String,
918 pub best_bid: Decimal,
919 pub best_ask: Decimal,
920 pub spread: Decimal,
921 #[serde(deserialize_with = "crate::decode::deserializers::number_from_string")]
922 pub timestamp: u64,
923}
924
925#[derive(Debug, Clone, Serialize, Deserialize)]
927pub struct NewMarket {
928 pub id: String,
929 pub question: String,
930 pub market: String,
931 pub slug: String,
932 pub description: String,
933 #[serde(rename = "assets_ids", alias = "asset_ids")]
934 pub asset_ids: Vec<String>,
935 #[serde(
936 default,
937 deserialize_with = "crate::decode::deserializers::vec_from_null"
938 )]
939 pub outcomes: Vec<String>,
940 #[serde(default)]
941 pub event_message: Option<EventMessage>,
942 #[serde(deserialize_with = "crate::decode::deserializers::number_from_string")]
943 pub timestamp: u64,
944}
945
946#[derive(Debug, Clone, Serialize, Deserialize)]
948pub struct MarketResolved {
949 pub id: String,
950 #[serde(default)]
951 pub question: Option<String>,
952 pub market: String,
953 #[serde(default)]
954 pub slug: Option<String>,
955 #[serde(default)]
956 pub description: Option<String>,
957 #[serde(rename = "assets_ids", alias = "asset_ids")]
958 pub asset_ids: Vec<String>,
959 #[serde(
960 default,
961 deserialize_with = "crate::decode::deserializers::vec_from_null"
962 )]
963 pub outcomes: Vec<String>,
964 pub winning_asset_id: String,
965 pub winning_outcome: String,
966 #[serde(default)]
967 pub event_message: Option<EventMessage>,
968 #[serde(deserialize_with = "crate::decode::deserializers::number_from_string")]
969 pub timestamp: u64,
970}
971
972#[derive(Debug, Clone, Serialize, Deserialize)]
974pub struct EventMessage {
975 pub id: String,
976 pub ticker: String,
977 pub slug: String,
978 pub title: String,
979 pub description: String,
980}
981
982#[derive(Debug, Clone, Serialize, Deserialize)]
984pub struct TradeMessage {
985 pub id: String,
986 pub market: String,
987 pub asset_id: String,
988 pub side: Side,
989 pub size: Decimal,
990 pub price: Decimal,
991 #[serde(default)]
993 pub status: TradeMessageStatus,
994 #[serde(rename = "type", default)]
995 pub msg_type: Option<TradeMessageType>,
996 #[serde(
997 default,
998 deserialize_with = "crate::decode::deserializers::optional_number_from_string"
999 )]
1000 pub last_update: Option<u64>,
1001 #[serde(
1002 default,
1003 alias = "match_time",
1004 deserialize_with = "crate::decode::deserializers::optional_number_from_string"
1005 )]
1006 pub matchtime: Option<u64>,
1007 #[serde(
1008 default,
1009 deserialize_with = "crate::decode::deserializers::optional_number_from_string"
1010 )]
1011 pub timestamp: Option<u64>,
1012 #[serde(default)]
1014 pub outcome: Option<String>,
1015 #[serde(default)]
1017 pub owner: Option<String>,
1018 #[serde(default)]
1020 pub trade_owner: Option<String>,
1021 #[serde(default)]
1023 pub taker_order_id: Option<String>,
1024 #[serde(
1026 default,
1027 deserialize_with = "crate::decode::deserializers::vec_from_null"
1028 )]
1029 pub maker_orders: Vec<MakerOrder>,
1030 #[serde(
1032 default,
1033 deserialize_with = "crate::decode::deserializers::optional_decimal_from_string"
1034 )]
1035 pub fee_rate_bps: Option<Decimal>,
1036 #[serde(default)]
1038 pub transaction_hash: Option<String>,
1039 #[serde(default)]
1041 pub trader_side: Option<TraderSide>,
1042 #[serde(default)]
1044 pub bucket_index: Option<u32>,
1045}
1046
1047#[derive(Debug, Clone, Serialize, Deserialize)]
1049pub struct OrderMessage {
1050 pub id: String,
1051 pub market: String,
1052 pub asset_id: String,
1053 pub side: Side,
1054 pub price: Decimal,
1055 #[serde(rename = "type", default)]
1057 pub msg_type: Option<String>,
1058 #[serde(
1059 default,
1060 deserialize_with = "crate::decode::deserializers::optional_decimal_from_string"
1061 )]
1062 pub original_size: Option<Decimal>,
1063 #[serde(
1064 default,
1065 deserialize_with = "crate::decode::deserializers::optional_decimal_from_string"
1066 )]
1067 pub size_matched: Option<Decimal>,
1068 #[serde(
1069 default,
1070 deserialize_with = "crate::decode::deserializers::optional_number_from_string"
1071 )]
1072 pub timestamp: Option<u64>,
1073 #[serde(default)]
1074 pub associate_trades: Option<Vec<String>>,
1075 #[serde(default)]
1076 pub status: Option<String>,
1077
1078 #[serde(default)]
1081 pub owner: Option<String>,
1082 #[serde(default)]
1084 pub order_owner: Option<String>,
1085 #[serde(default)]
1087 pub outcome: Option<String>,
1088 #[serde(default)]
1090 pub created_at: Option<String>,
1091 #[serde(default)]
1093 pub expiration: Option<String>,
1094 #[serde(default)]
1096 pub order_type: Option<String>,
1097 #[serde(default)]
1099 pub maker_address: Option<String>,
1100}
1101
1102#[derive(Debug, Clone, Serialize, Deserialize)]
1104pub struct Subscription {
1105 pub token_ids: Vec<String>,
1106 pub channels: Vec<String>,
1107}
1108
1109#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
1111pub enum WssChannelType {
1112 #[serde(rename = "USER")]
1113 User,
1114 #[serde(rename = "MARKET")]
1115 Market,
1116}
1117
1118impl WssChannelType {
1119 pub fn as_str(&self) -> &'static str {
1120 match self {
1121 WssChannelType::User => "USER",
1122 WssChannelType::Market => "MARKET",
1123 }
1124 }
1125}
1126
1127#[derive(Debug, Clone, Serialize, Deserialize)]
1129pub struct Quote {
1130 pub token_id: String,
1131 pub side: Side,
1132 #[serde(with = "rust_decimal::serde::str")]
1133 pub price: Decimal,
1134 pub timestamp: DateTime<Utc>,
1135}
1136
1137#[derive(Debug, Clone, Serialize, Deserialize)]
1139pub struct Balance {
1140 pub token_id: String,
1141 pub available: Decimal,
1142 pub locked: Decimal,
1143 pub total: Decimal,
1144}
1145
1146#[derive(Debug, Clone)]
1148pub struct Metrics {
1149 pub orders_per_second: f64,
1150 pub avg_latency_ms: f64,
1151 pub error_rate: f64,
1152 pub uptime_pct: f64,
1153}
1154
1155pub type TokenId = String;
1157pub type OrderId = String;
1158pub type MarketId = String;
1159pub type ClientId = String;
1160
1161#[derive(Debug, Clone)]
1163pub struct OpenOrderParams {
1164 pub id: Option<String>,
1165 pub asset_id: Option<String>,
1166 pub market: Option<String>,
1167}
1168
1169impl OpenOrderParams {
1170 pub fn to_query_params(&self) -> Vec<(&str, &String)> {
1171 let mut params = Vec::with_capacity(3);
1172
1173 if let Some(x) = &self.id {
1174 params.push(("id", x));
1175 }
1176
1177 if let Some(x) = &self.asset_id {
1178 params.push(("asset_id", x));
1179 }
1180
1181 if let Some(x) = &self.market {
1182 params.push(("market", x));
1183 }
1184 params
1185 }
1186}
1187
1188#[derive(Debug, Clone)]
1190pub struct TradeParams {
1191 pub id: Option<String>,
1192 pub maker_address: Option<String>,
1193 pub market: Option<String>,
1194 pub asset_id: Option<String>,
1195 pub before: Option<u64>,
1196 pub after: Option<u64>,
1197}
1198
1199impl TradeParams {
1200 pub fn to_query_params(&self) -> Vec<(&str, String)> {
1201 let mut params = Vec::with_capacity(6);
1202
1203 if let Some(x) = &self.id {
1204 params.push(("id", x.clone()));
1205 }
1206
1207 if let Some(x) = &self.asset_id {
1208 params.push(("asset_id", x.clone()));
1209 }
1210
1211 if let Some(x) = &self.market {
1212 params.push(("market", x.clone()));
1213 }
1214
1215 if let Some(x) = &self.maker_address {
1216 params.push(("maker_address", x.clone()));
1217 }
1218
1219 if let Some(x) = &self.before {
1220 params.push(("before", x.to_string()));
1221 }
1222
1223 if let Some(x) = &self.after {
1224 params.push(("after", x.to_string()));
1225 }
1226
1227 params
1228 }
1229}
1230
1231#[derive(Debug, Clone, Serialize, Deserialize)]
1233pub struct OpenOrder {
1234 pub associate_trades: Vec<String>,
1235 pub id: String,
1236 pub status: String,
1237 pub market: String,
1238 #[serde(with = "rust_decimal::serde::str")]
1239 pub original_size: Decimal,
1240 pub outcome: String,
1241 pub maker_address: String,
1242 pub owner: String,
1243 #[serde(with = "rust_decimal::serde::str")]
1244 pub price: Decimal,
1245 pub side: Side,
1246 #[serde(with = "rust_decimal::serde::str")]
1247 pub size_matched: Decimal,
1248 pub asset_id: String,
1249 #[serde(deserialize_with = "crate::decode::deserializers::number_from_string")]
1250 pub expiration: u64,
1251 #[serde(rename = "type", alias = "order_type", alias = "orderType", default)]
1252 pub order_type: OrderType,
1253 #[serde(deserialize_with = "crate::decode::deserializers::number_from_string")]
1254 pub created_at: u64,
1255}
1256
1257#[derive(Debug, Clone, Serialize, Deserialize)]
1259#[serde(rename_all = "camelCase")]
1260pub struct PostOrderResponse {
1261 #[serde(default)]
1262 pub error_msg: Option<String>,
1263 #[serde(
1264 default,
1265 deserialize_with = "crate::decode::deserializers::optional_decimal_from_string_default_on_error"
1266 )]
1267 pub making_amount: Option<Decimal>,
1268 #[serde(
1269 default,
1270 deserialize_with = "crate::decode::deserializers::optional_decimal_from_string_default_on_error"
1271 )]
1272 pub taking_amount: Option<Decimal>,
1273 #[serde(rename = "orderID")]
1274 pub order_id: String,
1275 #[serde(default)]
1276 pub status: Option<String>,
1277 pub success: bool,
1278 #[serde(default, alias = "transactionsHashes")]
1279 pub transaction_hashes: Vec<String>,
1280 #[serde(default)]
1281 pub trade_ids: Vec<String>,
1282}
1283
1284#[derive(Debug, Clone, Serialize, Deserialize)]
1286#[serde(rename_all = "camelCase")]
1287pub struct CancelOrdersResponse {
1288 #[serde(
1289 default,
1290 deserialize_with = "crate::decode::deserializers::vec_from_null"
1291 )]
1292 pub canceled: Vec<String>,
1293 #[serde(default, alias = "not_canceled")]
1294 pub not_canceled: std::collections::HashMap<String, String>,
1295}
1296
1297#[derive(Debug, Clone, Serialize, Deserialize)]
1299#[serde(rename_all = "snake_case")]
1300pub struct TradeResponse {
1301 pub id: String,
1302 #[serde(default)]
1303 pub taker_order_id: Option<String>,
1304 pub market: String,
1305 pub asset_id: String,
1306 pub side: Side,
1307 #[serde(deserialize_with = "crate::decode::deserializers::decimal_from_string")]
1308 pub size: Decimal,
1309 #[serde(
1310 default,
1311 deserialize_with = "crate::decode::deserializers::optional_decimal_from_string"
1312 )]
1313 pub fee_rate_bps: Option<Decimal>,
1314 #[serde(deserialize_with = "crate::decode::deserializers::decimal_from_string")]
1315 pub price: Decimal,
1316 #[serde(default)]
1317 pub status: Option<String>,
1318 #[serde(
1319 default,
1320 deserialize_with = "crate::decode::deserializers::optional_number_from_string"
1321 )]
1322 pub match_time: Option<u64>,
1323 #[serde(
1324 default,
1325 deserialize_with = "crate::decode::deserializers::optional_number_from_string"
1326 )]
1327 pub last_update: Option<u64>,
1328 #[serde(default)]
1329 pub outcome: Option<String>,
1330 #[serde(
1331 default,
1332 deserialize_with = "crate::decode::deserializers::optional_number_from_string"
1333 )]
1334 pub bucket_index: Option<u64>,
1335 #[serde(default)]
1336 pub owner: Option<String>,
1337 #[serde(default)]
1338 pub maker_address: Option<String>,
1339 #[serde(
1340 default,
1341 deserialize_with = "crate::decode::deserializers::vec_from_null"
1342 )]
1343 pub maker_orders: Vec<MakerOrder>,
1344 #[serde(default)]
1345 pub transaction_hash: Option<String>,
1346 #[serde(default)]
1347 pub trader_side: TraderSide,
1348 #[serde(default, alias = "err_msg")]
1349 pub error_msg: Option<String>,
1350}
1351
1352#[derive(Debug, Clone, Serialize, Deserialize)]
1353pub struct MakerOrder {
1354 pub order_id: String,
1355 #[serde(default)]
1356 pub owner: Option<String>,
1357 #[serde(default)]
1358 pub maker_address: Option<String>,
1359 #[serde(
1360 default,
1361 deserialize_with = "crate::decode::deserializers::optional_decimal_from_string"
1362 )]
1363 pub matched_amount: Option<Decimal>,
1364 #[serde(
1365 default,
1366 deserialize_with = "crate::decode::deserializers::optional_decimal_from_string"
1367 )]
1368 pub price: Option<Decimal>,
1369 #[serde(
1370 default,
1371 deserialize_with = "crate::decode::deserializers::optional_decimal_from_string"
1372 )]
1373 pub fee_rate_bps: Option<Decimal>,
1374 #[serde(default)]
1375 pub asset_id: Option<String>,
1376 #[serde(default)]
1377 pub outcome: Option<String>,
1378 #[serde(default)]
1379 pub side: Option<Side>,
1380}
1381
1382#[derive(Debug, Clone, Serialize, Deserialize)]
1384pub struct BalanceAllowance {
1385 pub asset_id: String,
1386 #[serde(with = "rust_decimal::serde::str")]
1387 pub balance: Decimal,
1388 #[serde(with = "rust_decimal::serde::str")]
1389 pub allowance: Decimal,
1390}
1391
1392#[derive(Default)]
1394pub struct BalanceAllowanceParams {
1395 pub asset_type: Option<AssetType>,
1396 pub token_id: Option<String>,
1397 pub signature_type: Option<u8>,
1398}
1399
1400impl BalanceAllowanceParams {
1401 pub fn to_query_params(&self) -> Vec<(&str, String)> {
1402 let mut params = Vec::with_capacity(3);
1403
1404 if let Some(x) = &self.asset_type {
1405 params.push(("asset_type", x.to_string()));
1406 }
1407
1408 if let Some(x) = &self.token_id {
1409 params.push(("token_id", x.to_string()));
1410 }
1411
1412 if let Some(x) = &self.signature_type {
1413 params.push(("signature_type", x.to_string()));
1414 }
1415 params
1416 }
1417
1418 pub fn set_signature_type(&mut self, s: u8) {
1419 self.signature_type = Some(s);
1420 }
1421}
1422
1423#[allow(clippy::upper_case_acronyms)]
1425pub enum AssetType {
1426 COLLATERAL,
1427 CONDITIONAL,
1428}
1429
1430impl std::fmt::Display for AssetType {
1431 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1432 match self {
1433 AssetType::COLLATERAL => write!(f, "COLLATERAL"),
1434 AssetType::CONDITIONAL => write!(f, "CONDITIONAL"),
1435 }
1436 }
1437}
1438
1439#[derive(Debug, Clone, Serialize, Deserialize)]
1441pub struct NotificationParams {
1442 pub signature: String,
1443 pub timestamp: u64,
1444}
1445
1446#[derive(Debug, Clone, Serialize, Deserialize)]
1448pub struct BatchMidpointRequest {
1449 pub token_ids: Vec<String>,
1450}
1451
1452#[derive(Debug, Clone, Serialize, Deserialize)]
1454pub struct BatchMidpointResponse {
1455 pub midpoints: std::collections::HashMap<String, Option<Decimal>>,
1456}
1457
1458#[derive(Debug, Clone, Serialize, Deserialize)]
1460pub struct BatchPriceRequest {
1461 pub token_ids: Vec<String>,
1462}
1463
1464#[derive(Debug, Clone, Serialize, Deserialize)]
1466pub struct TokenPrice {
1467 pub token_id: String,
1468 #[serde(skip_serializing_if = "Option::is_none")]
1469 pub bid: Option<Decimal>,
1470 #[serde(skip_serializing_if = "Option::is_none")]
1471 pub ask: Option<Decimal>,
1472 #[serde(skip_serializing_if = "Option::is_none")]
1473 pub mid: Option<Decimal>,
1474}
1475
1476#[derive(Debug, Clone, Serialize, Deserialize)]
1478pub struct BatchPriceResponse {
1479 pub prices: Vec<TokenPrice>,
1480}
1481
1482#[derive(Debug, Deserialize)]
1484pub struct ApiKeysResponse {
1485 #[serde(rename = "apiKeys")]
1486 pub api_keys: Vec<String>,
1487}
1488
1489#[derive(Debug, Deserialize)]
1490pub struct MidpointResponse {
1491 #[serde(with = "rust_decimal::serde::str")]
1492 pub mid: Decimal,
1493}
1494
1495#[derive(Debug, Deserialize)]
1496pub struct PriceResponse {
1497 #[serde(with = "rust_decimal::serde::str")]
1498 pub price: Decimal,
1499}
1500
1501#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1510pub enum PricesHistoryInterval {
1511 OneMinute,
1512 OneHour,
1513 SixHours,
1514 OneDay,
1515 OneWeek,
1516}
1517
1518impl PricesHistoryInterval {
1519 pub const fn as_str(self) -> &'static str {
1520 match self {
1521 Self::OneMinute => "1m",
1522 Self::OneHour => "1h",
1523 Self::SixHours => "6h",
1524 Self::OneDay => "1d",
1525 Self::OneWeek => "1w",
1526 }
1527 }
1528}
1529
1530#[derive(Debug, Clone, Serialize, Deserialize)]
1534pub struct PriceHistoryPoint {
1535 pub t: i64,
1536 #[serde(deserialize_with = "crate::decode::deserializers::decimal_from_string")]
1537 pub p: Decimal,
1538}
1539
1540#[derive(Debug, Clone, Serialize, Deserialize)]
1542pub struct PricesHistoryResponse {
1543 pub history: Vec<PriceHistoryPoint>,
1544}
1545
1546#[derive(Debug, Deserialize)]
1547pub struct SpreadResponse {
1548 pub spread: Decimal,
1549}
1550
1551#[derive(Debug, Deserialize)]
1552pub struct TickSizeResponse {
1553 pub minimum_tick_size: Decimal,
1554}
1555
1556#[derive(Debug, Deserialize)]
1557pub struct NegRiskResponse {
1558 pub neg_risk: bool,
1559}
1560
1561#[derive(Debug, Clone, Serialize, Deserialize)]
1562pub struct OrderScoringResponse {
1563 pub scoring: bool,
1564}
1565
1566#[derive(Debug, Serialize, Deserialize)]
1567pub struct BookParams {
1568 pub token_id: String,
1569 pub side: Side,
1570}
1571
1572#[derive(Debug, Deserialize)]
1573pub struct OrderBookSummary {
1574 pub market: String,
1575 pub asset_id: String,
1576 #[serde(default)]
1577 pub hash: Option<String>,
1578 #[serde(deserialize_with = "crate::decode::deserializers::number_from_string")]
1579 pub timestamp: u64,
1580 #[serde(
1581 default,
1582 deserialize_with = "crate::decode::deserializers::vec_from_null"
1583 )]
1584 pub bids: Vec<OrderSummary>,
1585 #[serde(
1586 default,
1587 deserialize_with = "crate::decode::deserializers::vec_from_null"
1588 )]
1589 pub asks: Vec<OrderSummary>,
1590 pub min_order_size: Decimal,
1591 pub neg_risk: bool,
1592 pub tick_size: Decimal,
1593 #[serde(
1594 default,
1595 deserialize_with = "crate::decode::deserializers::optional_decimal_from_string_default_on_error"
1596 )]
1597 pub last_trade_price: Option<Decimal>,
1598}
1599
1600#[derive(Debug, Clone, Serialize, Deserialize)]
1601pub struct OrderSummary {
1602 #[serde(with = "rust_decimal::serde::str")]
1603 pub price: Decimal,
1604 #[serde(with = "rust_decimal::serde::str")]
1605 pub size: Decimal,
1606}
1607
1608#[derive(Debug, Serialize, Deserialize)]
1609pub struct MarketsResponse {
1610 pub limit: usize,
1611 pub count: usize,
1612 pub next_cursor: Option<String>,
1613 pub data: Vec<Market>,
1614}
1615
1616#[derive(Debug, Serialize, Deserialize)]
1617pub struct SimplifiedMarketsResponse {
1618 pub limit: usize,
1619 pub count: usize,
1620 pub next_cursor: Option<String>,
1621 pub data: Vec<SimplifiedMarket>,
1622}
1623
1624#[derive(Debug, Serialize, Deserialize)]
1626pub struct SimplifiedMarket {
1627 pub condition_id: String,
1628 pub tokens: [Token; 2],
1629 pub rewards: Rewards,
1630 pub min_incentive_size: Option<String>,
1631 pub max_incentive_spread: Option<String>,
1632 pub active: bool,
1633 pub closed: bool,
1634}
1635
1636#[derive(Debug, Clone, Serialize, Deserialize)]
1638pub struct Rewards {
1639 pub rates: Option<serde_json::Value>,
1640 pub min_size: Decimal,
1642 pub max_spread: Decimal,
1643 #[serde(default)]
1644 pub event_start_date: Option<String>,
1645 #[serde(default)]
1646 pub event_end_date: Option<String>,
1647 #[serde(skip_serializing_if = "Option::is_none", default)]
1648 pub in_game_multiplier: Option<Decimal>,
1649 #[serde(skip_serializing_if = "Option::is_none", default)]
1650 pub reward_epoch: Option<Decimal>,
1651}
1652
1653#[derive(Debug, Clone, Serialize, Deserialize)]
1659pub struct FeeRateResponse {
1660 pub base_fee: u32,
1661}
1662
1663#[derive(Debug, Clone, Serialize)]
1665#[serde(rename_all = "camelCase")]
1666pub struct RfqCreateRequest {
1667 pub asset_in: String,
1668 pub asset_out: String,
1669 pub amount_in: String,
1670 pub amount_out: String,
1671 pub user_type: u8,
1672}
1673
1674#[derive(Debug, Clone, Serialize, Deserialize)]
1675#[serde(rename_all = "camelCase")]
1676pub struct RfqCreateRequestResponse {
1677 pub request_id: String,
1678 pub expiry: u64,
1679}
1680
1681#[derive(Debug, Clone, Serialize)]
1683#[serde(rename_all = "camelCase")]
1684pub struct RfqCancelRequest {
1685 pub request_id: String,
1686}
1687
1688#[derive(Debug, Clone, Default)]
1690pub struct RfqRequestsParams {
1691 pub offset: Option<String>,
1692 pub limit: Option<u32>,
1693 pub state: Option<String>,
1694 pub request_ids: Vec<String>,
1695 pub markets: Vec<String>,
1696 pub size_min: Option<Decimal>,
1697 pub size_max: Option<Decimal>,
1698 pub size_usdc_min: Option<Decimal>,
1699 pub size_usdc_max: Option<Decimal>,
1700 pub price_min: Option<Decimal>,
1701 pub price_max: Option<Decimal>,
1702 pub sort_by: Option<String>,
1703 pub sort_dir: Option<String>,
1704}
1705
1706impl RfqRequestsParams {
1707 pub fn to_query_params(&self) -> Vec<(String, String)> {
1708 let mut params = Vec::new();
1709
1710 if let Some(x) = &self.offset {
1711 params.push(("offset".to_string(), x.clone()));
1712 }
1713 if let Some(x) = self.limit {
1714 params.push(("limit".to_string(), x.to_string()));
1715 }
1716 if let Some(x) = &self.state {
1717 params.push(("state".to_string(), x.clone()));
1718 }
1719 for x in &self.request_ids {
1720 params.push(("requestIds[]".to_string(), x.clone()));
1721 }
1722 for x in &self.markets {
1723 params.push(("markets[]".to_string(), x.clone()));
1724 }
1725
1726 if let Some(x) = self.size_min {
1727 params.push(("sizeMin".to_string(), x.to_string()));
1728 }
1729 if let Some(x) = self.size_max {
1730 params.push(("sizeMax".to_string(), x.to_string()));
1731 }
1732 if let Some(x) = self.size_usdc_min {
1733 params.push(("sizeUsdcMin".to_string(), x.to_string()));
1734 }
1735 if let Some(x) = self.size_usdc_max {
1736 params.push(("sizeUsdcMax".to_string(), x.to_string()));
1737 }
1738 if let Some(x) = self.price_min {
1739 params.push(("priceMin".to_string(), x.to_string()));
1740 }
1741 if let Some(x) = self.price_max {
1742 params.push(("priceMax".to_string(), x.to_string()));
1743 }
1744
1745 if let Some(x) = &self.sort_by {
1746 params.push(("sortBy".to_string(), x.clone()));
1747 }
1748 if let Some(x) = &self.sort_dir {
1749 params.push(("sortDir".to_string(), x.clone()));
1750 }
1751
1752 params
1753 }
1754}
1755
1756#[derive(Debug, Clone, Serialize, Deserialize)]
1758#[serde(rename_all = "camelCase")]
1759pub struct RfqRequestData {
1760 pub request_id: String,
1761 pub user_address: String,
1762 pub proxy_address: String,
1763 pub condition: String,
1764 pub token: String,
1765 pub complement: String,
1766 pub side: Side,
1767 #[serde(deserialize_with = "crate::decode::deserializers::decimal_from_string")]
1768 pub size_in: Decimal,
1769 #[serde(deserialize_with = "crate::decode::deserializers::decimal_from_string")]
1770 pub size_out: Decimal,
1771 #[serde(deserialize_with = "crate::decode::deserializers::decimal_from_string")]
1772 pub price: Decimal,
1773 pub state: String,
1774 pub expiry: u64,
1775}
1776
1777#[derive(Debug, Clone, Serialize)]
1779#[serde(rename_all = "camelCase")]
1780pub struct RfqCreateQuote {
1781 pub request_id: String,
1782 pub asset_in: String,
1783 pub asset_out: String,
1784 pub amount_in: String,
1785 pub amount_out: String,
1786 pub user_type: u8,
1787}
1788
1789#[derive(Debug, Clone, Serialize, Deserialize)]
1790#[serde(rename_all = "camelCase")]
1791pub struct RfqCreateQuoteResponse {
1792 pub quote_id: String,
1793}
1794
1795#[derive(Debug, Clone, Serialize)]
1797#[serde(rename_all = "camelCase")]
1798pub struct RfqCancelQuote {
1799 pub quote_id: String,
1800}
1801
1802#[derive(Debug, Clone, Default)]
1804pub struct RfqQuotesParams {
1805 pub offset: Option<String>,
1806 pub limit: Option<u32>,
1807 pub state: Option<String>,
1808 pub quote_ids: Vec<String>,
1809 pub request_ids: Vec<String>,
1810 pub markets: Vec<String>,
1811 pub size_min: Option<Decimal>,
1812 pub size_max: Option<Decimal>,
1813 pub size_usdc_min: Option<Decimal>,
1814 pub size_usdc_max: Option<Decimal>,
1815 pub price_min: Option<Decimal>,
1816 pub price_max: Option<Decimal>,
1817 pub sort_by: Option<String>,
1818 pub sort_dir: Option<String>,
1819}
1820
1821impl RfqQuotesParams {
1822 pub fn to_query_params(&self) -> Vec<(String, String)> {
1823 let mut params = Vec::new();
1824
1825 if let Some(x) = &self.offset {
1826 params.push(("offset".to_string(), x.clone()));
1827 }
1828 if let Some(x) = self.limit {
1829 params.push(("limit".to_string(), x.to_string()));
1830 }
1831 if let Some(x) = &self.state {
1832 params.push(("state".to_string(), x.clone()));
1833 }
1834 for x in &self.quote_ids {
1835 params.push(("quoteIds[]".to_string(), x.clone()));
1836 }
1837 for x in &self.request_ids {
1838 params.push(("requestIds[]".to_string(), x.clone()));
1839 }
1840 for x in &self.markets {
1841 params.push(("markets[]".to_string(), x.clone()));
1842 }
1843
1844 if let Some(x) = self.size_min {
1845 params.push(("sizeMin".to_string(), x.to_string()));
1846 }
1847 if let Some(x) = self.size_max {
1848 params.push(("sizeMax".to_string(), x.to_string()));
1849 }
1850 if let Some(x) = self.size_usdc_min {
1851 params.push(("sizeUsdcMin".to_string(), x.to_string()));
1852 }
1853 if let Some(x) = self.size_usdc_max {
1854 params.push(("sizeUsdcMax".to_string(), x.to_string()));
1855 }
1856 if let Some(x) = self.price_min {
1857 params.push(("priceMin".to_string(), x.to_string()));
1858 }
1859 if let Some(x) = self.price_max {
1860 params.push(("priceMax".to_string(), x.to_string()));
1861 }
1862
1863 if let Some(x) = &self.sort_by {
1864 params.push(("sortBy".to_string(), x.clone()));
1865 }
1866 if let Some(x) = &self.sort_dir {
1867 params.push(("sortDir".to_string(), x.clone()));
1868 }
1869
1870 params
1871 }
1872}
1873
1874#[derive(Debug, Clone, Serialize, Deserialize)]
1876#[serde(rename_all = "camelCase")]
1877pub struct RfqQuoteData {
1878 pub quote_id: String,
1879 pub request_id: String,
1880 pub user_address: String,
1881 pub proxy_address: String,
1882 pub condition: String,
1883 pub token: String,
1884 pub complement: String,
1885 pub side: Side,
1886 #[serde(deserialize_with = "crate::decode::deserializers::decimal_from_string")]
1887 pub size_in: Decimal,
1888 #[serde(deserialize_with = "crate::decode::deserializers::decimal_from_string")]
1889 pub size_out: Decimal,
1890 #[serde(deserialize_with = "crate::decode::deserializers::decimal_from_string")]
1891 pub price: Decimal,
1892 pub match_type: String,
1893 pub state: String,
1894}
1895
1896#[derive(Debug, Clone, Serialize, Deserialize)]
1898pub struct RfqListResponse<T> {
1899 pub data: Vec<T>,
1900 pub next_cursor: Option<String>,
1901 pub limit: u32,
1902 pub count: u32,
1903}
1904
1905#[derive(Debug, Clone, Serialize)]
1907#[serde(rename_all = "camelCase")]
1908pub struct RfqOrderExecutionRequest {
1909 pub request_id: String,
1910 pub quote_id: String,
1911 pub maker: String,
1912 pub signer: String,
1913 pub taker: String,
1914 pub expiration: u64,
1915 pub nonce: String,
1916 pub fee_rate_bps: String,
1917 pub side: String,
1918 pub token_id: String,
1919 pub maker_amount: String,
1920 pub taker_amount: String,
1921 pub signature_type: u8,
1922 pub signature: String,
1923 pub salt: u64,
1924 pub owner: String,
1925}
1926
1927#[derive(Debug, Clone, Serialize, Deserialize)]
1928#[serde(rename_all = "camelCase")]
1929pub struct RfqApproveOrderResponse {
1930 pub trade_ids: Vec<String>,
1931}
1932
1933pub type ClientResult<T> = anyhow::Result<T>;
1935
1936pub type Result<T> = std::result::Result<T, crate::errors::PolyfillError>;
1938
1939pub type ApiCreds = ApiCredentials;
1941pub type CreateOrderOptions = OrderOptions;
1942pub type OrderArgs = OrderRequest;
1943
1944#[cfg(test)]
1945mod tests {
1946 use super::{
1947 OrderMessage, OrderType, PostOrder, SignedOrderRequest, TradeMessage, TradeMessageStatus,
1948 };
1949
1950 #[test]
1951 fn test_order_type_fak_serde_and_string() {
1952 assert_eq!(OrderType::FAK.as_str(), "FAK");
1953
1954 let json = serde_json::to_string(&OrderType::FAK).unwrap();
1955 assert_eq!(json, "\"FAK\"");
1956
1957 let parsed: OrderType = serde_json::from_str("\"FAK\"").unwrap();
1958 assert_eq!(parsed, OrderType::FAK);
1959 }
1960
1961 #[test]
1962 fn test_post_order_v2_json_shape() {
1963 let order = SignedOrderRequest {
1964 salt: 42,
1965 maker: "0xAbC0000000000000000000000000000000000001".to_string(),
1966 signer: "0xAbC0000000000000000000000000000000000001".to_string(),
1967 taker: "0x0000000000000000000000000000000000000000".to_string(),
1968 token_id: "123456789".to_string(),
1969 maker_amount: "100000000".to_string(),
1970 taker_amount: "50000000".to_string(),
1971 side: "BUY".to_string(),
1972 signature_type: 0,
1973 timestamp: "1700000000000".to_string(),
1974 expiration: "0".to_string(),
1975 metadata: "0x0000000000000000000000000000000000000000000000000000000000000000"
1976 .to_string(),
1977 builder: "0x0000000000000000000000000000000000000000000000000000000000000000"
1978 .to_string(),
1979 signature: "0xdeadbeef".to_string(),
1980 };
1981 let body = PostOrder::new(order, "owner-uuid".to_string(), OrderType::GTC);
1982 let v = serde_json::to_value(&body).unwrap();
1983
1984 let obj = v.as_object().expect("PostOrder must serialize as object");
1986 assert!(obj.contains_key("order"));
1987 assert!(obj.contains_key("owner"));
1988 assert!(obj.contains_key("orderType"));
1989 assert!(obj.contains_key("deferExec"));
1990 assert!(obj.contains_key("postOnly"));
1991 assert_eq!(obj["deferExec"], serde_json::Value::Bool(false));
1992 assert_eq!(obj["postOnly"], serde_json::Value::Bool(false));
1993 assert_eq!(obj["orderType"], serde_json::json!("GTC"));
1994
1995 let order = obj["order"].as_object().expect("order must be object");
1997 for key in [
1998 "salt",
1999 "maker",
2000 "signer",
2001 "taker",
2002 "tokenId",
2003 "makerAmount",
2004 "takerAmount",
2005 "side",
2006 "signatureType",
2007 "timestamp",
2008 "expiration",
2009 "metadata",
2010 "builder",
2011 "signature",
2012 ] {
2013 assert!(order.contains_key(key), "missing {key}");
2014 }
2015 assert!(!order.contains_key("nonce"), "V2 must not include nonce");
2016 assert!(
2017 !order.contains_key("feeRateBps"),
2018 "V2 must not include feeRateBps"
2019 );
2020 }
2021
2022 #[test]
2023 fn test_post_order_with_flags_sets_post_only_and_defer_exec() {
2024 let order = SignedOrderRequest {
2025 salt: 1,
2026 maker: "0x0".to_string(),
2027 signer: "0x0".to_string(),
2028 taker: "0x0".to_string(),
2029 token_id: "1".to_string(),
2030 maker_amount: "0".to_string(),
2031 taker_amount: "0".to_string(),
2032 side: "BUY".to_string(),
2033 signature_type: 0,
2034 timestamp: "0".to_string(),
2035 expiration: "0".to_string(),
2036 metadata: "0x0".to_string(),
2037 builder: "0x0".to_string(),
2038 signature: "0x0".to_string(),
2039 };
2040 let body = PostOrder::new(order, "o".to_string(), OrderType::GTC).with_flags(true, true);
2041 assert!(body.post_only);
2042 assert!(body.defer_exec);
2043 }
2044
2045 #[test]
2046 fn test_trade_message_status_retrying_failed() {
2047 let s: TradeMessageStatus = serde_json::from_str("\"RETRYING\"").unwrap();
2049 assert_eq!(s, TradeMessageStatus::Retrying);
2050
2051 let s: TradeMessageStatus = serde_json::from_str("\"FAILED\"").unwrap();
2052 assert_eq!(s, TradeMessageStatus::Failed);
2053
2054 let s: TradeMessageStatus = serde_json::from_str("\"retrying\"").unwrap();
2056 assert_eq!(s, TradeMessageStatus::Retrying);
2057
2058 let s: TradeMessageStatus = serde_json::from_str("\"failed\"").unwrap();
2059 assert_eq!(s, TradeMessageStatus::Failed);
2060 }
2061
2062 #[test]
2063 fn test_trade_message_with_bucket_index() {
2064 let json_with = serde_json::json!({
2065 "id": "t1",
2066 "market": "0xmkt",
2067 "asset_id": "123",
2068 "side": "BUY",
2069 "size": "1.0",
2070 "price": "0.5",
2071 "bucket_index": 42
2072 });
2073 let tm: TradeMessage = serde_json::from_value(json_with).unwrap();
2074 assert_eq!(tm.bucket_index, Some(42));
2075
2076 let json_without = serde_json::json!({
2077 "id": "t2",
2078 "market": "0xmkt",
2079 "asset_id": "123",
2080 "side": "BUY",
2081 "size": "1.0",
2082 "price": "0.5"
2083 });
2084 let tm: TradeMessage = serde_json::from_value(json_without).unwrap();
2085 assert_eq!(tm.bucket_index, None);
2086 }
2087
2088 #[test]
2089 fn test_order_message_v2_fields() {
2090 let json = serde_json::json!({
2091 "id": "o1",
2092 "market": "0xmkt",
2093 "asset_id": "123",
2094 "side": "BUY",
2095 "price": "0.5",
2096 "owner": "owner-uuid",
2097 "expiration": "1700000000",
2098 "order_type": "GTD",
2099 "maker_address": "0xabc0000000000000000000000000000000000001"
2100 });
2101 let om: OrderMessage = serde_json::from_value(json).unwrap();
2102 assert_eq!(om.owner.as_deref(), Some("owner-uuid"));
2103 assert_eq!(om.expiration.as_deref(), Some("1700000000"));
2104 assert_eq!(om.order_type.as_deref(), Some("GTD"));
2105 assert_eq!(
2106 om.maker_address.as_deref(),
2107 Some("0xabc0000000000000000000000000000000000001")
2108 );
2109 }
2110}