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}