1use reqwest::{self, Method, RequestBuilder, header::HeaderMap};
2
3use crate::{
4 SensitiveString,
5 crypto::sign_query,
6 margin::{
7 ApiError, Error, HEADER_RETRY_AFTER, HEADER_X_MBX_APIKEY, Path,
8 http::{
9 EmptyResponse, GetAllMarginAssetsParams, GetMarginAccountParams,
10 GetMaxBorrowableParams, Headers, ListenKey, MarginAccount, MarginAsset, MaxBorrowable,
11 NewOrderRequest, NewOrderResponse, Order, PrivateConfig, QueryOrderParams, Response,
12 },
13 },
14 serde::{deserialize_json, serialize_query},
15 timestamp,
16};
17
18pub struct PrivateClient {
24 base_url: String,
25 headers: HeaderMap,
26 api_secret: SensitiveString,
27}
28
29impl PrivateClient {
30 pub fn new(cfg: PrivateConfig) -> Self {
31 let headers = {
32 let mut headers = HeaderMap::new();
33 let api_key = cfg.api_key.expose().parse().unwrap();
34 headers.append(HEADER_X_MBX_APIKEY, api_key);
35 if let Some(extra) = cfg.headers {
36 headers.extend(extra);
37 }
38 headers
39 };
40 Self {
41 base_url: cfg.base_url,
42 headers,
43 api_secret: cfg.api_secret,
44 }
45 }
46}
47
48impl PrivateClient {
50 pub async fn get_all_assets(
52 &self,
53 params: GetAllMarginAssetsParams,
54 ) -> Result<Response<Vec<MarginAsset>>, Error> {
55 let query = serialize_query(¶ms)?;
56 let query = sign_query(&self.api_secret, timestamp(), &query);
57 let url = format!("{}{}?{query}", self.base_url, Path::AllAssets);
58 let client = reqwest::Client::builder().build()?;
59 let request = client
60 .request(Method::GET, url)
61 .headers(self.headers.clone());
62 send(request).await
63 }
64}
65
66impl PrivateClient {
68 pub async fn margin_account(
70 &self,
71 params: GetMarginAccountParams,
72 ) -> Result<Response<MarginAccount>, Error> {
73 let query = serialize_query(¶ms)?;
74 let query = sign_query(&self.api_secret, timestamp(), &query);
75 let url = format!("{}{}?{query}", self.base_url, Path::Account);
76 let client = reqwest::Client::builder().build()?;
77 let request = client
78 .request(Method::GET, url)
79 .headers(self.headers.clone());
80 send(request).await
81 }
82}
83
84impl PrivateClient {
86 pub async fn new_order(
93 &self,
94 params: NewOrderRequest,
95 ) -> Result<Response<NewOrderResponse>, Error> {
96 let query = serialize_query(¶ms)?;
97 let query = sign_query(&self.api_secret, timestamp(), &query);
98 let url = format!("{}{}", self.base_url, Path::Order);
99 let client = reqwest::Client::builder().build()?;
100 let request = client
101 .request(Method::POST, url)
102 .headers(self.headers.clone())
103 .body(query); send(request).await
105 }
106
107 pub async fn query_order(&self, params: QueryOrderParams) -> Result<Response<Order>, Error> {
109 let query = serialize_query(¶ms)?;
110 let query = sign_query(&self.api_secret, timestamp(), &query);
111 let url = format!("{}{}?{query}", self.base_url, Path::Order);
112 let client = reqwest::Client::builder().build()?;
113 let request = client
114 .request(Method::GET, url)
115 .headers(self.headers.clone());
116 send(request).await
117 }
118}
119
120impl PrivateClient {
122 pub async fn max_borrowable(
127 &self,
128 params: GetMaxBorrowableParams,
129 ) -> Result<Response<MaxBorrowable>, Error> {
130 let query = serialize_query(¶ms)?;
131 let query = sign_query(&self.api_secret, timestamp(), &query);
132 let url = format!("{}{}?{query}", self.base_url, Path::MaxBorrowable);
133 let client = reqwest::Client::builder().build()?;
134 let request = client
135 .request(Method::GET, url)
136 .headers(self.headers.clone());
137 send(request).await
138 }
139}
140
141impl PrivateClient {
147 pub async fn create_listen_key(&self) -> Result<Response<ListenKey>, Error> {
153 let url = format!("{}{}", self.base_url, Path::UserDataStream);
154 let client = reqwest::Client::builder().build()?;
155 let request = client
156 .request(Method::POST, url)
157 .headers(self.headers.clone());
158 send(request).await
159 }
160
161 pub async fn keepalive_listen_key(
164 &self,
165 listen_key: &str,
166 ) -> Result<Response<EmptyResponse>, Error> {
167 let url = format!("{}{}", self.base_url, Path::UserDataStream);
168 let client = reqwest::Client::builder().build()?;
169 let request = client
170 .request(Method::PUT, url)
171 .headers(self.headers.clone())
172 .query(&[("listenKey", listen_key)]);
173 send(request).await
174 }
175
176 pub async fn close_listen_key(
179 &self,
180 listen_key: &str,
181 ) -> Result<Response<EmptyResponse>, Error> {
182 let url = format!("{}{}", self.base_url, Path::UserDataStream);
183 let client = reqwest::Client::builder().build()?;
184 let request = client
185 .request(Method::DELETE, url)
186 .headers(self.headers.clone())
187 .query(&[("listenKey", listen_key)]);
188 send(request).await
189 }
190}
191
192impl PrivateClient {
195 pub async fn create_isolated_listen_key(
198 &self,
199 symbol: &str,
200 ) -> Result<Response<ListenKey>, Error> {
201 let url = format!("{}{}", self.base_url, Path::UserDataStreamIsolated);
202 let client = reqwest::Client::builder().build()?;
203 let request = client
204 .request(Method::POST, url)
205 .headers(self.headers.clone())
206 .query(&[("symbol", symbol)]);
207 send(request).await
208 }
209
210 pub async fn keepalive_isolated_listen_key(
212 &self,
213 symbol: &str,
214 listen_key: &str,
215 ) -> Result<Response<EmptyResponse>, Error> {
216 let url = format!("{}{}", self.base_url, Path::UserDataStreamIsolated);
217 let client = reqwest::Client::builder().build()?;
218 let request = client
219 .request(Method::PUT, url)
220 .headers(self.headers.clone())
221 .query(&[("symbol", symbol), ("listenKey", listen_key)]);
222 send(request).await
223 }
224
225 pub async fn close_isolated_listen_key(
227 &self,
228 symbol: &str,
229 listen_key: &str,
230 ) -> Result<Response<EmptyResponse>, Error> {
231 let url = format!("{}{}", self.base_url, Path::UserDataStreamIsolated);
232 let client = reqwest::Client::builder().build()?;
233 let request = client
234 .request(Method::DELETE, url)
235 .headers(self.headers.clone())
236 .query(&[("symbol", symbol), ("listenKey", listen_key)]);
237 send(request).await
238 }
239}
240
241async fn send<T>(request: RequestBuilder) -> Result<Response<T>, Error>
242where
243 T: serde::de::DeserializeOwned,
244{
245 let response = request.send().await?;
246 let status = response.status();
247 let headers = parse_headers(response.headers());
248 let json = response.text().await?;
249
250 if !status.is_success() {
251 #[cfg(debug_assertions)]
252 tracing::debug!(?status, ?json, "request failed");
253
254 let api_err = deserialize_json::<ApiError>(&json)?;
255 return Err(Error::Api(api_err));
256 }
257
258 let result = deserialize_json(&json)?;
259 Ok(Response { result, headers })
260}
261
262fn parse_headers(headers: &HeaderMap) -> Headers {
263 let retry_after = headers
264 .get(HEADER_RETRY_AFTER)
265 .and_then(|h| h.to_str().unwrap_or_default().parse().ok());
266 Headers { retry_after }
267}