drm_core/exchange/
factory.rs1use 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}