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}