crypto_pay_api/api/
exchange.rs

1use async_trait::async_trait;
2
3use crate::{
4    client::CryptoBot,
5    error::CryptoBotResult,
6    models::{APIEndpoint, APIMethod, ExchangeRate, Method},
7};
8
9use super::ExchangeRateAPI;
10
11#[async_trait]
12impl ExchangeRateAPI for CryptoBot {
13    /// Gets current exchange rates for all supported cryptocurrencies
14    ///
15    /// This method returns exchange rates between supported cryptocurrencies and target currencies.
16    /// Exchange rates are updated regularly by CryptoBot.
17    ///
18    /// # Returns
19    /// * `Ok(Vec<ExchangeRate>)` - A list of current exchange rates
20    /// * `Err(CryptoBotError)` - If the request fails
21    ///
22    /// # Exchange Rate Pairs
23    /// Exchange rates are provided for various pairs:
24    /// * Cryptocurrency to fiat (e.g., TON/USD, BTC/EUR)
25    /// * Cryptocurrency to cryptocurrency (e.g., TON/BTC, ETH/BTC)
26    /// * Test currencies are also included in testnet mode
27    ///
28    /// # Example
29    /// ```no_run
30    /// use crypto_pay_api::prelude::*;
31    ///
32    /// #[tokio::main]
33    /// async fn main() -> Result<(), CryptoBotError> {
34    ///     let client = CryptoBot::builder().api_token("YOUR_API_TOKEN").build().unwrap();
35    ///     
36    ///     let rates = client.get_exchange_rates().await?;
37    ///     
38    ///     for rate in rates {
39    ///         println!("Exchange Rate: {} {} = {}",
40    ///             rate.source,
41    ///             rate.target,
42    ///             rate.rate,
43    ///         );
44    ///     }
45    ///     
46    ///     Ok(())
47    /// }
48    /// ```
49    ///
50    /// # See Also
51    /// * [ExchangeRate](struct.ExchangeRate.html) - The structure representing an exchange rate
52    async fn get_exchange_rates(&self) -> CryptoBotResult<Vec<ExchangeRate>> {
53        #[cfg(test)]
54        if let Some(rates) = &self.test_rates {
55            return Ok(rates.clone());
56        }
57
58        self.make_request(
59            &APIMethod {
60                endpoint: APIEndpoint::GetExchangeRates,
61                method: Method::GET,
62            },
63            None::<()>.as_ref(),
64        )
65        .await
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use mockito::Mock;
72    use rust_decimal_macros::dec;
73    use serde_json::json;
74
75    use crate::{
76        models::{CryptoCurrencyCode, FiatCurrencyCode},
77        utils::test_utils::TestContext,
78    };
79
80    use super::*;
81
82    impl TestContext {
83        pub fn mock_exchange_rates_response(&mut self) -> Mock {
84            self.server
85                .mock("GET", "/getExchangeRates")
86                .with_header("content-type", "application/json")
87                .with_header("Crypto-Pay-API-Token", "test_token")
88                .with_body(
89                    json!({
90                        "ok": true,
91                        "result": [
92                        {
93                            "is_valid": true,
94                            "is_crypto": true,
95                            "is_fiat": false,
96                            "source": "TON",
97                            "target": "USD",
98                            "rate": "3.70824926"
99                        },
100                        {
101                            "is_valid": true,
102                            "is_crypto": true,
103                            "is_fiat": false,
104                            "source": "DOGE",
105                            "target": "EUR",
106                            "rate": "0.24000835"
107                        },
108                        {
109                            "is_valid": true,
110                            "is_crypto": true,
111                            "is_fiat": false,
112                            "source": "USDT",
113                            "target": "RUB",
114                            "rate": "96.92078586"
115                        },
116                        {
117                            "is_valid": true,
118                            "is_crypto": true,
119                            "is_fiat": false,
120                            "source": "TON",
121                            "target": "EUR",
122                            "rate": "3.59048268"
123                        },
124                        ]
125                    })
126                    .to_string(),
127                )
128                .create()
129        }
130    }
131
132    #[test]
133    fn test_get_exchange_rates() {
134        let mut ctx = TestContext::new();
135        let _m = ctx.mock_exchange_rates_response();
136
137        let client = CryptoBot::builder()
138            .api_token("test_token")
139            .base_url(ctx.server.url())
140            .build()
141            .unwrap();
142
143        let result = ctx.run(async { client.get_exchange_rates().await });
144
145        println!("result: {:?}", result);
146
147        assert!(result.is_ok());
148
149        let exchange_rates = result.unwrap();
150        assert_eq!(exchange_rates.len(), 4);
151        assert_eq!(exchange_rates[0].source, CryptoCurrencyCode::Ton);
152        assert_eq!(exchange_rates[0].target, FiatCurrencyCode::Usd);
153        assert_eq!(exchange_rates[0].rate, dec!(3.70824926));
154    }
155}