1use reqwest::{self, Method, RequestBuilder, header::HeaderMap};
2use tracing::debug;
3
4use crate::{
5 SensitiveString,
6 crypto::sign_query,
7 serde::{deserialize_json, serialize_query},
8 timestamp,
9 wallet::{
10 ApiError, Error, HEADER_RETRY_AFTER, HEADER_X_MBX_APIKEY, Path,
11 http::{
12 AccountStatus, CoinInfo, Deposit, DepositAddress, GetAccountStatusParams,
13 GetAllCoinsParams, GetDepositAddressParams, GetDepositHistoryParams, GetTradeFeeParams,
14 GetWithdrawHistoryParams, Headers, PrivateConfig, Response, TradeFee, Withdraw,
15 },
16 },
17};
18
19pub struct PrivateClient {
25 base_url: String,
26 headers: HeaderMap,
27 api_secret: SensitiveString,
28}
29
30impl PrivateClient {
31 pub fn new(cfg: PrivateConfig) -> Self {
32 let headers = {
33 let mut headers = HeaderMap::new();
34 let api_key = cfg.api_key.expose().parse().unwrap();
35 headers.append(HEADER_X_MBX_APIKEY, api_key);
36 if let Some(extra) = cfg.headers {
37 headers.extend(extra);
38 }
39 headers
40 };
41 Self {
42 base_url: cfg.base_url,
43 headers,
44 api_secret: cfg.api_secret,
45 }
46 }
47}
48
49impl PrivateClient {
51 pub async fn get_all_coins(
54 &self,
55 params: GetAllCoinsParams,
56 ) -> Result<Response<Vec<CoinInfo>>, Error> {
57 let query = serialize_query(¶ms)?;
58 let query = sign_query(&self.api_secret, timestamp(), &query);
59 let url = format!("{}{}?{query}", self.base_url, Path::CapitalConfigGetAll);
60 let client = reqwest::Client::builder().build()?;
61 let request = client
62 .request(Method::GET, url)
63 .headers(self.headers.clone());
64 send(request).await
65 }
66
67 pub async fn get_deposit_address(
69 &self,
70 params: GetDepositAddressParams,
71 ) -> Result<Response<DepositAddress>, Error> {
72 let query = serialize_query(¶ms)?;
73 let query = sign_query(&self.api_secret, timestamp(), &query);
74 let url = format!("{}{}?{query}", self.base_url, Path::CapitalDepositAddress);
75 let client = reqwest::Client::builder().build()?;
76 let request = client
77 .request(Method::GET, url)
78 .headers(self.headers.clone());
79 send(request).await
80 }
81
82 pub async fn get_deposit_history(
84 &self,
85 params: GetDepositHistoryParams,
86 ) -> Result<Response<Vec<Deposit>>, Error> {
87 let query = serialize_query(¶ms)?;
88 let query = sign_query(&self.api_secret, timestamp(), &query);
89 let url = format!("{}{}?{query}", self.base_url, Path::CapitalDepositHistory);
90 let client = reqwest::Client::builder().build()?;
91 let request = client
92 .request(Method::GET, url)
93 .headers(self.headers.clone());
94 send(request).await
95 }
96
97 pub async fn get_withdraw_history(
99 &self,
100 params: GetWithdrawHistoryParams,
101 ) -> Result<Response<Vec<Withdraw>>, Error> {
102 let query = serialize_query(¶ms)?;
103 let query = sign_query(&self.api_secret, timestamp(), &query);
104 let url = format!("{}{}?{query}", self.base_url, Path::CapitalWithdrawHistory);
105 let client = reqwest::Client::builder().build()?;
106 let request = client
107 .request(Method::GET, url)
108 .headers(self.headers.clone());
109 send(request).await
110 }
111}
112
113impl PrivateClient {
115 pub async fn get_account_status(
117 &self,
118 params: GetAccountStatusParams,
119 ) -> Result<Response<AccountStatus>, Error> {
120 let query = serialize_query(¶ms)?;
121 let query = sign_query(&self.api_secret, timestamp(), &query);
122 let url = format!("{}{}?{query}", self.base_url, Path::AccountStatus);
123 let client = reqwest::Client::builder().build()?;
124 let request = client
125 .request(Method::GET, url)
126 .headers(self.headers.clone());
127 send(request).await
128 }
129}
130
131impl PrivateClient {
133 pub async fn get_trade_fee(
135 &self,
136 params: GetTradeFeeParams,
137 ) -> Result<Response<Vec<TradeFee>>, Error> {
138 let query = serialize_query(¶ms)?;
139 let query = sign_query(&self.api_secret, timestamp(), &query);
140 let url = format!("{}{}?{query}", self.base_url, Path::AssetTradeFee);
141 let client = reqwest::Client::builder().build()?;
142 let request = client
143 .request(Method::GET, url)
144 .headers(self.headers.clone());
145 send(request).await
146 }
147}
148
149async fn send<T>(request: RequestBuilder) -> Result<Response<T>, Error>
150where
151 T: serde::de::DeserializeOwned,
152{
153 let response = request.send().await?;
154 let status = response.status();
155 let headers = parse_headers(response.headers());
156 let json = response.text().await?;
157
158 if !status.is_success() {
159 #[cfg(debug_assertions)]
160 debug!(?status, ?json, "request failed");
161
162 let api_err = deserialize_json::<ApiError>(&json)?;
164 return Err(Error::Api(api_err));
165 }
166
167 let result = deserialize_json(&json)?;
168 Ok(Response { result, headers })
169}
170
171fn parse_headers(headers: &HeaderMap) -> Headers {
172 let retry_after = headers
173 .get(HEADER_RETRY_AFTER)
174 .and_then(|h| h.to_str().unwrap_or_default().parse().ok());
175 Headers { retry_after }
176}