1use std::time::Duration;
2
3#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
8#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
9#[serde(rename_all = "lowercase")]
10pub enum MarketStatusFilter {
11 Active,
12 Closed,
13 Resolved,
14 All,
15}
16
17impl std::fmt::Display for MarketStatusFilter {
18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 match self {
20 MarketStatusFilter::Active => write!(f, "active"),
21 MarketStatusFilter::Closed => write!(f, "closed"),
22 MarketStatusFilter::Resolved => write!(f, "resolved"),
23 MarketStatusFilter::All => write!(f, "all"),
24 }
25 }
26}
27
28impl std::str::FromStr for MarketStatusFilter {
29 type Err = String;
30
31 fn from_str(s: &str) -> Result<Self, Self::Err> {
32 match s.to_lowercase().as_str() {
33 "active" | "open" => Ok(MarketStatusFilter::Active),
34 "closed" | "inactive" | "paused" => Ok(MarketStatusFilter::Closed),
35 "resolved" | "settled" | "determined" | "finalized" => Ok(MarketStatusFilter::Resolved),
36 "all" => Ok(MarketStatusFilter::All),
37 _ => Err(format!("Unknown market status filter: {}", s)),
38 }
39 }
40}
41
42impl From<crate::models::MarketStatus> for MarketStatusFilter {
43 fn from(s: crate::models::MarketStatus) -> Self {
44 match s {
45 crate::models::MarketStatus::Active => MarketStatusFilter::Active,
46 crate::models::MarketStatus::Closed => MarketStatusFilter::Closed,
47 crate::models::MarketStatus::Resolved => MarketStatusFilter::Resolved,
48 }
49 }
50}
51
52#[derive(Debug, Clone)]
53pub struct ExchangeConfig {
54 pub timeout: Duration,
55 pub rate_limit_per_second: u32,
56 pub max_retries: u32,
57 pub retry_delay: Duration,
58 pub verbose: bool,
59}
60
61impl Default for ExchangeConfig {
62 fn default() -> Self {
63 Self {
64 timeout: Duration::from_secs(30),
65 rate_limit_per_second: 10,
66 max_retries: 3,
67 retry_delay: Duration::from_secs(1),
68 verbose: false,
69 }
70 }
71}
72
73impl ExchangeConfig {
74 pub fn new() -> Self {
75 Self::default()
76 }
77
78 pub fn with_timeout(mut self, timeout: Duration) -> Self {
79 self.timeout = timeout;
80 self
81 }
82
83 pub fn with_rate_limit(mut self, requests_per_second: u32) -> Self {
84 self.rate_limit_per_second = requests_per_second;
85 self
86 }
87
88 pub fn with_retries(mut self, max_retries: u32, delay: Duration) -> Self {
89 self.max_retries = max_retries;
90 self.retry_delay = delay;
91 self
92 }
93
94 pub fn with_verbose(mut self, verbose: bool) -> Self {
95 self.verbose = verbose;
96 self
97 }
98}
99
100#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
101#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
102pub struct FetchMarketsParams {
103 pub limit: Option<usize>,
104 #[serde(default)]
106 pub cursor: Option<String>,
107 #[serde(default)]
110 pub status: Option<MarketStatusFilter>,
111 #[serde(default)]
115 pub series_id: Option<String>,
116 #[serde(default)]
123 pub event_id: Option<String>,
124}
125
126#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
127#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
128pub struct FetchOrdersParams {
129 pub market_id: Option<String>,
130}
131
132#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
133#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
134pub struct FetchUserActivityParams {
135 pub address: String,
136 pub limit: Option<usize>,
137}
138
139#[derive(Debug, Clone)]
173pub struct PolymarketCredentials {
174 pub private_key: Option<String>,
175 pub funder: Option<String>,
176 pub api_key: Option<String>,
177 pub api_secret: Option<String>,
178 pub api_passphrase: Option<String>,
179 pub signature_type: String,
180}
181
182impl PolymarketCredentials {
183 pub fn from_fields(
189 private_key: Option<String>,
190 funder: Option<String>,
191 api_key: Option<String>,
192 api_secret: Option<String>,
193 api_passphrase: Option<String>,
194 signature_type: Option<String>,
195 ) -> Self {
196 let resolved_signature_type = signature_type.unwrap_or_else(|| {
198 if funder.is_some() {
199 "GnosisSafe".to_string()
200 } else {
201 "EOA".to_string()
202 }
203 });
204
205 Self {
206 private_key,
207 funder,
208 api_key,
209 api_secret,
210 api_passphrase,
211 signature_type: resolved_signature_type,
212 }
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219 use crate::models::MarketStatus;
220
221 #[test]
222 fn market_status_filter_from_str() {
223 assert_eq!(
224 "active".parse::<MarketStatusFilter>().unwrap(),
225 MarketStatusFilter::Active
226 );
227 assert_eq!(
228 "open".parse::<MarketStatusFilter>().unwrap(),
229 MarketStatusFilter::Active
230 );
231 assert_eq!(
232 "closed".parse::<MarketStatusFilter>().unwrap(),
233 MarketStatusFilter::Closed
234 );
235 assert_eq!(
236 "resolved".parse::<MarketStatusFilter>().unwrap(),
237 MarketStatusFilter::Resolved
238 );
239 assert_eq!(
240 "settled".parse::<MarketStatusFilter>().unwrap(),
241 MarketStatusFilter::Resolved
242 );
243 assert_eq!(
244 "all".parse::<MarketStatusFilter>().unwrap(),
245 MarketStatusFilter::All
246 );
247 assert_eq!(
248 "ALL".parse::<MarketStatusFilter>().unwrap(),
249 MarketStatusFilter::All
250 );
251 assert!("invalid".parse::<MarketStatusFilter>().is_err());
252 }
253
254 #[test]
255 fn market_status_filter_display() {
256 assert_eq!(MarketStatusFilter::Active.to_string(), "active");
257 assert_eq!(MarketStatusFilter::Closed.to_string(), "closed");
258 assert_eq!(MarketStatusFilter::Resolved.to_string(), "resolved");
259 assert_eq!(MarketStatusFilter::All.to_string(), "all");
260 }
261
262 #[test]
263 fn market_status_filter_from_market_status() {
264 assert_eq!(
265 MarketStatusFilter::from(MarketStatus::Active),
266 MarketStatusFilter::Active
267 );
268 assert_eq!(
269 MarketStatusFilter::from(MarketStatus::Closed),
270 MarketStatusFilter::Closed
271 );
272 assert_eq!(
273 MarketStatusFilter::from(MarketStatus::Resolved),
274 MarketStatusFilter::Resolved
275 );
276 }
277
278 #[test]
279 fn market_status_filter_serde_roundtrip() {
280 let filter = MarketStatusFilter::All;
281 let json = serde_json::to_string(&filter).unwrap();
282 assert_eq!(json, "\"all\"");
283 let parsed: MarketStatusFilter = serde_json::from_str(&json).unwrap();
284 assert_eq!(parsed, MarketStatusFilter::All);
285 }
286
287 #[test]
288 fn fetch_markets_params_default_status_is_none() {
289 let params = FetchMarketsParams::default();
290 assert!(params.status.is_none());
291 }
292
293 #[test]
294 fn fetch_markets_params_serde_with_all_status() {
295 let params = FetchMarketsParams {
296 status: Some(MarketStatusFilter::All),
297 ..Default::default()
298 };
299 let json = serde_json::to_value(¶ms).unwrap();
300 assert_eq!(json["status"], "all");
301
302 let parsed: FetchMarketsParams = serde_json::from_value(json).unwrap();
303 assert_eq!(parsed.status, Some(MarketStatusFilter::All));
304 }
305}
306
307#[derive(Debug, Clone)]
308pub struct KalshiCredentials {
309 pub api_key_id: String,
310 pub private_key: String,
311}
312
313#[derive(Debug, Clone)]
314pub struct OpinionCredentials {
315 pub api_key: String,
316 pub private_key: String,
317 pub multi_sig_addr: String,
318}