kraken_rest_api/api/
api_impl.rs1use crate::api::utils::create_signature;
3use chrono::Utc;
4use data_encoding::BASE64;
5use log::trace;
6use reqwest::blocking::Response;
7use reqwest::header::USER_AGENT;
8use serde::de::DeserializeOwned;
9use std::{collections::HashMap, error::Error};
10
11use reqwest::header::HeaderMap;
12
13use super::{api::KrakenAPI, error::KrakenError, methods::Method, types::KrakenResponse};
14
15const API_URL: &str = "https://api.kraken.com";
16const API_VERSION: &str = "0";
17const API_USER_AGENT: &str = "Kraken Rust API Agent";
18pub const BTCUSD: &str = "XXBTZUSD";
19
20impl KrakenAPI {
22 pub fn new(api_key: String, secret: String) -> KrakenAPI {
23 KrakenAPI {
24 api_key,
25 secret,
26 client: reqwest::blocking::Client::new(),
27 }
28 }
29
30 pub async fn query_private<T>(
32 &self,
33 method: Method,
34 params: &mut HashMap<String, String>,
35 ) -> Result<KrakenResponse<T>, Box<dyn Error>>
36 where
37 T: DeserializeOwned + 'static,
38 {
39 let method: &str = method.into();
40 let url_path = format!("/{}/private/{}", API_VERSION, method);
41 let url = format!("{}{}", API_URL, url_path);
42 let secret_bytes = BASE64
43 .decode(&self.secret.as_bytes())
44 .expect("Not able to decode Kraken api secret");
45 let nonce = format!("{}", Utc::now().timestamp_millis() * 1000);
46 params.insert("nonce".to_owned(), nonce.to_owned());
47
48 let sig = create_signature(&url_path, params, &secret_bytes)?;
49
50 let mut header_map = HeaderMap::new();
51 header_map.insert(
52 "API-Key",
53 self.api_key.parse().expect("fail to parse api key"),
54 );
55
56 header_map.insert(
57 "API-Sign",
58 sig.parse().expect("fail to parse request signature"),
59 );
60
61 trace!("Query request url: {}", url);
62 trace!("Query request method: {}", method);
63 trace!("Query with nonce: {}", nonce);
64 trace!("Query request params: {:?}", params);
65
66 let res: KrakenResponse<T> = self.do_request(&url, params, &header_map).await?.json()?;
67 Ok(res)
68 }
69
70 pub async fn query_public<T>(
72 &self,
73 method: Method,
74 params: &HashMap<String, String>,
75 ) -> Result<KrakenResponse<T>, Box<dyn Error>>
76 where
77 T: DeserializeOwned + 'static,
78 {
79 let method: &str = method.into();
80 let url = format!("{}/{}/public/{}", API_URL, API_VERSION, method);
81
82 trace!("Query request url: {}", url);
83 trace!("Query request method: {}", method);
84
85 let res: KrakenResponse<T> = self
86 .do_request(&url, params, &HeaderMap::new())
87 .await?
88 .json()?;
89 Ok(res)
90 }
91
92 async fn do_request(
94 &self,
95 url: &str,
96 params: &HashMap<String, String>,
97 headers: &HeaderMap,
98 ) -> Result<Response, Box<dyn Error>> {
99 let mut header_map = HeaderMap::new();
100 header_map.insert(USER_AGENT, API_USER_AGENT.parse().unwrap());
101
102 for (n, v) in headers {
104 header_map.insert(n, v.to_owned());
105 }
106
107 let res = self
108 .client
109 .post(url)
110 .form(params)
111 .headers(header_map)
112 .send()?;
113
114 let ok = res.status().is_success();
115
116 if !ok {
117 let status_code = res.status();
118 let res: KrakenResponse<serde_json::Value> = res.json()?;
119 return Err(Box::new(KrakenError::new(Some(status_code), res.error)));
120 }
121
122 Ok(res)
123 }
124}