Skip to main content

bybit_rust_api/rest/
client.rs

1use crate::consts::{
2    API_REQUEST_KEY, RECV_WINDOW_KEY, SIGNATURE_KEY, SIGN_TYPE_KEY, TIMESTAMP_KEY,
3};
4use crate::rest::api_key_pair::ApiKeyPair;
5use crate::rest::errors::{BybitResult, ErrorCodes};
6use crate::utils::{millis, sign, RateLimiter};
7use reqwest::RequestBuilder;
8use serde::de::DeserializeOwned;
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, Copy, PartialEq)]
12pub enum SecType {
13    None = 0,
14    Signed = 1,
15}
16
17#[derive(Clone)]
18pub struct RestClient {
19    api_key_pair: ApiKeyPair,
20    base_url: String,
21
22    http_client: reqwest::Client,
23    recv_window: String,
24    rate_limiter: Option<RateLimiter>,
25}
26
27#[derive(Serialize, Deserialize, Debug, Clone)]
28pub struct ServerResponse<T> {
29    #[serde(rename = "retCode")]
30    pub ret_code: i32,
31    #[serde(rename = "retMsg")]
32    pub ret_msg: String,
33    #[serde(rename = "result")]
34    pub result: T,
35    #[serde(rename = "retExtInfo")]
36    pub ret_ext_info: serde_json::Value,
37    #[serde(rename = "time")]
38    pub time: i64,
39}
40
41#[derive(Serialize, Deserialize, Debug, Clone)]
42struct RawServerResponse {
43    #[serde(rename = "retCode")]
44    pub ret_code: i32,
45    #[serde(rename = "retMsg")]
46    pub ret_msg: String,
47    #[serde(rename = "result")]
48    pub result: serde_json::Value,
49    #[serde(rename = "retExtInfo")]
50    pub ret_ext_info: serde_json::Value,
51    #[serde(rename = "time")]
52    pub time: i64,
53}
54
55impl RestClient {
56    pub fn new(api_key_pair: ApiKeyPair, base_url: String) -> RestClient {
57        RestClient {
58            api_key_pair,
59            base_url,
60            http_client: reqwest::Client::new(),
61            recv_window: "5000".to_string(),
62            rate_limiter: None,
63        }
64    }
65
66    /// Set a rate limiter for this client.
67    ///
68    /// Bybit limits: 50 req/s for both public and private endpoints.
69    /// Use `RateLimiter::public_rest()` or `RateLimiter::private_rest()`.
70    pub fn with_rate_limiter(mut self, limiter: RateLimiter) -> Self {
71        self.rate_limiter = Some(limiter);
72        self
73    }
74
75    pub fn with_recv_window(mut self, recv_window: impl Into<String>) -> Self {
76        self.recv_window = recv_window.into();
77        self
78    }
79
80    fn query_string(&self, query: serde_json::Value) -> BybitResult<String> {
81        let object = query.as_object().ok_or_else(|| {
82            crate::rest::errors::BybitError::Internal(
83                "Query params must be a JSON object".to_string(),
84            )
85        })?;
86        Ok(serde_urlencoded::to_string(object)?)
87    }
88
89    fn sign_request(
90        &self,
91        request_builder: RequestBuilder,
92        query_or_body_param: String,
93    ) -> RequestBuilder {
94        let recv_window = &self.recv_window;
95        let timestamp_str = millis().to_string();
96        let signature = sign(
97            self.api_key_pair.secret(),
98            &format!(
99                "{}{}{}{}",
100                timestamp_str,
101                self.api_key_pair.key(),
102                recv_window,
103                query_or_body_param
104            ),
105        );
106
107        request_builder
108            .header(SIGN_TYPE_KEY, "2")
109            .header(API_REQUEST_KEY, self.api_key_pair.key())
110            .header(TIMESTAMP_KEY, timestamp_str)
111            .header(RECV_WINDOW_KEY, recv_window)
112            .header(SIGNATURE_KEY, signature)
113    }
114
115    pub async fn get<A: DeserializeOwned>(
116        &self,
117        endpoint: &str,
118        query: serde_json::Value,
119        sec_type: SecType,
120    ) -> BybitResult<ServerResponse<A>> {
121        if let Some(ref limiter) = self.rate_limiter {
122            limiter.acquire().await;
123        }
124        let mut url = format!("{}/{}", self.base_url, endpoint);
125        let query_string = self.query_string(query)?;
126
127        if !query_string.is_empty() {
128            url.push_str(&format!("?{}", query_string));
129        }
130
131        let mut request_builder = self.http_client.get(&url);
132        if sec_type == SecType::Signed {
133            request_builder = self.sign_request(request_builder, query_string);
134        }
135
136        log::debug!("url: {}", url);
137
138        let r = request_builder.send().await?;
139        let raw_response: RawServerResponse = r.json().await?;
140
141        if raw_response.ret_code != 0 {
142            let code_str = raw_response.ret_code.to_string();
143            // Try to parse into known ErrorCodes, otherwise fallback to generic
144            if let Ok(error_code) = serde_json::from_value(serde_json::json!(code_str)) {
145                return Err(crate::rest::errors::BybitError::Api(error_code));
146            } else {
147                return Err(crate::rest::errors::BybitError::Api(
148                    crate::rest::errors::ErrorCodes::E10001,
149                )); // System error fallback or similar
150            }
151        }
152
153        let result: A = serde_json::from_value(raw_response.result)?;
154        Ok(ServerResponse {
155            ret_code: raw_response.ret_code,
156            ret_msg: raw_response.ret_msg,
157            result,
158            ret_ext_info: raw_response.ret_ext_info,
159            time: raw_response.time,
160        })
161    }
162
163    pub async fn post<A: DeserializeOwned>(
164        &self,
165        endpoint: &str,
166        body: serde_json::Value,
167        sec_type: SecType,
168    ) -> BybitResult<ServerResponse<A>> {
169        if let Some(ref limiter) = self.rate_limiter {
170            limiter.acquire().await;
171        }
172        let url = format!("{}/{}", self.base_url, endpoint);
173        let mut request_builder = self.http_client.post(&url);
174        if sec_type == SecType::Signed {
175            let body_str =
176                serde_json::to_string(&body).map_err(crate::rest::errors::BybitError::Json)?;
177            request_builder = self.sign_request(request_builder, body_str);
178        }
179
180        let r = request_builder.json(&body).send().await?;
181        let raw_response: RawServerResponse = r.json().await?;
182
183        if raw_response.ret_code != 0 {
184            let code_str = raw_response.ret_code.to_string();
185            if let Ok(error_code) = serde_json::from_value(serde_json::json!(code_str)) {
186                return Err(crate::rest::errors::BybitError::Api(error_code));
187            } else {
188                return Err(crate::rest::errors::BybitError::Api(ErrorCodes::E10001));
189            }
190        }
191
192        let result: A = serde_json::from_value(raw_response.result)?;
193        Ok(ServerResponse {
194            ret_code: raw_response.ret_code,
195            ret_msg: raw_response.ret_msg,
196            result,
197            ret_ext_info: raw_response.ret_ext_info,
198            time: raw_response.time,
199        })
200    }
201}