koios_sdk/api/pool.rs
1use crate::{
2 error::Result,
3 models::{
4 pool::{PoolDelegator, PoolDelegatorsHistory, PoolInfo, PoolList, PoolSnapshot},
5 requests::PoolIdsRequest,
6 PoolHistoryInfo, PoolIdsOptionalRequest, PoolMetadataInfo, PoolRegistration, PoolRelay,
7 PoolUpdate, PoolVotes,
8 },
9 types::{EpochNo, PoolBech32},
10 Client,
11};
12use urlencoding::encode;
13
14impl Client {
15 /// Get list of brief info for all pools
16 ///
17 /// # Examples
18 ///
19 /// ```no_run
20 /// use koios_sdk::Client;
21 ///
22 /// #[tokio::main]
23 /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
24 /// let client = Client::new()?;
25 /// let pools = client.get_pool_list().await?;
26 /// println!("Pool list: {:?}", pools);
27 /// Ok(())
28 /// }
29 /// ```
30 pub async fn get_pool_list(&self) -> Result<Vec<PoolList>> {
31 self.get("/pool_list").await
32 }
33
34 /// Get current pool statuses and details for a specified list of pool ids
35 ///
36 /// # Arguments
37 ///
38 /// * `pool_ids` - List of pool IDs to query
39 ///
40 /// # Examples
41 ///
42 /// ```no_run
43 /// use koios_sdk::Client;
44 ///
45 /// #[tokio::main]
46 /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
47 /// let client = Client::new()?;
48 /// let pool_ids = vec![
49 /// "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy".to_string()
50 /// ];
51 /// let info = client.get_pool_info(&pool_ids).await?;
52 /// println!("Pool info: {:?}", info);
53 /// Ok(())
54 /// }
55 /// ```
56 pub async fn get_pool_info(&self, pool_ids: &[String]) -> Result<Vec<PoolInfo>> {
57 let request = PoolIdsRequest::new(pool_ids.to_vec());
58 self.post("/pool_info", &request).await
59 }
60
61 /// Get Mark, Set and Go stake snapshots for the selected pool
62 ///
63 /// # Arguments
64 ///
65 /// * `pool_bech32` - Pool ID in bech32 format
66 ///
67 /// # Examples
68 ///
69 /// ```no_run
70 /// use koios_sdk::Client;
71 /// use koios_sdk::types::PoolBech32;
72 ///
73 /// #[tokio::main]
74 /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
75 /// let client = Client::new()?;
76 /// let pool_id = PoolBech32::new(
77 /// "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"
78 /// );
79 /// let snapshot = client.get_pool_stake_snapshot(&pool_id).await?;
80 /// println!("Pool stake snapshot: {:?}", snapshot);
81 /// Ok(())
82 /// }
83 /// ```
84 pub async fn get_pool_stake_snapshot(
85 &self,
86 pool_bech32: &PoolBech32,
87 ) -> Result<Vec<PoolSnapshot>> {
88 self.get(&format!(
89 "/pool_stake_snapshot?_pool_bech32={}",
90 encode(pool_bech32.value())
91 ))
92 .await
93 }
94
95 /// Get information about live delegators for a given pool
96 ///
97 /// # Arguments
98 ///
99 /// * `pool_bech32` - Pool ID in bech32 format
100 ///
101 /// # Examples
102 ///
103 /// ```no_run
104 /// use koios_sdk::Client;
105 /// use koios_sdk::types::PoolBech32;
106 ///
107 /// #[tokio::main]
108 /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
109 /// let client = Client::new()?;
110 /// let pool_id = PoolBech32::new(
111 /// "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"
112 /// );
113 /// let delegators = client.get_pool_delegators(&pool_id).await?;
114 /// println!("Pool delegators: {:?}", delegators);
115 /// Ok(())
116 /// }
117 /// ```
118 pub async fn get_pool_delegators(
119 &self,
120 pool_bech32: &PoolBech32,
121 ) -> Result<Vec<PoolDelegator>> {
122 self.get(&format!(
123 "/pool_delegators?_pool_bech32={}",
124 encode(pool_bech32.value())
125 ))
126 .await
127 }
128
129 /// Get information about active delegators (incl. history) for a given pool and epoch number
130 ///
131 /// # Arguments
132 ///
133 /// * `pool_bech32` - Pool ID in bech32 format
134 /// * `epoch_no` - Optional epoch number to query
135 ///
136 /// # Examples
137 ///
138 /// ```no_run
139 /// use koios_sdk::Client;
140 /// use koios_sdk::types::{PoolBech32, EpochNo};
141 ///
142 /// #[tokio::main]
143 /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
144 /// let client = Client::new()?;
145 /// let pool_id = PoolBech32::new(
146 /// "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"
147 /// );
148 /// let history = client.get_pool_delegators_history(
149 /// &pool_id,
150 /// Some(EpochNo::new("320"))
151 /// ).await?;
152 /// println!("Pool delegators history: {:?}", history);
153 /// Ok(())
154 /// }
155 /// ```
156 pub async fn get_pool_delegators_history(
157 &self,
158 pool_bech32: &PoolBech32,
159 epoch_no: Option<EpochNo>,
160 ) -> Result<Vec<PoolDelegatorsHistory>> {
161 let mut endpoint = format!(
162 "/pool_delegators_history?_pool_bech32={}",
163 encode(pool_bech32.value())
164 );
165
166 if let Some(epoch) = epoch_no {
167 endpoint.push_str(&format!("&_epoch_no={}", encode(epoch.value())));
168 }
169
170 self.get(&endpoint).await
171 }
172
173 /// Get pool stake, block and reward history for a specific epoch or all epochs
174 ///
175 /// # Arguments
176 ///
177 /// * `pool_bech32` - Pool ID in bech32 format
178 /// * `epoch_no` - Optional epoch number to query
179 ///
180 /// # Examples
181 ///
182 /// ```no_run
183 /// use koios_sdk::Client;
184 /// use koios_sdk::types::{PoolBech32, EpochNo};
185 ///
186 /// #[tokio::main]
187 /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
188 /// let client = Client::new()?;
189 /// let pool_id = PoolBech32::new(
190 /// "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"
191 /// );
192 /// let history = client.get_pool_history(
193 /// &pool_id,
194 /// Some(EpochNo::new("320"))
195 /// ).await?;
196 /// println!("Pool history: {:?}", history);
197 /// Ok(())
198 /// }
199 /// ```
200 pub async fn get_pool_history(
201 &self,
202 pool_bech32: &PoolBech32,
203 epoch_no: Option<EpochNo>,
204 ) -> Result<Vec<PoolHistoryInfo>> {
205 let mut endpoint = format!("/pool_history?_pool_bech32={}", encode(pool_bech32.value()));
206
207 if let Some(epoch) = epoch_no {
208 endpoint.push_str(&format!("&_epoch_no={}", encode(epoch.value())));
209 }
210
211 self.get(&endpoint).await
212 }
213
214 /// Get update history for all pools or a specific pool
215 ///
216 /// # Arguments
217 ///
218 /// * `pool_bech32` - Optional pool ID in bech32 format to filter by
219 ///
220 /// # Examples
221 ///
222 /// ```no_run
223 /// use koios_sdk::Client;
224 /// use koios_sdk::types::PoolBech32;
225 ///
226 /// #[tokio::main]
227 /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
228 /// let client = Client::new()?;
229 /// let pool_id = PoolBech32::new(
230 /// "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"
231 /// );
232 /// let updates = client.get_pool_updates(Some(&pool_id)).await?;
233 /// println!("Pool updates: {:?}", updates);
234 /// Ok(())
235 /// }
236 /// ```
237 pub async fn get_pool_updates(
238 &self,
239 pool_bech32: Option<&PoolBech32>,
240 ) -> Result<Vec<PoolUpdate>> {
241 let endpoint = if let Some(pool_id) = pool_bech32 {
242 format!("/pool_updates?_pool_bech32={}", encode(pool_id.value()))
243 } else {
244 "/pool_updates".to_string()
245 };
246 self.get(&endpoint).await
247 }
248
249 /// Get all pool registrations initiated in the requested epoch
250 ///
251 /// # Arguments
252 ///
253 /// * `epoch_no` - Optional epoch number to query
254 ///
255 /// # Examples
256 ///
257 /// ```no_run
258 /// use koios_sdk::Client;
259 /// use koios_sdk::types::EpochNo;
260 ///
261 /// #[tokio::main]
262 /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
263 /// let client = Client::new()?;
264 /// let registrations = client.get_pool_registrations(
265 /// Some(EpochNo::new("320"))
266 /// ).await?;
267 /// println!("Pool registrations: {:?}", registrations);
268 /// Ok(())
269 /// }
270 /// ```
271 pub async fn get_pool_registrations(
272 &self,
273 epoch_no: Option<EpochNo>,
274 ) -> Result<Vec<PoolRegistration>> {
275 let endpoint = if let Some(epoch) = epoch_no {
276 format!("/pool_registrations?_epoch_no={}", encode(epoch.value()))
277 } else {
278 "/pool_registrations".to_string()
279 };
280 self.get(&endpoint).await
281 }
282 /// Get all pool retirements initiated in the requested epoch
283 ///
284 /// # Arguments
285 ///
286 /// * `epoch_no` - Optional epoch number to query
287 ///
288 /// # Examples
289 ///
290 /// ```no_run
291 /// use koios_sdk::Client;
292 /// use koios_sdk::types::EpochNo;
293 ///
294 /// #[tokio::main]
295 /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
296 /// let client = Client::new()?;
297 /// let retirements = client.get_pool_retirements(
298 /// Some(EpochNo::new("320"))
299 /// ).await?;
300 /// println!("Pool retirements: {:?}", retirements);
301 /// Ok(())
302 /// }
303 /// ```
304 pub async fn get_pool_retirements(
305 &self,
306 epoch_no: Option<EpochNo>,
307 ) -> Result<Vec<PoolRegistration>> {
308 let endpoint = if let Some(epoch) = epoch_no {
309 format!("/pool_retirements?_epoch_no={}", encode(epoch.value()))
310 } else {
311 "/pool_retirements".to_string()
312 };
313 self.get(&endpoint).await
314 }
315
316 /// Get a list of registered relays for all pools
317 ///
318 /// # Examples
319 ///
320 /// ```no_run
321 /// use koios_sdk::Client;
322 ///
323 /// #[tokio::main]
324 /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
325 /// let client = Client::new()?;
326 /// let relays = client.get_pool_relays().await?;
327 /// println!("Pool relays: {:?}", relays);
328 /// Ok(())
329 /// }
330 /// ```
331 pub async fn get_pool_relays(&self) -> Result<Vec<PoolRelay>> {
332 self.get("/pool_relays").await
333 }
334
335 /// Get list of all votes cast by a pool
336 ///
337 /// # Arguments
338 ///
339 /// * `pool_bech32` - Pool ID in bech32 format
340 ///
341 /// # Examples
342 ///
343 /// ```no_run
344 /// use koios_sdk::Client;
345 /// use koios_sdk::types::PoolBech32;
346 ///
347 /// #[tokio::main]
348 /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
349 /// let client = Client::new()?;
350 /// let pool_id = PoolBech32::new(
351 /// "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"
352 /// );
353 /// let votes = client.get_pool_votes(&pool_id).await?;
354 /// println!("Pool votes: {:?}", votes);
355 /// Ok(())
356 /// }
357 /// ```
358 pub async fn get_pool_votes(&self, pool_bech32: &PoolBech32) -> Result<Vec<PoolVotes>> {
359 self.get(&format!(
360 "/pool_votes?_pool_bech32={}",
361 encode(pool_bech32.value())
362 ))
363 .await
364 }
365
366 /// Get metadata (on & off-chain) for all pools or specific pools
367 ///
368 /// # Arguments
369 ///
370 /// * `pool_ids` - Optional list of pool IDs to query
371 ///
372 /// # Examples
373 ///
374 /// ```no_run
375 /// use koios_sdk::Client;
376 ///
377 /// #[tokio::main]
378 /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
379 /// let client = Client::new()?;
380 /// let pool_ids = Some(vec![
381 /// "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy".to_string()
382 /// ]);
383 /// let metadata = client.get_pool_metadata(pool_ids.as_deref()).await?;
384 /// println!("Pool metadata: {:?}", metadata);
385 /// Ok(())
386 /// }
387 /// ```
388 pub async fn get_pool_metadata(
389 &self,
390 pool_ids: Option<&[String]>,
391 ) -> Result<Vec<PoolMetadataInfo>> {
392 let request = PoolIdsOptionalRequest::new(pool_ids.map(|ids| ids.to_vec()));
393 self.post("/pool_metadata", &request).await
394 }
395}
396
397#[cfg(test)]
398mod tests {
399 use super::*;
400 use serde_json::json;
401 use wiremock::matchers::{method, path, query_param};
402 use wiremock::{Mock, MockServer, ResponseTemplate};
403
404 #[tokio::test]
405 async fn test_get_pool_list() {
406 let mock_server = MockServer::start().await;
407 let client = Client::builder()
408 .base_url(mock_server.uri())
409 .build()
410 .unwrap();
411
412 let mock_response = json!([{
413 "pool_id_bech32": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy",
414 "pool_id_hex": "0f292fcaa02b8b2f9b3c8f9fd8e0bb21abedb692a6d5058df3ef2735",
415 "active_epoch_no": 321,
416 "margin": 0.015,
417 "fixed_cost": "340000000",
418 "pledge": "10000000000",
419 "reward_addr": "stake1uxkptsa4lkr55jleztw43t37vgdn88l6ghclfwuxld2eykq7dls9w",
420 "owners": ["stake1u98nnlkvkk23vtvf9273uq7cph5ww6u2yq2389psuqet90sv4xv9v"],
421 "relays": [],
422 "ticker": "TEST",
423 "meta_url": "https://example.com/metadata.json",
424 "meta_hash": "e394c39f06741ace92445d61d0853d8358fd49d1fd08a0b26599bda520",
425 "pool_status": "registered",
426 "retiring_epoch": null
427 }]);
428
429 Mock::given(method("GET"))
430 .and(path("/pool_list"))
431 .respond_with(ResponseTemplate::new(200).set_body_json(&mock_response))
432 .mount(&mock_server)
433 .await;
434
435 let response = client.get_pool_list().await.unwrap();
436 assert_eq!(response.len(), 1);
437 assert_eq!(
438 response[0].pool_id_bech32,
439 "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"
440 );
441 }
442
443 #[tokio::test]
444 async fn test_get_pool_history() {
445 let mock_server = MockServer::start().await;
446 let client = Client::builder()
447 .base_url(mock_server.uri())
448 .build()
449 .unwrap();
450
451 let pool_id = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy";
452 let mock_response = json!([{
453 "epoch_no": 321,
454 "active_stake": "1000000000000",
455 "active_stake_pct": 0.5,
456 "saturation_pct": 0.75,
457 "block_cnt": 100,
458 "delegator_cnt": 1000,
459 "margin": 0.015,
460 "fixed_cost": "340000000",
461 "pool_fees": "1500000000",
462 "deleg_rewards": "10000000000",
463 "epoch_ros": 0.05
464 }]);
465
466 Mock::given(method("GET"))
467 .and(path("/pool_history"))
468 .and(query_param("_pool_bech32", pool_id))
469 .respond_with(ResponseTemplate::new(200).set_body_json(&mock_response))
470 .mount(&mock_server)
471 .await;
472
473 let response = client
474 .get_pool_history(&PoolBech32::new(pool_id), None)
475 .await
476 .unwrap();
477 assert_eq!(response.len(), 1);
478 assert_eq!(response[0].epoch_no, 321);
479 }
480
481 // Add more tests for other endpoints...
482}