Skip to main content

px_core/exchange/
config.rs

1use std::time::Duration;
2
3use crate::models::MarketStatus;
4
5#[derive(Debug, Clone)]
6pub struct ExchangeConfig {
7    pub timeout: Duration,
8    pub rate_limit_per_second: u32,
9    pub max_retries: u32,
10    pub retry_delay: Duration,
11    pub verbose: bool,
12}
13
14impl Default for ExchangeConfig {
15    fn default() -> Self {
16        Self {
17            timeout: Duration::from_secs(30),
18            rate_limit_per_second: 10,
19            max_retries: 3,
20            retry_delay: Duration::from_secs(1),
21            verbose: false,
22        }
23    }
24}
25
26impl ExchangeConfig {
27    pub fn new() -> Self {
28        Self::default()
29    }
30
31    pub fn with_timeout(mut self, timeout: Duration) -> Self {
32        self.timeout = timeout;
33        self
34    }
35
36    pub fn with_rate_limit(mut self, requests_per_second: u32) -> Self {
37        self.rate_limit_per_second = requests_per_second;
38        self
39    }
40
41    pub fn with_retries(mut self, max_retries: u32, delay: Duration) -> Self {
42        self.max_retries = max_retries;
43        self.retry_delay = delay;
44        self
45    }
46
47    pub fn with_verbose(mut self, verbose: bool) -> Self {
48        self.verbose = verbose;
49        self
50    }
51}
52
53#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
54#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
55pub struct FetchMarketsParams {
56    pub limit: Option<usize>,
57    /// Exchange-specific cursor (offset, page number, or cursor string)
58    #[serde(default)]
59    pub cursor: Option<String>,
60    /// Filter by market status. Defaults to Active at the exchange level when None.
61    #[serde(default)]
62    pub status: Option<MarketStatus>,
63}
64
65#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
66#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
67pub struct FetchOrdersParams {
68    pub market_id: Option<String>,
69}
70
71#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
72#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
73pub struct FetchUserActivityParams {
74    pub address: String,
75    pub limit: Option<usize>,
76}
77
78// ============================================================================
79// Customer Credentials (for per-customer exchange authentication)
80//
81// These credential structs hold per-exchange authentication data.
82// Users provide their own exchange credentials to trade directly.
83// ============================================================================
84
85// TODO(wallet-support): Current wallet support and planned improvements.
86//
87// **Supported today:**
88// - Raw private key + optional funder: covers EOA (sig_type=0), Proxy (sig_type=1),
89//   GnosisSafe (sig_type=2). Server signs orders with the private key.
90// - CLOB API credentials (api_key, api_secret, api_passphrase): if provided alongside
91//   the private key, skips the expensive init_trading() derivation step.
92//
93// **SDK-side helpers to add (no server changes needed):**
94// 1. CLOB credential derivation helper — SDK method that takes a wallet signer, signs a
95//    ClobAuth EIP-712 message, calls Polymarket's /auth/derive-api-key, and returns
96//    {apiKey, apiSecret, apiPassphrase}. Runs client-side.
97//    Useful for both direct traders (automate credential setup) and platform builders
98//    (onboard end-users without manual Polymarket UI steps).
99//
100// 2. Approval/allowance helpers — SDK methods to check and set the 6 Polymarket token
101//    approvals (USDC + CTF for CTF Exchange, NegRisk CTF Exchange, NegRisk Adapter).
102//    Expose via client.approvals.check() and client.approvals.setAll(). Should also
103//    surface clear errors when orders fail due to missing approvals.
104//
105// **Future server-side additions (lower priority):**
106// 3. Pre-signed order endpoint — POST /orders/signed that accepts orders already signed
107//    client-side (EIP-712). Enables browser wallets (MetaMask, WalletConnect), hardware
108//    wallets (Ledger, Trezor), and Privy embedded wallets to trade without exposing
109//    private keys to any server. The server just forwards the pre-signed order to the
110//    exchange CLOB. Useful for both Mode A and Mode B.
111#[derive(Debug, Clone)]
112pub struct PolymarketCredentials {
113    pub private_key: Option<String>,
114    pub funder: Option<String>,
115    pub api_key: Option<String>,
116    pub api_secret: Option<String>,
117    pub api_passphrase: Option<String>,
118    pub signature_type: String,
119}
120
121impl PolymarketCredentials {
122    /// Create credentials from individual field values (e.g., from DynamoDB).
123    ///
124    /// Auto-detection: If signature_type is not provided:
125    /// - funder present → GnosisSafe (type 2)
126    /// - funder absent → EOA (type 0)
127    pub fn from_fields(
128        private_key: Option<String>,
129        funder: Option<String>,
130        api_key: Option<String>,
131        api_secret: Option<String>,
132        api_passphrase: Option<String>,
133        signature_type: Option<String>,
134    ) -> Self {
135        // Auto-detect: funder present without explicit type → GnosisSafe
136        let resolved_signature_type = signature_type.unwrap_or_else(|| {
137            if funder.is_some() {
138                "GnosisSafe".to_string()
139            } else {
140                "EOA".to_string()
141            }
142        });
143
144        Self {
145            private_key,
146            funder,
147            api_key,
148            api_secret,
149            api_passphrase,
150            signature_type: resolved_signature_type,
151        }
152    }
153}
154
155#[derive(Debug, Clone)]
156pub struct KalshiCredentials {
157    pub api_key_id: String,
158    pub private_key: String,
159}
160
161#[derive(Debug, Clone)]
162pub struct OpinionCredentials {
163    pub api_key: String,
164    pub private_key: String,
165    pub multi_sig_addr: String,
166}