drm_core/exchange/
factory.rs

1use std::env;
2use std::str::FromStr;
3
4use crate::error::DrmError;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7pub enum ExchangeId {
8    Polymarket,
9    Opinion,
10    Limitless,
11}
12
13impl ExchangeId {
14    pub fn as_str(&self) -> &'static str {
15        match self {
16            ExchangeId::Polymarket => "polymarket",
17            ExchangeId::Opinion => "opinion",
18            ExchangeId::Limitless => "limitless",
19        }
20    }
21}
22
23impl FromStr for ExchangeId {
24    type Err = ();
25
26    fn from_str(s: &str) -> Result<Self, Self::Err> {
27        match s.to_lowercase().as_str() {
28            "polymarket" => Ok(ExchangeId::Polymarket),
29            "opinion" => Ok(ExchangeId::Opinion),
30            "limitless" => Ok(ExchangeId::Limitless),
31            _ => Err(()),
32        }
33    }
34}
35
36impl ExchangeId {
37    pub fn env_prefix(&self) -> &'static str {
38        match self {
39            ExchangeId::Polymarket => "POLYMARKET",
40            ExchangeId::Opinion => "OPINION",
41            ExchangeId::Limitless => "LIMITLESS",
42        }
43    }
44
45    pub fn required_env_vars(&self) -> Vec<&'static str> {
46        match self {
47            ExchangeId::Polymarket => vec!["POLYMARKET_PRIVATE_KEY", "POLYMARKET_FUNDER"],
48            ExchangeId::Opinion => vec![
49                "OPINION_API_KEY",
50                "OPINION_PRIVATE_KEY",
51                "OPINION_MULTI_SIG_ADDR",
52            ],
53            ExchangeId::Limitless => vec!["LIMITLESS_PRIVATE_KEY"],
54        }
55    }
56}
57
58pub fn list_exchanges() -> Vec<ExchangeId> {
59    vec![
60        ExchangeId::Polymarket,
61        ExchangeId::Opinion,
62        ExchangeId::Limitless,
63    ]
64}
65
66pub fn list_exchange_names() -> Vec<&'static str> {
67    vec!["polymarket", "opinion", "limitless"]
68}
69
70pub fn validate_env_config(exchange: ExchangeId) -> Result<(), DrmError> {
71    let required = exchange.required_env_vars();
72    let missing: Vec<_> = required
73        .iter()
74        .filter(|var| env::var(var).is_err())
75        .collect();
76
77    if !missing.is_empty() {
78        return Err(DrmError::InvalidInput(format!(
79            "Missing required environment variables for {}: {:?}",
80            exchange.as_str(),
81            missing
82        )));
83    }
84
85    Ok(())
86}
87
88pub fn validate_private_key(key: &str) -> Result<(), DrmError> {
89    if key.is_empty() {
90        return Err(DrmError::InvalidInput(
91            "Private key cannot be empty".to_string(),
92        ));
93    }
94
95    let clean_key = key.strip_prefix("0x").unwrap_or(key);
96
97    if clean_key.len() != 64 {
98        return Err(DrmError::InvalidInput(format!(
99            "Invalid private key length. Expected 64 hex characters, got {}",
100            clean_key.len()
101        )));
102    }
103
104    if !clean_key.chars().all(|c| c.is_ascii_hexdigit()) {
105        return Err(DrmError::InvalidInput(
106            "Invalid private key format. Must be valid hexadecimal".to_string(),
107        ));
108    }
109
110    Ok(())
111}
112
113pub fn get_env_var(name: &str) -> Option<String> {
114    env::var(name).ok()
115}
116
117pub fn get_env_var_or(name: &str, default: &str) -> String {
118    env::var(name).unwrap_or_else(|_| default.to_string())
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    #[test]
126    fn test_exchange_id_from_str() {
127        assert_eq!(
128            ExchangeId::from_str("polymarket"),
129            Ok(ExchangeId::Polymarket)
130        );
131        assert_eq!(
132            ExchangeId::from_str("POLYMARKET"),
133            Ok(ExchangeId::Polymarket)
134        );
135        assert_eq!(ExchangeId::from_str("opinion"), Ok(ExchangeId::Opinion));
136        assert_eq!(ExchangeId::from_str("limitless"), Ok(ExchangeId::Limitless));
137        assert_eq!(ExchangeId::from_str("unknown"), Err(()));
138    }
139
140    #[test]
141    fn test_list_exchanges() {
142        let exchanges = list_exchanges();
143        assert_eq!(exchanges.len(), 3);
144    }
145
146    #[test]
147    fn test_validate_private_key_valid() {
148        let valid_key = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
149        assert!(validate_private_key(valid_key).is_ok());
150
151        let valid_key_no_prefix =
152            "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
153        assert!(validate_private_key(valid_key_no_prefix).is_ok());
154    }
155
156    #[test]
157    fn test_validate_private_key_invalid() {
158        assert!(validate_private_key("").is_err());
159        assert!(validate_private_key("0x1234").is_err());
160        assert!(validate_private_key("not_hex_at_all_gg").is_err());
161    }
162}