1use polyoxide_core::{
2 HttpClient, HttpClientBuilder, RateLimiter, RetryConfig, DEFAULT_POOL_SIZE, DEFAULT_TIMEOUT_MS,
3};
4
5use crate::{
6 api::{
7 builders::BuildersApi,
8 health::Health,
9 holders::Holders,
10 leaderboard::LeaderboardApi,
11 live_volume::LiveVolumeApi,
12 open_interest::OpenInterestApi,
13 trades::Trades,
14 users::{UserApi, UserTraded},
15 },
16 error::DataApiError,
17};
18
19const DEFAULT_BASE_URL: &str = "https://data-api.polymarket.com";
20
21#[derive(Clone)]
23pub struct DataApi {
24 pub(crate) http_client: HttpClient,
25}
26
27impl DataApi {
28 pub fn new() -> Result<Self, DataApiError> {
30 Self::builder().build()
31 }
32
33 pub fn builder() -> DataApiBuilder {
35 DataApiBuilder::new()
36 }
37
38 pub fn health(&self) -> Health {
40 Health {
41 http_client: self.http_client.clone(),
42 }
43 }
44
45 pub fn user(&self, user_address: impl Into<String>) -> UserApi {
47 UserApi {
48 http_client: self.http_client.clone(),
49 user_address: user_address.into(),
50 }
51 }
52
53 pub fn positions(&self, user_address: impl Into<String>) -> UserApi {
55 self.user(user_address)
56 }
57
58 pub fn traded(&self, user_address: impl Into<String>) -> Traded {
60 Traded {
61 user_api: self.user(user_address),
62 }
63 }
64
65 pub fn trades(&self) -> Trades {
67 Trades {
68 http_client: self.http_client.clone(),
69 }
70 }
71
72 pub fn holders(&self) -> Holders {
74 Holders {
75 http_client: self.http_client.clone(),
76 }
77 }
78
79 pub fn open_interest(&self) -> OpenInterestApi {
81 OpenInterestApi {
82 http_client: self.http_client.clone(),
83 }
84 }
85
86 pub fn live_volume(&self) -> LiveVolumeApi {
88 LiveVolumeApi {
89 http_client: self.http_client.clone(),
90 }
91 }
92
93 pub fn builders(&self) -> BuildersApi {
95 BuildersApi {
96 http_client: self.http_client.clone(),
97 }
98 }
99
100 pub fn leaderboard(&self) -> LeaderboardApi {
102 LeaderboardApi {
103 http_client: self.http_client.clone(),
104 }
105 }
106}
107
108pub struct DataApiBuilder {
110 base_url: String,
111 timeout_ms: u64,
112 pool_size: usize,
113 retry_config: Option<RetryConfig>,
114}
115
116impl DataApiBuilder {
117 fn new() -> Self {
118 Self {
119 base_url: DEFAULT_BASE_URL.to_string(),
120 timeout_ms: DEFAULT_TIMEOUT_MS,
121 pool_size: DEFAULT_POOL_SIZE,
122 retry_config: None,
123 }
124 }
125
126 pub fn base_url(mut self, url: impl Into<String>) -> Self {
128 self.base_url = url.into();
129 self
130 }
131
132 pub fn timeout_ms(mut self, timeout: u64) -> Self {
134 self.timeout_ms = timeout;
135 self
136 }
137
138 pub fn pool_size(mut self, size: usize) -> Self {
140 self.pool_size = size;
141 self
142 }
143
144 pub fn with_retry_config(mut self, config: RetryConfig) -> Self {
146 self.retry_config = Some(config);
147 self
148 }
149
150 pub fn build(self) -> Result<DataApi, DataApiError> {
152 let mut builder = HttpClientBuilder::new(&self.base_url)
153 .timeout_ms(self.timeout_ms)
154 .pool_size(self.pool_size)
155 .with_rate_limiter(RateLimiter::data_default());
156 if let Some(config) = self.retry_config {
157 builder = builder.with_retry_config(config);
158 }
159 let http_client = builder.build()?;
160
161 Ok(DataApi { http_client })
162 }
163}
164
165impl Default for DataApiBuilder {
166 fn default() -> Self {
167 Self::new()
168 }
169}
170
171pub struct Traded {
173 user_api: UserApi,
174}
175
176impl Traded {
177 pub async fn get(self) -> std::result::Result<UserTraded, DataApiError> {
179 self.user_api.traded().await
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186
187 #[test]
188 fn test_builder_default() {
189 let builder = DataApiBuilder::default();
190 assert_eq!(builder.base_url, DEFAULT_BASE_URL);
191 }
192
193 #[test]
194 fn test_builder_custom_retry_config() {
195 let config = RetryConfig {
196 max_retries: 5,
197 initial_backoff_ms: 1000,
198 max_backoff_ms: 30_000,
199 };
200 let builder = DataApiBuilder::new().with_retry_config(config);
201 let config = builder.retry_config.unwrap();
202 assert_eq!(config.max_retries, 5);
203 assert_eq!(config.initial_backoff_ms, 1000);
204 }
205
206 #[test]
207 fn test_builder_build_success() {
208 let data = DataApi::builder().build();
209 assert!(data.is_ok());
210 }
211}