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#[derive(Debug, Clone, Serialize, Deserialize)]
22#[serde(rename_all = "camelCase")]
23pub struct ApiKeysResponse {
24    pub api_keys: Vec<ApiKeyCreds>,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct BanStatus {
29    pub closed_only: bool,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct BalanceAllowanceResponse {
34    pub balance: String,
35    pub allowance: String,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct OrderScoring {
40    pub scoring: bool,
41}
42
43pub type OrdersScoring = std::collections::HashMap<String, bool>;
44
45#[derive(Copy, Debug, Clone, Serialize, Deserialize, PartialEq)]
46#[serde(rename_all = "UPPERCASE")]
47pub enum Side {
48    BUY,
49    SELL,
50}
51
52#[derive(Copy, Debug, Clone, Serialize, Deserialize, PartialEq)]
53#[serde(rename_all = "UPPERCASE")]
54pub enum OrderType {
55    GTC,
56    FOK,
57    GTD,
58    FAK,
59}
60
61pub type TickSize = String; // e.g. "0.01"
62
63pub type TickSizes = std::collections::HashMap<String, TickSize>;
64pub type NegRisk = std::collections::HashMap<String, bool>;
65pub type FeeRates = std::collections::HashMap<String, u32>;
66
67#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
68#[serde(rename_all = "camelCase")]
69pub struct Market {
70    pub id: String,
71    pub name: Option<String>,
72    pub asset_id: Option<String>,
73    pub min_order_size: Option<String>,
74    pub tick_size: Option<String>,
75    pub neg_risk: Option<bool>,
76    pub metadata: Option<serde_json::Value>,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
80#[serde(rename_all = "camelCase")]
81pub struct MarketSummary {
82    pub market: String,
83    pub asset_id: String,
84    pub timestamp: String,
85    pub bids: Vec<OrderSummary>,
86    pub asks: Vec<OrderSummary>,
87    pub min_order_size: String,
88    pub tick_size: String,
89    pub neg_risk: bool,
90    pub hash: String,
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
94pub struct OrderSummary {
95    pub price: String,
96    pub size: String,
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize)]
100#[serde(rename_all = "camelCase")]
101pub struct OrderBookSummary {
102    pub market: String,
103    pub asset_id: String,
104    pub timestamp: String,
105    pub bids: Vec<OrderSummary>,
106    pub asks: Vec<OrderSummary>,
107    pub min_order_size: String,
108    pub tick_size: String,
109    pub neg_risk: bool,
110    pub hash: String,
111}
112
113/// Signature type for orders
114/// - EOA: Standard Externally Owned Account (default)
115/// - PolyProxy: Polymarket Proxy Wallet
116/// - PolyGnosisSafe: Gnosis Safe Multisig
117#[derive(Debug, Clone, Copy, PartialEq, Eq)]
118#[repr(u8)]
119#[derive(Default)]
120pub enum SignatureType {
121    #[default]
122    EOA = 0,
123    PolyProxy = 1,
124    PolyGnosisSafe = 2,
125}
126
127impl From<SignatureType> for u8 {
128    fn from(sig_type: SignatureType) -> Self {
129        sig_type as u8
130    }
131}
132
133impl From<u8> for SignatureType {
134    fn from(value: u8) -> Self {
135        match value {
136            0 => SignatureType::EOA,
137            1 => SignatureType::PolyProxy,
138            2 => SignatureType::PolyGnosisSafe,
139            _ => SignatureType::EOA, // fallback to default
140        }
141    }
142}
143
144// Custom serialization: always serialize as number
145impl Serialize for SignatureType {
146    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
147    where
148        S: Serializer,
149    {
150        serializer.serialize_u8(*self as u8)
151    }
152}
153
154// Custom deserialization: accept both string and number
155impl<'de> Deserialize<'de> for SignatureType {
156    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
157    where
158        D: Deserializer<'de>,
159    {
160        struct SignatureTypeVisitor;
161
162        impl<'de> serde::de::Visitor<'de> for SignatureTypeVisitor {
163            type Value = SignatureType;
164
165            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
166                formatter
167                    .write_str("a string (EOA, POLY_PROXY, POLY_GNOSIS_SAFE) or a number (0, 1, 2)")
168            }
169
170            fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
171            where
172                E: serde::de::Error,
173            {
174                match value {
175                    0 => Ok(SignatureType::EOA),
176                    1 => Ok(SignatureType::PolyProxy),
177                    2 => Ok(SignatureType::PolyGnosisSafe),
178                    _ => Ok(SignatureType::EOA), // fallback
179                }
180            }
181
182            fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
183            where
184                E: serde::de::Error,
185            {
186                self.visit_u64(value as u64)
187            }
188
189            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
190            where
191                E: serde::de::Error,
192            {
193                match value {
194                    "EOA" => Ok(SignatureType::EOA),
195                    "POLY_PROXY" => Ok(SignatureType::PolyProxy),
196                    "POLY_GNOSIS_SAFE" => Ok(SignatureType::PolyGnosisSafe),
197                    _ => Ok(SignatureType::EOA), // fallback
198                }
199            }
200        }
201
202        deserializer.deserialize_any(SignatureTypeVisitor)
203    }
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize)]
207#[serde(rename_all = "camelCase")]
208pub struct OrderData {
209    pub maker: String,
210    pub taker: String,
211    pub token_id: String,
212    pub maker_amount: String,
213    pub taker_amount: String,
214    pub side: Side,
215    pub fee_rate_bps: String,
216    pub nonce: String,
217    pub signer: String,
218    pub expiration: String,
219    pub signature_type: SignatureType,
220}
221
222#[derive(Debug, Clone, Serialize, Deserialize)]
223#[serde(rename_all = "camelCase")]
224pub struct SignedOrder {
225    pub salt: String,
226    pub maker: String,
227    pub signer: String,
228    pub taker: String,
229    pub token_id: String,
230    pub maker_amount: String,
231    pub taker_amount: String,
232    pub expiration: String,
233    pub nonce: String,
234    pub fee_rate_bps: String,
235    pub side: Side,
236    pub signature_type: SignatureType,
237    pub signature: String,
238}
239
240/// NewOrder is the payload structure for posting orders to the API
241/// It wraps SignedOrder with orderType, owner, and deferExec fields
242#[derive(Debug, Clone, Serialize, Deserialize)]
243#[serde(rename_all = "camelCase")]
244pub struct NewOrder {
245    pub order: NewOrderData,
246    pub owner: String,
247    pub order_type: OrderType,
248    #[serde(default)]
249    pub defer_exec: bool,
250}
251
252#[derive(Debug, Clone, Serialize, Deserialize)]
253#[serde(rename_all = "camelCase")]
254pub struct NewOrderData {
255    // API 要求 salt 为 number;为避免 JS 精度丢失,生成时限制在 2^53-1 范围内
256    // 序列化时使用 i64 以确保 JSON 中是 number 而非字符串
257    pub salt: i64,
258    pub maker: String,
259    pub signer: String,
260    pub taker: String,
261    pub token_id: String,
262    pub maker_amount: String,
263    pub taker_amount: String,
264    pub expiration: String,
265    pub nonce: String,
266    pub fee_rate_bps: String,
267    pub side: Side,
268    pub signature_type: SignatureType,
269    pub signature: String,
270}
271
272// Simple Chain constants
273pub const CHAIN_POLYGON: i32 = 137;
274pub const CHAIN_AMOY: i32 = 80002;
275
276#[derive(Debug, Clone, Serialize, Deserialize)]
277#[serde(rename_all = "camelCase")]
278pub struct UserOrder {
279    pub token_id: String,
280    pub price: f64,
281    pub size: f64,
282    pub side: Side,
283    // 改为必填: fee_rate_bps 由外部传入,不再在创建流程中自动获取
284    pub fee_rate_bps: f64,
285    pub nonce: Option<u64>,
286    pub expiration: Option<u64>,
287    pub taker: Option<String>,
288}
289
290#[derive(Debug, Clone, Serialize, Deserialize)]
291#[serde(rename_all = "camelCase")]
292pub struct UserMarketOrder {
293    pub token_id: String,
294    // 改为必填: 市价单价格需由外部计算并传入
295    pub price: f64,
296    pub amount: f64,
297    pub side: Side,
298    // 改为必填: 由外部传入
299    pub fee_rate_bps: f64,
300    pub nonce: Option<u64>,
301    pub taker: Option<String>,
302    // order_type 改为必填: 市价单必须明确 FOK/FAK (或未来支持的其他类型)
303    pub order_type: OrderType,
304}
305
306#[derive(Debug, Clone, Serialize, Deserialize)]
307#[serde(rename_all = "camelCase")]
308pub struct Order {
309    pub id: Option<String>,
310    pub salt: Option<String>,
311    pub maker: Option<String>,
312    pub signer: Option<String>,
313    pub taker: Option<String>,
314    pub token_id: Option<String>,
315    pub maker_amount: Option<String>,
316    pub taker_amount: Option<String>,
317    pub price: Option<String>,
318    pub size: Option<String>,
319    pub expiration: Option<String>,
320    pub nonce: Option<String>,
321    pub fee_rate_bps: Option<String>,
322    pub side: Option<Side>,
323    pub signature_type: Option<SignatureType>,
324    pub signature: Option<String>,
325    pub status: Option<OrderStatus>,
326    pub metadata: Option<serde_json::Value>,
327}
328
329/// Response from GET /order endpoint
330/// According to API docs: https://docs.polymarket.com/developers/CLOB/orders/get-order
331/// Matches TypeScript SDK's OpenOrder interface
332#[derive(Debug, Clone, Serialize, Deserialize)]
333#[serde(rename_all = "snake_case")]
334pub struct OpenOrder {
335    pub id: String,
336    pub status: String,
337    pub owner: String,
338    pub maker_address: String,
339    pub market: String,
340    pub asset_id: String,
341    pub side: String,
342    pub original_size: String,
343    pub size_matched: String,
344    pub price: String,
345    pub associate_trades: Vec<String>,
346    pub outcome: String,
347    pub created_at: u64,
348    pub expiration: String,
349    #[serde(rename = "type", alias = "order_type")]
350    pub order_type: String,
351}
352
353/// Response from POST /order endpoint
354/// According to API docs: https://docs.polymarket.com/developers/CLOB/orders/create-order
355#[derive(Debug, Clone, Serialize, Deserialize)]
356#[serde(rename_all = "camelCase")]
357pub struct OrderResponse {
358    /// Boolean indicating if server-side error occurred (success = false -> server-side error)
359    pub success: bool,
360
361    /// Error message in case of unsuccessful placement
362    #[serde(rename = "errorMsg", default)]
363    pub error_msg: String,
364
365    /// ID of the order
366    #[serde(rename = "orderID", alias = "orderId", default)]
367    pub order_id: String,
368
369    /// Hash of settlement transaction if order was marketable and triggered a match
370    /// API docs call this "orderHashes", but TypeScript SDK uses "transactionsHashes"
371    #[serde(rename = "orderHashes", alias = "transactionsHashes", default)]
372    pub order_hashes: Vec<String>,
373
374    /// Order status: "matched", "live", "delayed", "unmatched"
375    #[serde(default)]
376    pub status: Option<String>,
377
378    /// Taking amount (not in official API docs, but returned by some endpoints)
379    #[serde(default)]
380    pub taking_amount: Option<String>,
381
382    /// Making amount (not in official API docs, but returned by some endpoints)
383    #[serde(default)]
384    pub making_amount: Option<String>,
385}
386
387#[derive(Debug, Clone, PartialEq)]
388pub enum OrderStatus {
389    OPEN,
390    FILLED,
391    CANCELLED,
392    OTHER(String),
393}
394
395impl fmt::Display for OrderStatus {
396    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397        let s = match self {
398            OrderStatus::OPEN => "OPEN",
399            OrderStatus::FILLED => "FILLED",
400            OrderStatus::CANCELLED => "CANCELLED",
401            OrderStatus::OTHER(v) => v.as_str(),
402        };
403        write!(f, "{}", s)
404    }
405}
406
407impl Serialize for OrderStatus {
408    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
409    where
410        S: Serializer,
411    {
412        serializer.serialize_str(&self.to_string())
413    }
414}
415
416impl<'de> Deserialize<'de> for OrderStatus {
417    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
418    where
419        D: Deserializer<'de>,
420    {
421        let s = String::deserialize(deserializer)?;
422        match s.as_str() {
423            "OPEN" | "open" => Ok(OrderStatus::OPEN),
424            "FILLED" | "filled" => Ok(OrderStatus::FILLED),
425            "CANCELLED" | "CANCELED" | "cancelled" | "canceled" => Ok(OrderStatus::CANCELLED),
426            other => Ok(OrderStatus::OTHER(other.to_string())),
427        }
428    }
429}
430
431#[derive(Debug, Clone, Serialize, Deserialize)]
432pub struct MakerOrder {
433    pub order_id: String,
434    pub owner: String,
435    pub maker_address: String,
436    pub matched_amount: String,
437    pub price: String,
438    pub fee_rate_bps: String,
439    pub asset_id: String,
440    pub outcome: String,
441    pub side: Side,
442}
443
444#[derive(Debug, Clone, Serialize, Deserialize)]
445pub struct Trade {
446    pub id: String,
447    pub taker_order_id: String,
448    pub market: String,
449    pub asset_id: String,
450    pub side: Side,
451    pub size: String,
452    pub fee_rate_bps: String,
453    pub price: String,
454    pub status: String,
455    pub match_time: String,
456    pub last_update: String,
457    pub outcome: String,
458    pub bucket_index: i64,
459    pub owner: String,
460    pub maker_address: String,
461    pub maker_orders: Vec<MakerOrder>,
462    pub transaction_hash: String,
463    pub trader_side: String,
464}
465
466#[derive(Debug, Clone, Serialize, Deserialize)]
467#[serde(rename_all = "camelCase")]
468pub struct Notification {
469    pub id: Option<String>,
470    pub title: Option<String>,
471    pub body: Option<String>,
472    pub data: Option<serde_json::Value>,
473    pub created_at: Option<String>,
474    pub read: Option<bool>,
475}
476
477#[derive(Debug, Clone, Serialize, Deserialize)]
478#[serde(rename_all = "camelCase")]
479pub struct Reward {
480    pub market: Option<String>,
481    pub amount: Option<String>,
482    pub timestamp: Option<String>,
483    pub metadata: Option<serde_json::Value>,
484}
485
486#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
487pub struct MarketPrice {
488    /// timestamp (ms or unix seconds depending on API)
489    pub t: i64,
490    /// price
491    pub p: f64,
492}
493
494#[derive(Debug, Clone, Serialize, Deserialize)]
495#[serde(rename_all = "camelCase")]
496pub struct BuilderTrade {
497    pub id: String,
498    pub trade_type: String,
499    pub taker_order_hash: String,
500    pub builder: String,
501    pub market: String,
502    pub asset_id: String,
503    pub side: String,
504    pub size: String,
505    pub size_usdc: String,
506    pub price: String,
507    pub status: String,
508    pub outcome: String,
509    pub outcome_index: i64,
510    pub owner: String,
511    pub maker: String,
512    pub transaction_hash: String,
513    pub match_time: String,
514    pub bucket_index: i64,
515    pub fee: String,
516    pub fee_usdc: String,
517    pub err_msg: Option<String>,
518    pub created_at: Option<String>,
519    pub updated_at: Option<String>,
520}