Skip to main content

polyoxide_clob/ws/
subscription.rs

1//! WebSocket subscription message types.
2
3use serde::{Deserialize, Serialize};
4
5use super::auth::ApiCredentials;
6
7/// WebSocket endpoint URL for market channel
8pub const WS_MARKET_URL: &str = "wss://ws-subscriptions-clob.polymarket.com/ws/market";
9
10/// WebSocket endpoint URL for user channel
11pub const WS_USER_URL: &str = "wss://ws-subscriptions-clob.polymarket.com/ws/user";
12
13/// Channel type for WebSocket subscription
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
15#[serde(rename_all = "lowercase")]
16pub enum ChannelType {
17    /// Market channel for public order book and price updates
18    Market,
19    /// User channel for authenticated order and trade updates
20    User,
21}
22
23/// Subscription message for market channel
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct MarketSubscription {
26    /// Asset IDs (token IDs) to subscribe to
27    pub assets_ids: Vec<String>,
28    /// Channel type (always "market")
29    #[serde(rename = "type")]
30    pub channel_type: ChannelType,
31}
32
33impl MarketSubscription {
34    /// Create a new market subscription
35    pub fn new(assets_ids: Vec<String>) -> Self {
36        Self {
37            assets_ids,
38            channel_type: ChannelType::Market,
39        }
40    }
41}
42
43/// Subscription message for user channel
44#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct UserSubscription {
46    /// Market condition IDs to subscribe to
47    pub markets: Vec<String>,
48    /// Authentication credentials
49    pub auth: ApiCredentials,
50    /// Channel type (always "user")
51    #[serde(rename = "type")]
52    pub channel_type: ChannelType,
53}
54
55impl UserSubscription {
56    /// Create a new user subscription
57    pub fn new(markets: Vec<String>, credentials: ApiCredentials) -> Self {
58        Self {
59            markets,
60            auth: credentials,
61            channel_type: ChannelType::User,
62        }
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn channel_type_serialization() {
72        let market = serde_json::to_value(ChannelType::Market).unwrap();
73        let user = serde_json::to_value(ChannelType::User).unwrap();
74
75        assert_eq!(market, "market");
76        assert_eq!(user, "user");
77    }
78
79    #[test]
80    fn channel_type_deserialization() {
81        let market: ChannelType = serde_json::from_str("\"market\"").unwrap();
82        let user: ChannelType = serde_json::from_str("\"user\"").unwrap();
83
84        assert_eq!(market, ChannelType::Market);
85        assert_eq!(user, ChannelType::User);
86    }
87
88    #[test]
89    fn channel_type_rejects_uppercase() {
90        let result = serde_json::from_str::<ChannelType>("\"MARKET\"");
91        assert!(result.is_err(), "Should reject uppercase channel type");
92    }
93
94    #[test]
95    fn market_subscription_new_sets_channel_type() {
96        let sub = MarketSubscription::new(vec!["asset1".into(), "asset2".into()]);
97        assert_eq!(sub.channel_type, ChannelType::Market);
98        assert_eq!(sub.assets_ids.len(), 2);
99        assert_eq!(sub.assets_ids[0], "asset1");
100        assert_eq!(sub.assets_ids[1], "asset2");
101    }
102
103    #[test]
104    fn market_subscription_serialization() {
105        let sub = MarketSubscription::new(vec!["token123".into()]);
106        let json = serde_json::to_value(&sub).unwrap();
107
108        assert_eq!(json["type"], "market");
109        assert_eq!(json["assets_ids"][0], "token123");
110    }
111
112    #[test]
113    fn market_subscription_empty_assets() {
114        let sub = MarketSubscription::new(vec![]);
115        let json = serde_json::to_value(&sub).unwrap();
116
117        assert_eq!(json["type"], "market");
118        assert!(json["assets_ids"].as_array().unwrap().is_empty());
119    }
120
121    #[test]
122    fn user_subscription_new_sets_channel_type() {
123        let creds = ApiCredentials::new("key", "secret", "pass");
124        let sub = UserSubscription::new(vec!["cond1".into()], creds);
125        assert_eq!(sub.channel_type, ChannelType::User);
126        assert_eq!(sub.markets.len(), 1);
127        assert_eq!(sub.markets[0], "cond1");
128    }
129
130    #[test]
131    fn user_subscription_serialization() {
132        let creds = ApiCredentials::new("my_key", "my_secret", "my_pass");
133        let sub = UserSubscription::new(vec!["market1".into(), "market2".into()], creds);
134        let json = serde_json::to_value(&sub).unwrap();
135
136        assert_eq!(json["type"], "user");
137        assert_eq!(json["markets"][0], "market1");
138        assert_eq!(json["markets"][1], "market2");
139        assert_eq!(json["auth"]["apiKey"], "my_key");
140        assert_eq!(json["auth"]["secret"], "my_secret");
141        assert_eq!(json["auth"]["passphrase"], "my_pass");
142    }
143
144    #[test]
145    fn ws_url_constants() {
146        assert!(WS_MARKET_URL.starts_with("wss://"));
147        assert!(WS_MARKET_URL.contains("market"));
148        assert!(WS_USER_URL.starts_with("wss://"));
149        assert!(WS_USER_URL.contains("user"));
150    }
151}