clob_client_rust/
types.rs

1use serde::de::Deserializer;
2use serde::ser::Serializer;
3use serde::{Deserialize, Serialize};
4use std::fmt;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct ApiKeyCreds {
8    pub key: String,
9    pub secret: String,
10    pub passphrase: String,
11}
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
14#[serde(rename_all = "camelCase")]
15pub struct ApiKeyRaw {
16    pub api_key: String,
17    pub secret: String,
18    pub passphrase: String,
19}
20
21/// Response returned by `/auth/readonly-api-key`.
22///
23/// TypeScript SDK returns `{ apiKey: string }`.
24#[derive(Debug, Clone, Serialize, Deserialize)]
25#[serde(rename_all = "camelCase")]
26pub struct ReadonlyApiKeyResponse {
27    pub api_key: String,
28}
29
30/// Request body for DELETE `/auth/readonly-api-key`.
31#[derive(Debug, Clone, Serialize, Deserialize)]
32#[serde(rename_all = "camelCase")]
33pub struct DeleteReadonlyApiKeyRequest {
34    pub key: String,
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
38#[serde(rename_all = "camelCase")]
39pub struct ApiKeysResponse {
40    pub api_keys: Vec<ApiKeyCreds>,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct BanStatus {
45    pub closed_only: bool,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct BalanceAllowanceResponse {
50    pub balance: String,
51    pub allowance: String,
52}
53
54// --------------------------------------------------------------------------------------
55// RFQ Types (TypeScript parity)
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
58#[serde(rename_all = "camelCase")]
59pub struct CancelRfqRequestParams {
60    pub request_id: String,
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
64#[serde(rename_all = "camelCase")]
65pub struct CreateRfqRequestParams {
66    pub asset_in: String,
67    pub asset_out: String,
68    pub amount_in: String,
69    pub amount_out: String,
70    pub user_type: u8,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
74#[serde(rename_all = "camelCase")]
75pub struct RfqQuoteParams {
76    pub request_id: String,
77    pub asset_in: String,
78    pub asset_out: String,
79    pub amount_in: String,
80    pub amount_out: String,
81    pub user_type: u8,
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
85#[serde(rename_all = "camelCase")]
86pub struct CreateRfqQuoteParams {
87    pub request_id: String,
88    pub asset_in: String,
89    pub asset_out: String,
90    pub amount_in: String,
91    pub amount_out: String,
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize)]
95#[serde(rename_all = "camelCase")]
96pub struct CancelRfqQuoteParams {
97    pub quote_id: String,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
101#[serde(rename_all = "camelCase")]
102pub struct AcceptQuoteParams {
103    pub request_id: String,
104    pub quote_id: String,
105    pub expiration: u64,
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
109#[serde(rename_all = "camelCase")]
110pub struct ApproveOrderParams {
111    pub request_id: String,
112    pub quote_id: String,
113    pub expiration: u64,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize, Default)]
117#[serde(rename_all = "camelCase")]
118pub struct GetRfqQuotesParams {
119    pub quote_ids: Option<Vec<String>>,
120    pub states: Option<Vec<String>>,
121    pub user_address: Option<String>,
122    pub state: Option<String>,
123    pub markets: Option<Vec<String>>,
124    pub request_ids: Option<Vec<String>>,
125    pub size_min: Option<f64>,
126    pub size_max: Option<f64>,
127    pub size_usdc_min: Option<f64>,
128    pub size_usdc_max: Option<f64>,
129    pub price_min: Option<f64>,
130    pub price_max: Option<f64>,
131    pub sort_by: Option<String>,
132    pub sort_dir: Option<String>,
133    pub limit: Option<u32>,
134    pub offset: Option<String>,
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize, Default)]
138#[serde(rename_all = "camelCase")]
139pub struct GetRfqBestQuoteParams {
140    pub request_id: Option<String>,
141}
142
143/// RFQ user order input (not sent directly to API).
144#[derive(Debug, Clone)]
145pub struct RfqUserOrder {
146    pub token_id: String,
147    pub price: f64,
148    pub size: f64,
149    pub side: Side,
150}
151
152/// RFQ user quote input (not sent directly to API).
153#[derive(Debug, Clone)]
154pub struct RfqUserQuote {
155    pub request_id: String,
156    pub token_id: String,
157    pub price: f64,
158    pub size: f64,
159    pub side: Side,
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize, Default)]
163#[serde(rename_all = "camelCase")]
164pub struct GetRfqRequestsParams {
165    pub request_ids: Option<Vec<String>>,
166    pub states: Option<Vec<String>>,
167    pub state: Option<String>,
168    pub markets: Option<Vec<String>>,
169    pub size_min: Option<f64>,
170    pub size_max: Option<f64>,
171    pub size_usdc_min: Option<f64>,
172    pub size_usdc_max: Option<f64>,
173    pub price_min: Option<f64>,
174    pub price_max: Option<f64>,
175    pub sort_by: Option<String>,
176    pub sort_dir: Option<String>,
177    pub limit: Option<u32>,
178    pub offset: Option<String>,
179}
180
181#[derive(Debug, Clone, Serialize, Deserialize)]
182#[serde(rename_all = "snake_case")]
183pub struct RfqPaginatedResponse<T> {
184    pub data: Vec<T>,
185    pub next_cursor: String,
186    pub limit: u32,
187    pub count: u32,
188    pub total_count: Option<u32>,
189}
190
191#[derive(Debug, Clone, Serialize, Deserialize)]
192#[serde(rename_all = "camelCase")]
193pub struct RfqRequest {
194    pub request_id: String,
195    pub user_address: String,
196    pub proxy_address: String,
197    pub token: String,
198    pub complement: String,
199    pub condition: String,
200    pub side: String,
201    pub size_in: String,
202    pub size_out: String,
203    pub price: f64,
204    pub accepted_quote_id: String,
205    pub state: String,
206    pub expiry: String,
207    pub created_at: String,
208    pub updated_at: String,
209}
210
211#[derive(Debug, Clone, Serialize, Deserialize)]
212#[serde(rename_all = "camelCase")]
213pub struct RfqQuote {
214    pub quote_id: String,
215    pub request_id: String,
216    pub user_address: String,
217    pub proxy_address: String,
218    pub complement: String,
219    pub condition: String,
220    pub token: String,
221    pub side: String,
222    pub size_in: String,
223    pub size_out: String,
224    pub price: f64,
225    pub state: String,
226    pub expiry: String,
227    pub created_at: String,
228    pub updated_at: String,
229}
230
231pub type RfqRequestsResponse = RfqPaginatedResponse<RfqRequest>;
232pub type RfqQuotesResponse = RfqPaginatedResponse<RfqQuote>;
233
234#[derive(Debug, Clone, Serialize, Deserialize)]
235#[serde(rename_all = "camelCase")]
236pub struct RfqRequestResponse {
237    pub request_id: String,
238    #[serde(default)]
239    pub error: Option<String>,
240}
241
242#[derive(Debug, Clone, Serialize, Deserialize)]
243#[serde(rename_all = "camelCase")]
244pub struct RfqQuoteResponse {
245    pub quote_id: String,
246    #[serde(default)]
247    pub error: Option<String>,
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
251pub struct OrderScoring {
252    pub scoring: bool,
253}
254
255pub type OrdersScoring = std::collections::HashMap<String, bool>;
256
257#[derive(Copy, Debug, Clone, Serialize, Deserialize, PartialEq)]
258#[serde(rename_all = "UPPERCASE")]
259pub enum Side {
260    BUY,
261    SELL,
262}
263
264#[derive(Copy, Debug, Clone, Serialize, Deserialize, PartialEq)]
265#[serde(rename_all = "UPPERCASE")]
266pub enum OrderType {
267    GTC,
268    FOK,
269    GTD,
270    FAK,
271}
272
273pub type TickSize = String; // e.g. "0.01"
274
275pub type TickSizes = std::collections::HashMap<String, TickSize>;
276pub type NegRisk = std::collections::HashMap<String, bool>;
277pub type FeeRates = std::collections::HashMap<String, u32>;
278
279#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
280#[serde(rename_all = "camelCase")]
281pub struct Market {
282    pub id: String,
283    pub name: Option<String>,
284    pub asset_id: Option<String>,
285    pub min_order_size: Option<String>,
286    pub tick_size: Option<String>,
287    pub neg_risk: Option<bool>,
288    pub metadata: Option<serde_json::Value>,
289}
290
291#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
292#[serde(rename_all = "camelCase")]
293pub struct MarketSummary {
294    pub market: String,
295    pub asset_id: String,
296    pub timestamp: String,
297    pub bids: Vec<OrderSummary>,
298    pub asks: Vec<OrderSummary>,
299    pub min_order_size: String,
300    pub tick_size: String,
301    pub neg_risk: bool,
302    pub hash: String,
303}
304
305#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
306pub struct OrderSummary {
307    pub price: String,
308    pub size: String,
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize)]
312#[serde(rename_all = "camelCase")]
313pub struct OrderBookSummary {
314    pub market: String,
315    pub asset_id: String,
316    pub timestamp: String,
317    pub bids: Vec<OrderSummary>,
318    pub asks: Vec<OrderSummary>,
319    pub min_order_size: String,
320    pub tick_size: String,
321    pub neg_risk: bool,
322    pub hash: String,
323}
324
325/// Signature type for orders
326/// - EOA: Standard Externally Owned Account (default)
327/// - PolyProxy: Polymarket Proxy Wallet
328/// - PolyGnosisSafe: Gnosis Safe Multisig
329#[derive(Debug, Clone, Copy, PartialEq, Eq)]
330#[repr(u8)]
331#[derive(Default)]
332pub enum SignatureType {
333    #[default]
334    EOA = 0,
335    PolyProxy = 1,
336    PolyGnosisSafe = 2,
337}
338
339impl From<SignatureType> for u8 {
340    fn from(sig_type: SignatureType) -> Self {
341        sig_type as u8
342    }
343}
344
345impl From<u8> for SignatureType {
346    fn from(value: u8) -> Self {
347        match value {
348            0 => SignatureType::EOA,
349            1 => SignatureType::PolyProxy,
350            2 => SignatureType::PolyGnosisSafe,
351            _ => SignatureType::EOA, // fallback to default
352        }
353    }
354}
355
356// Custom serialization: always serialize as number
357impl Serialize for SignatureType {
358    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
359    where
360        S: Serializer,
361    {
362        serializer.serialize_u8(*self as u8)
363    }
364}
365
366// Custom deserialization: accept both string and number
367impl<'de> Deserialize<'de> for SignatureType {
368    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
369    where
370        D: Deserializer<'de>,
371    {
372        struct SignatureTypeVisitor;
373
374        impl<'de> serde::de::Visitor<'de> for SignatureTypeVisitor {
375            type Value = SignatureType;
376
377            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
378                formatter
379                    .write_str("a string (EOA, POLY_PROXY, POLY_GNOSIS_SAFE) or a number (0, 1, 2)")
380            }
381
382            fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
383            where
384                E: serde::de::Error,
385            {
386                match value {
387                    0 => Ok(SignatureType::EOA),
388                    1 => Ok(SignatureType::PolyProxy),
389                    2 => Ok(SignatureType::PolyGnosisSafe),
390                    _ => Ok(SignatureType::EOA), // fallback
391                }
392            }
393
394            fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
395            where
396                E: serde::de::Error,
397            {
398                self.visit_u64(value as u64)
399            }
400
401            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
402            where
403                E: serde::de::Error,
404            {
405                match value {
406                    "EOA" => Ok(SignatureType::EOA),
407                    "POLY_PROXY" => Ok(SignatureType::PolyProxy),
408                    "POLY_GNOSIS_SAFE" => Ok(SignatureType::PolyGnosisSafe),
409                    _ => Ok(SignatureType::EOA), // fallback
410                }
411            }
412        }
413
414        deserializer.deserialize_any(SignatureTypeVisitor)
415    }
416}
417
418#[derive(Debug, Clone, Serialize, Deserialize)]
419#[serde(rename_all = "camelCase")]
420pub struct OrderData {
421    pub maker: String,
422    pub taker: String,
423    pub token_id: String,
424    pub maker_amount: String,
425    pub taker_amount: String,
426    pub side: Side,
427    pub fee_rate_bps: String,
428    pub nonce: String,
429    pub signer: String,
430    pub expiration: String,
431    pub signature_type: SignatureType,
432}
433
434#[derive(Debug, Clone, Serialize, Deserialize)]
435#[serde(rename_all = "camelCase")]
436pub struct SignedOrder {
437    pub salt: String,
438    pub maker: String,
439    pub signer: String,
440    pub taker: String,
441    pub token_id: String,
442    pub maker_amount: String,
443    pub taker_amount: String,
444    pub expiration: String,
445    pub nonce: String,
446    pub fee_rate_bps: String,
447    pub side: Side,
448    pub signature_type: SignatureType,
449    pub signature: String,
450}
451
452/// NewOrder is the payload structure for posting orders to the API
453/// It wraps SignedOrder with orderType, owner, and deferExec fields
454#[derive(Debug, Clone, Serialize, Deserialize)]
455#[serde(rename_all = "camelCase")]
456pub struct NewOrder {
457    pub order: NewOrderData,
458    pub owner: String,
459    pub order_type: OrderType,
460    #[serde(default)]
461    pub defer_exec: bool,
462}
463
464#[derive(Debug, Clone, Serialize, Deserialize)]
465#[serde(rename_all = "camelCase")]
466pub struct NewOrderData {
467    // API 要求 salt 为 number;为避免 JS 精度丢失,生成时限制在 2^53-1 范围内
468    // 序列化时使用 i64 以确保 JSON 中是 number 而非字符串
469    pub salt: i64,
470    pub maker: String,
471    pub signer: String,
472    pub taker: String,
473    pub token_id: String,
474    pub maker_amount: String,
475    pub taker_amount: String,
476    pub expiration: String,
477    pub nonce: String,
478    pub fee_rate_bps: String,
479    pub side: Side,
480    pub signature_type: SignatureType,
481    pub signature: String,
482}
483
484// Simple Chain constants
485pub const CHAIN_POLYGON: i32 = 137;
486pub const CHAIN_AMOY: i32 = 80002;
487
488#[derive(Debug, Clone, Serialize, Deserialize)]
489#[serde(rename_all = "camelCase")]
490pub struct UserOrder {
491    pub token_id: String,
492    pub price: f64,
493    pub size: f64,
494    pub side: Side,
495    // 改为必填: fee_rate_bps 由外部传入,不再在创建流程中自动获取
496    pub fee_rate_bps: f64,
497    pub nonce: Option<u64>,
498    pub expiration: Option<u64>,
499    pub taker: Option<String>,
500}
501
502#[derive(Debug, Clone, Serialize, Deserialize)]
503#[serde(rename_all = "camelCase")]
504pub struct UserMarketOrder {
505    pub token_id: String,
506    // 改为必填: 市价单价格需由外部计算并传入
507    pub price: f64,
508    pub amount: f64,
509    pub side: Side,
510    // 改为必填: 由外部传入
511    pub fee_rate_bps: f64,
512    pub nonce: Option<u64>,
513    pub taker: Option<String>,
514    // order_type 改为必填: 市价单必须明确 FOK/FAK (或未来支持的其他类型)
515    pub order_type: OrderType,
516}
517
518#[derive(Debug, Clone, Serialize, Deserialize)]
519#[serde(rename_all = "camelCase")]
520pub struct Order {
521    pub id: Option<String>,
522    pub salt: Option<String>,
523    pub maker: Option<String>,
524    pub signer: Option<String>,
525    pub taker: Option<String>,
526    pub token_id: Option<String>,
527    pub maker_amount: Option<String>,
528    pub taker_amount: Option<String>,
529    pub price: Option<String>,
530    pub size: Option<String>,
531    pub expiration: Option<String>,
532    pub nonce: Option<String>,
533    pub fee_rate_bps: Option<String>,
534    pub side: Option<Side>,
535    pub signature_type: Option<SignatureType>,
536    pub signature: Option<String>,
537    pub status: Option<OrderStatus>,
538    pub metadata: Option<serde_json::Value>,
539}
540
541/// Response from GET /order endpoint
542/// According to API docs: https://docs.polymarket.com/developers/CLOB/orders/get-order
543/// Matches TypeScript SDK's OpenOrder interface
544#[derive(Debug, Clone, Serialize, Deserialize)]
545#[serde(rename_all = "snake_case")]
546pub struct OpenOrder {
547    pub id: String,
548    pub status: String,
549    pub owner: String,
550    pub maker_address: String,
551    pub market: String,
552    pub asset_id: String,
553    pub side: String,
554    pub original_size: String,
555    pub size_matched: String,
556    pub price: String,
557    pub associate_trades: Vec<String>,
558    pub outcome: String,
559    pub created_at: u64,
560    pub expiration: String,
561    #[serde(rename = "type", alias = "order_type")]
562    pub order_type: String,
563}
564
565/// Response from POST /order endpoint
566/// According to API docs: https://docs.polymarket.com/developers/CLOB/orders/create-order
567#[derive(Debug, Clone, Serialize, Deserialize)]
568#[serde(rename_all = "camelCase")]
569pub struct OrderResponse {
570    /// Boolean indicating if server-side error occurred (success = false -> server-side error)
571    pub success: bool,
572
573    /// Error message in case of unsuccessful placement
574    #[serde(rename = "errorMsg", default)]
575    pub error_msg: String,
576
577    /// ID of the order
578    #[serde(rename = "orderID", alias = "orderId", default)]
579    pub order_id: String,
580
581    /// Hash of settlement transaction if order was marketable and triggered a match
582    /// API docs call this "orderHashes", but TypeScript SDK uses "transactionsHashes"
583    #[serde(rename = "orderHashes", alias = "transactionsHashes", default)]
584    pub order_hashes: Vec<String>,
585
586    /// Order status: "matched", "live", "delayed", "unmatched"
587    #[serde(default)]
588    pub status: Option<String>,
589
590    /// Taking amount (not in official API docs, but returned by some endpoints)
591    #[serde(default)]
592    pub taking_amount: Option<String>,
593
594    /// Making amount (not in official API docs, but returned by some endpoints)
595    #[serde(default)]
596    pub making_amount: Option<String>,
597}
598
599#[derive(Debug, Clone, PartialEq)]
600pub enum OrderStatus {
601    OPEN,
602    FILLED,
603    CANCELLED,
604    OTHER(String),
605}
606
607impl fmt::Display for OrderStatus {
608    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
609        let s = match self {
610            OrderStatus::OPEN => "OPEN",
611            OrderStatus::FILLED => "FILLED",
612            OrderStatus::CANCELLED => "CANCELLED",
613            OrderStatus::OTHER(v) => v.as_str(),
614        };
615        write!(f, "{}", s)
616    }
617}
618
619impl Serialize for OrderStatus {
620    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
621    where
622        S: Serializer,
623    {
624        serializer.serialize_str(&self.to_string())
625    }
626}
627
628impl<'de> Deserialize<'de> for OrderStatus {
629    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
630    where
631        D: Deserializer<'de>,
632    {
633        let s = String::deserialize(deserializer)?;
634        match s.as_str() {
635            "OPEN" | "open" => Ok(OrderStatus::OPEN),
636            "FILLED" | "filled" => Ok(OrderStatus::FILLED),
637            "CANCELLED" | "CANCELED" | "cancelled" | "canceled" => Ok(OrderStatus::CANCELLED),
638            other => Ok(OrderStatus::OTHER(other.to_string())),
639        }
640    }
641}
642
643#[derive(Debug, Clone, Serialize, Deserialize)]
644pub struct MakerOrder {
645    pub order_id: String,
646    pub owner: String,
647    pub maker_address: String,
648    pub matched_amount: String,
649    pub price: String,
650    pub fee_rate_bps: String,
651    pub asset_id: String,
652    pub outcome: String,
653    pub side: Side,
654}
655
656#[derive(Debug, Clone, Serialize, Deserialize)]
657pub struct Trade {
658    pub id: String,
659    pub taker_order_id: String,
660    pub market: String,
661    pub asset_id: String,
662    pub side: Side,
663    pub size: String,
664    pub fee_rate_bps: String,
665    pub price: String,
666    pub status: String,
667    pub match_time: String,
668    pub last_update: String,
669    pub outcome: String,
670    pub bucket_index: i64,
671    pub owner: String,
672    pub maker_address: String,
673    pub maker_orders: Vec<MakerOrder>,
674    pub transaction_hash: String,
675    pub trader_side: String,
676}
677
678#[derive(Debug, Clone, Serialize, Deserialize)]
679#[serde(rename_all = "camelCase")]
680pub struct Notification {
681    pub id: Option<String>,
682    pub title: Option<String>,
683    pub body: Option<String>,
684    pub data: Option<serde_json::Value>,
685    pub created_at: Option<String>,
686    pub read: Option<bool>,
687}
688
689#[derive(Debug, Clone, Serialize, Deserialize)]
690#[serde(rename_all = "camelCase")]
691pub struct Reward {
692    pub market: Option<String>,
693    pub amount: Option<String>,
694    pub timestamp: Option<String>,
695    pub metadata: Option<serde_json::Value>,
696}
697
698#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
699pub struct MarketPrice {
700    /// timestamp (ms or unix seconds depending on API)
701    pub t: i64,
702    /// price
703    pub p: f64,
704}
705
706#[derive(Debug, Clone, Serialize, Deserialize)]
707#[serde(rename_all = "camelCase")]
708pub struct BuilderTrade {
709    pub id: String,
710    pub trade_type: String,
711    pub taker_order_hash: String,
712    pub builder: String,
713    pub market: String,
714    pub asset_id: String,
715    pub side: String,
716    pub size: String,
717    pub size_usdc: String,
718    pub price: String,
719    pub status: String,
720    pub outcome: String,
721    pub outcome_index: i64,
722    pub owner: String,
723    pub maker: String,
724    pub transaction_hash: String,
725    pub match_time: String,
726    pub bucket_index: i64,
727    pub fee: String,
728    pub fee_usdc: String,
729    pub err_msg: Option<String>,
730    pub created_at: Option<String>,
731    pub updated_at: Option<String>,
732}