Skip to main content

polyoxide_clob/api/
rewards.rs

1use polyoxide_core::HttpClient;
2use serde::{Deserialize, Serialize};
3
4use crate::{
5    account::{Credentials, Signer, Wallet},
6    request::{AuthMode, Request},
7};
8
9/// Rewards namespace for liquidity reward operations
10#[derive(Clone)]
11pub struct Rewards {
12    pub(crate) http_client: HttpClient,
13    pub(crate) wallet: Wallet,
14    pub(crate) credentials: Credentials,
15    pub(crate) signer: Signer,
16    pub(crate) chain_id: u64,
17}
18
19impl Rewards {
20    fn l2_auth(&self) -> AuthMode {
21        AuthMode::L2 {
22            address: self.wallet.address(),
23            credentials: self.credentials.clone(),
24            signer: self.signer.clone(),
25        }
26    }
27
28    /// Get user earnings, optionally filtered by day via `.query("day", "YYYY-MM-DD")`
29    pub fn earnings(&self) -> Request<RewardEarnings> {
30        Request::get(
31            self.http_client.clone(),
32            "/rewards/user",
33            self.l2_auth(),
34            self.chain_id,
35        )
36    }
37
38    /// Get user total accumulated earnings
39    pub fn total_earnings(&self) -> Request<RewardTotalEarnings> {
40        Request::get(
41            self.http_client.clone(),
42            "/rewards/user/total",
43            self.l2_auth(),
44            self.chain_id,
45        )
46    }
47
48    /// Get user reward percentages
49    pub fn percentages(&self) -> Request<RewardPercentages> {
50        Request::get(
51            self.http_client.clone(),
52            "/rewards/user/percentages",
53            self.l2_auth(),
54            self.chain_id,
55        )
56    }
57
58    /// Get user earnings broken down by market
59    pub fn market_earnings(&self) -> Request<Vec<RewardMarketEarning>> {
60        Request::get(
61            self.http_client.clone(),
62            "/rewards/user/markets",
63            self.l2_auth(),
64            self.chain_id,
65        )
66    }
67
68    /// Get currently active reward markets
69    pub fn current_markets(&self) -> Request<Vec<RewardMarket>> {
70        Request::get(
71            self.http_client.clone(),
72            "/rewards/markets/current",
73            AuthMode::None,
74            self.chain_id,
75        )
76    }
77
78    /// Get rewards for a specific market
79    pub fn market(&self, condition_id: impl Into<String>) -> Request<RewardMarket> {
80        Request::get(
81            self.http_client.clone(),
82            format!(
83                "/rewards/markets/{}",
84                urlencoding::encode(&condition_id.into())
85            ),
86            AuthMode::None,
87            self.chain_id,
88        )
89    }
90}
91
92/// User earnings response
93#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct RewardEarnings {
95    #[serde(flatten)]
96    pub data: serde_json::Value,
97}
98
99/// User total accumulated earnings
100#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct RewardTotalEarnings {
102    #[serde(flatten)]
103    pub data: serde_json::Value,
104}
105
106/// User reward percentages
107#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct RewardPercentages {
109    #[serde(flatten)]
110    pub data: serde_json::Value,
111}
112
113/// Per-market earnings breakdown
114#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct RewardMarketEarning {
116    #[serde(flatten)]
117    pub data: serde_json::Value,
118}
119
120/// Reward market information
121#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct RewardMarket {
123    #[serde(flatten)]
124    pub data: serde_json::Value,
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130
131    #[test]
132    fn reward_earnings_deserializes() {
133        let json = r#"{"amount": "1.5", "day": "2024-01-15"}"#;
134        let resp: RewardEarnings = serde_json::from_str(json).unwrap();
135        assert_eq!(resp.data["amount"], "1.5");
136        assert_eq!(resp.data["day"], "2024-01-15");
137    }
138
139    #[test]
140    fn reward_total_earnings_deserializes() {
141        let json = r#"{"total": "42.0"}"#;
142        let resp: RewardTotalEarnings = serde_json::from_str(json).unwrap();
143        assert_eq!(resp.data["total"], "42.0");
144    }
145
146    #[test]
147    fn reward_percentages_deserializes() {
148        let json = r#"{"maker": "0.5", "taker": "0.3"}"#;
149        let resp: RewardPercentages = serde_json::from_str(json).unwrap();
150        assert_eq!(resp.data["maker"], "0.5");
151    }
152
153    #[test]
154    fn reward_market_earning_list_deserializes() {
155        let json = r#"[
156            {"condition_id": "0xabc", "amount": "10.0"},
157            {"condition_id": "0xdef", "amount": "5.0"}
158        ]"#;
159        let resp: Vec<RewardMarketEarning> = serde_json::from_str(json).unwrap();
160        assert_eq!(resp.len(), 2);
161        assert_eq!(resp[0].data["condition_id"], "0xabc");
162    }
163
164    #[test]
165    fn reward_market_deserializes() {
166        let json = r#"{"condition_id": "0xabc", "reward_rate": "0.01"}"#;
167        let resp: RewardMarket = serde_json::from_str(json).unwrap();
168        assert_eq!(resp.data["condition_id"], "0xabc");
169    }
170
171    #[test]
172    fn reward_market_list_deserializes() {
173        let json = r#"[{"condition_id": "0xabc"}, {"condition_id": "0xdef"}]"#;
174        let resp: Vec<RewardMarket> = serde_json::from_str(json).unwrap();
175        assert_eq!(resp.len(), 2);
176    }
177
178    #[test]
179    fn reward_earnings_empty_object_deserializes() {
180        let json = r#"{}"#;
181        let resp: RewardEarnings = serde_json::from_str(json).unwrap();
182        assert!(resp.data.is_object());
183    }
184}