1use crate::{
2 errors::{BybitError, BybitResult},
3 types::*,
4};
5use chrono::Utc;
6use hmac::{Hmac, Mac};
7use reqwest::{Client, RequestBuilder};
8use serde_json::{Map, Value};
9use sha2::Sha256;
10use std::collections::HashMap;
11
12static BYBIT_HOST: &'static str = "https://api.bybit.com";
13static RECV_WINDOW: &'static str = "5000";
14
15pub struct BybitHttp {
16 api_key: String,
17 api_secret: String,
18 client: Client,
19}
20
21impl BybitHttp {
22 pub fn new(api_key: String, api_secret: String) -> Self {
23 Self {
24 api_key,
25 api_secret,
26 client: Client::builder()
27 .timeout(std::time::Duration::from_secs(10))
28 .build()
29 .expect("reqwest client"),
30 }
31 }
32
33 fn generate_get_signature(
34 &self,
35 timestamp: &str,
36 params: &HashMap<&str, &str>,
37 ) -> BybitResult<String> {
38 let mut mac = Hmac::<Sha256>::new_from_slice(self.api_secret.as_bytes())
39 .expect("HMAC can take key of any size");
40 mac.update(timestamp.as_bytes());
41 mac.update(self.api_key.as_bytes());
42 mac.update(RECV_WINDOW.as_bytes());
43 mac.update(Self::generate_query_str(params).as_bytes());
44
45 let result = mac.finalize();
46 let code_bytes = result.into_bytes();
47 Ok(hex::encode(code_bytes))
48 }
49
50 fn generate_post_signature(
51 &self,
52 timestamp: &str,
53 params: &Map<String, Value>,
54 ) -> BybitResult<String> {
55 let mut mac = Hmac::<Sha256>::new_from_slice(self.api_secret.as_bytes())
56 .expect("HMAC can take key of any size");
57 mac.update(timestamp.as_bytes());
58 mac.update(self.api_key.as_bytes());
59 mac.update(RECV_WINDOW.as_bytes());
60 mac.update(serde_json::to_string(¶ms)?.as_bytes());
61
62 let result = mac.finalize();
63 let code_bytes = result.into_bytes();
64 Ok(hex::encode(code_bytes))
65 }
66
67 fn generate_query_str(params: &HashMap<&str, &str>) -> String {
68 params
69 .iter()
70 .map(|(key, value)| format!("{}={}", key, value))
71 .collect::<Vec<String>>()
72 .join("&")
73 }
74
75 fn add_headers(
76 &self,
77 builder: RequestBuilder,
78 timestamp: &String,
79 signature: &String,
80 ) -> RequestBuilder {
81 builder
82 .header("X-BAPI-API-KEY", self.api_key.clone())
83 .header("X-BAPI-SIGN", signature)
84 .header("X-BAPI-SIGN-TYPE", "2")
85 .header("X-BAPI-TIMESTAMP", timestamp.clone())
86 .header("X-BAPI-RECV-WINDOW", RECV_WINDOW)
87 .header("Content-Type", "application/json")
88 }
89
90 pub(crate) async fn send_get_request<T>(
91 &self,
92 endpoint: &str,
93 params: HashMap<&str, &str>,
94 is_auth: bool,
95 ) -> BybitResult<T>
96 where
97 T: for<'a> serde::Deserialize<'a>,
98 {
99 let query_str = Self::generate_query_str(¶ms);
100
101 let mut builder = self
102 .client
103 .get(&format!("{}/{}?{}", BYBIT_HOST, endpoint, query_str));
104
105 if is_auth {
106 let timestamp = Utc::now().timestamp_millis().to_string();
107 let signature = self.generate_get_signature(×tamp, ¶ms)?;
108 builder = self.add_headers(builder, ×tamp, &signature)
109 }
110
111 let response = builder
112 .send()
113 .await
114 .map_err(|err| BybitError::Unknown(format!("{}", err)))?;
115 let res = &response
116 .text()
117 .await
118 .map_err(|err| BybitError::Unknown(format!("{}", err)))?;
119 let res = serde_json::from_str::<BybitHttpResponse>(res)
120 .map_err(|err| BybitError::DeserializeError(format!("{res}: {err}")))?;
121 match res.ret_code {
122 0 => {
123 let value_str = &res.result.to_string();
124 let res = serde_json::from_str::<T>(value_str)
125 .map_err(|err| BybitError::DeserializeError(format!("{value_str}: {err}")))?;
126 Ok(res)
127 }
128 _ => Err(BybitError::ApiError(res.ret_code, res.ret_msg)),
129 }
130 }
131
132 pub(crate) async fn send_post_request<T>(
133 &self,
134 endpoint: &str,
135 params: Map<String, Value>,
136 ) -> BybitResult<T>
137 where
138 T: for<'a> serde::Deserialize<'a>,
139 {
140 let timestamp = Utc::now().timestamp_millis().to_string();
141 let signature = self.generate_post_signature(×tamp, ¶ms)?;
142
143 let builder = self
144 .client
145 .post(format!("{BYBIT_HOST}/{}", endpoint))
146 .json(¶ms);
147 let builder = self.add_headers(builder, ×tamp, &signature);
148 let response = builder
149 .send()
150 .await
151 .map_err(|err| BybitError::Unknown(format!("{}", err)))?;
152 let res = &response
153 .text()
154 .await
155 .map_err(|err| BybitError::Unknown(format!("{}", err)))?;
156 let res = serde_json::from_str::<BybitHttpResponse>(res)
157 .map_err(|err| BybitError::DeserializeError(format!("{res}: {err}")))?;
158 match res.ret_code {
159 0 => {
160 let value_str = &res.result.to_string();
161 let res = serde_json::from_str::<T>(value_str)
162 .map_err(|err| BybitError::DeserializeError(format!("{value_str}: {err}")))?;
163 Ok(res)
164 }
165 _ => Err(BybitError::ApiError(res.ret_code, res.ret_msg)),
166 }
167 }
168}