1use std::{time::SystemTime, borrow::Cow, marker::PhantomData, vec};
5use hmac::{Hmac, Mac};
6use sha2::Sha256;
7use serde::{Serialize, de::DeserializeOwned};
8use serde_json::json;
9use crypto_botters_api::{HandlerOption, HandlerOptions, HttpOption, WebSocketOption};
10use generic_api_client::{http::{*, header::HeaderValue}, websocket::*};
11
12pub type BybitRequestResult<T> = Result<T, BybitRequestError>;
14pub type BybitRequestError = RequestError<&'static str, BybitHandlerError>;
15
16pub enum BybitOption {
18 Default,
20 Key(String),
22 Secret(String),
24 HttpUrl(BybitHttpUrl),
26 HttpAuth(BybitHttpAuth),
28 RecvWindow(i32),
30 RequestConfig(RequestConfig),
33 WebSocketUrl(BybitWebSocketUrl),
35 WebSocketAuth(bool),
37 WebSocketTopics(Vec<String>),
39 WebSocketConfig(WebSocketConfig),
43}
44
45#[derive(Clone, Debug)]
47pub struct BybitOptions {
48 pub key: Option<String>,
50 pub secret: Option<String>,
52 pub http_url: BybitHttpUrl,
54 pub http_auth: BybitHttpAuth,
56 pub recv_window: Option<i32>,
58 pub request_config: RequestConfig,
60 pub websocket_url: BybitWebSocketUrl,
62 pub websocket_auth: bool,
64 pub websocket_topics: Vec<String>,
66 pub websocket_config: WebSocketConfig,
68}
69
70#[derive(Debug, Eq, PartialEq, Copy, Clone)]
72pub enum BybitHttpUrl {
73 Bybit,
75 Bytick,
77 Test,
79 None,
81}
82
83#[derive(Debug, Eq, PartialEq, Copy, Clone)]
85pub enum BybitWebSocketUrl {
86 Bybit,
88 Bytick,
90 Test,
92 None,
94}
95
96#[derive(Debug, Eq, PartialEq, Copy, Clone)]
114pub enum BybitHttpAuth {
115 Type1,
116 SpotType1,
117 Type2,
118 None,
119}
120
121#[derive(Debug)]
122pub enum BybitHandlerError {
123 ApiError(serde_json::Value),
124 IpBan(serde_json::Value),
125 ParseError,
126}
127
128pub struct BybitRequestHandler<'a, R: DeserializeOwned> {
130 options: BybitOptions,
131 _phantom: PhantomData<&'a R>,
132}
133
134pub struct BybitWebSocketHandler<H: FnMut(serde_json::Value) + Send + 'static> {
135 message_handler: H,
136 options: BybitOptions,
137}
138
139impl<'a, B, R> RequestHandler<B> for BybitRequestHandler<'a, R>
140where
141 B: Serialize,
142 R: DeserializeOwned,
143{
144 type Successful = R;
145 type Unsuccessful = BybitHandlerError;
146 type BuildError = &'static str;
147
148 fn request_config(&self) -> RequestConfig {
149 let mut config = self.options.request_config.clone();
150 if self.options.http_url != BybitHttpUrl::None {
151 config.url_prefix = self.options.http_url.as_str().to_owned();
152 }
153 config
154 }
155
156 fn build_request(&self, mut builder: RequestBuilder, request_body: &Option<B>, _: u8) -> Result<Request, Self::BuildError> {
157 if self.options.http_auth == BybitHttpAuth::None {
158 if let Some(body) = request_body {
159 let json = serde_json::to_string(body).or(Err("could not serialize body as application/json"))?;
160 builder = builder
161 .header(header::CONTENT_TYPE, "application/json")
162 .body(json);
163 }
164 return builder.build().or(Err("failed to build request"));
165 }
166
167 let key = self.options.key.as_deref().ok_or("API key not set")?;
168 let secret = self.options.secret.as_deref().ok_or("API secret not set")?;
169
170 let time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); let timestamp = time.as_millis();
172
173 let hmac = Hmac::<Sha256>::new_from_slice(secret.as_bytes()).unwrap(); match self.options.http_auth {
176 BybitHttpAuth::Type1 => Self::type1_auth(builder, request_body, key, timestamp, hmac, false, self.options.recv_window),
177 BybitHttpAuth::SpotType1 => Self::type1_auth(builder, request_body, key, timestamp, hmac, true, self.options.recv_window),
178 BybitHttpAuth::Type2 => Self::type2_auth(builder, request_body, key, timestamp, hmac, self.options.recv_window),
179 BybitHttpAuth::None => unreachable!(), }
181 }
182
183 fn handle_response(&self, status: StatusCode, _: HeaderMap, response_body: Bytes) -> Result<Self::Successful, Self::Unsuccessful> {
184 if status.is_success() {
185 serde_json::from_slice(&response_body).map_err(|error| {
186 log::error!("Failed to parse response due to an error: {}", error);
187 BybitHandlerError::ParseError
188 })
189 } else {
190 let error = match serde_json::from_slice(&response_body) {
192 Ok(parsed) => {
193 if status == 403 {
194 BybitHandlerError::IpBan(parsed)
195 } else {
196 BybitHandlerError::ApiError(parsed)
197 }
198 }
199 Err(error) => {
200 log::error!("Failed to parse error response due to an error: {}", error);
201 BybitHandlerError::ParseError
202 },
203 };
204 Err(error)
205 }
206 }
207}
208
209impl<'a, R> BybitRequestHandler<'a, R> where R: DeserializeOwned {
210 fn type1_auth<B>(builder: RequestBuilder, request_body: &Option<B>, key: &str, timestamp: u128, mut hmac: Hmac<Sha256>, spot: bool, window: Option<i32>)
211 -> Result<Request, <BybitRequestHandler<'a, R> as RequestHandler<B>>::BuildError>
212 where
213 B: Serialize,
214 {
215 fn sort_and_add<'a>(mut pairs: Vec<(Cow<str>, Cow<'a, str>)>, key: &'a str, timestamp: u128) -> String {
216 pairs.push((Cow::Borrowed("api_key"), Cow::Borrowed(key)));
217 pairs.push((Cow::Borrowed("timestamp"), Cow::Owned(timestamp.to_string())));
218 pairs.sort_unstable();
219
220 let mut urlencoded = String::new();
221 for (key, value) in pairs {
222 urlencoded.push_str(&key);
223 if !value.is_empty() {
224 urlencoded.push('=');
225 urlencoded.push_str(&value);
226 }
227 urlencoded.push('&');
228 }
229 urlencoded.pop(); urlencoded
231 }
232
233 let mut request = builder.build().or(Err("failed to build request"))?;
234 if matches!(*request.method(), Method::GET | Method::DELETE) {
235 let mut queries: Vec<_> = request.url().query_pairs().collect();
236 if let Some(window) = window {
237 if spot {
238 queries.push((Cow::Borrowed("recvWindow"), Cow::Owned(window.to_string())));
239 } else {
240 queries.push((Cow::Borrowed("recv_window"), Cow::Owned(window.to_string())));
241 }
242 }
243 let query = sort_and_add(queries, key, timestamp);
244 request.url_mut().set_query(Some(&query));
245
246 hmac.update(query.as_bytes());
247 let signature = hex::encode(hmac.finalize().into_bytes());
248
249 request.url_mut().query_pairs_mut().append_pair("sign", &signature);
250
251 if let Some(body) = request_body {
252 if spot {
253 let body_string = serde_urlencoded::to_string(body).or(Err("could not serialize body as application/x-www-form-urlencoded"))?;
254 *request.body_mut() = Some(body_string.into());
255 request.headers_mut().insert(header::CONTENT_TYPE, HeaderValue::from_static("application/x-www-form-urlencoded"));
256 } else {
257 let body_string = serde_json::to_string(body).or(Err("could not serialize body as application/json"))?;
258 *request.body_mut() = Some(body_string.into());
259 request.headers_mut().insert(header::CONTENT_TYPE, HeaderValue::from_static("application/json"));
260 }
261 }
262 } else {
263 let mut body = if let Some(body) = request_body {
264 serde_urlencoded::to_string(body).or(Err("could not serialize body as application/x-www-form-urlencoded"))?
265 } else {
266 String::new()
267 };
268 if let Some(window) = window {
269 if !body.is_empty() {
270 body.push('&');
271 }
272 if spot {
273 body.push_str("recvWindow=");
274 } else {
275 body.push_str("recv_window=");
276 }
277 body.push_str(&window.to_string());
278 }
279
280 let pairs: Vec<_> = body.split('&')
281 .map(|pair| pair.split_once('=').unwrap_or((pair, "")))
282 .map(|(k, v)| (Cow::Borrowed(k), Cow::Borrowed(v)))
283 .collect();
284 let mut body_query_string = sort_and_add(pairs, key, timestamp);
285
286 hmac.update(body_query_string.as_bytes());
287 let signature = hex::encode(hmac.finalize().into_bytes());
288
289 if spot {
290 body_query_string.push_str(&format!("sign={signature}"));
291
292 *request.body_mut() = Some(body_query_string.into());
293 request.headers_mut().insert(header::CONTENT_TYPE, HeaderValue::from_static("application/x-www-form-urlencoded"));
294 } else {
295 let mut json = serde_json::to_value(request_body).or(Err("could not serialize body as application/json"))?;
296 let Some(map) = json.as_object_mut() else {
297 return Err("body must to be serializable as a JSON object");
298 };
299 map.insert("sign".to_owned(), serde_json::Value::String(signature));
300 if let Some(window) = window {
301 if spot {
302 map.insert("recvWindow".to_owned(), serde_json::Value::Number(window.into()));
303 } else {
304 map.insert("recv_window".to_owned(), serde_json::Value::Number(window.into()));
305 }
306 }
307
308 *request.body_mut() = Some(json.to_string().into());
309 request.headers_mut().insert(header::CONTENT_TYPE, HeaderValue::from_static("application/json"));
310 }
311 }
312 Ok(request)
313 }
314
315 fn type2_auth<B>(mut builder: RequestBuilder, request_body: &Option<B>, key: &str, timestamp: u128, mut hmac: Hmac<Sha256>, window: Option<i32>)
316 -> Result<Request, <BybitRequestHandler<'a, R> as RequestHandler<B>>::BuildError>
317 where
318 B: Serialize,
319 {
320 let body = if let Some(body) = request_body {
321 let json = serde_json::to_value(body).or(Err("could not serialize body as application/json"))?;
322 builder = builder
323 .header(header::CONTENT_TYPE, "application/json")
324 .body(json.to_string());
325 Some(json)
326 } else {
327 None
328 };
329
330 let mut request = builder.build().or(Err("failed to build request"))?;
331
332 let mut sign_contents = format!("{timestamp}{key}");
333 if let Some(window) = window {
334 sign_contents.push_str(&window.to_string());
335 }
336
337 if matches!(*request.method(), Method::GET | Method::DELETE) {
338 if let Some(query) = request.url().query() {
339 sign_contents.push_str(query);
340 }
341 } else {
342 let body = body.unwrap_or_else(|| {
343 *request.body_mut() = Some("{}".into());
344 request.headers_mut().insert(header::CONTENT_TYPE, HeaderValue::from_static("application/json"));
345 json!({})
346 });
347 sign_contents.push_str(&body.to_string());
348 }
349
350 hmac.update(sign_contents.as_bytes());
351 let signature = hex::encode(hmac.finalize().into_bytes());
352
353 let headers = request.headers_mut();
354 headers.insert("X-BAPI-SIGN-TYPE", HeaderValue::from(2));
355 headers.insert("X-BAPI-SIGN", HeaderValue::from_str(&signature).unwrap()); headers.insert("X-BAPI-API-KEY", HeaderValue::from_str(key).or(Err("invalid character in API key"))?);
357 headers.insert("X-BAPI-TIMESTAMP", HeaderValue::from(timestamp as u64));
358 if let Some(window) = window {
359 headers.insert("X-BAPI-RECV-WINDOW", HeaderValue::from(window));
360 }
361 Ok(request)
362 }
363}
364
365impl<H> WebSocketHandler for BybitWebSocketHandler<H> where H: FnMut(serde_json::Value) + Send + 'static {
366 fn websocket_config(&self) -> WebSocketConfig {
367 let mut config = self.options.websocket_config.clone();
368 if self.options.websocket_url != BybitWebSocketUrl::None {
369 config.url_prefix = self.options.websocket_url.as_str().to_owned();
370 }
371 config
372 }
373
374 fn handle_start(&mut self) -> Vec<WebSocketMessage> {
375 if self.options.websocket_auth {
376 if let Some(key) = self.options.key.as_deref() {
377 if let Some(secret) = self.options.secret.as_deref() {
378 let time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); let expires = time.as_millis() as u64 + 1000;
380
381 let mut hmac = Hmac::<Sha256>::new_from_slice(secret.as_bytes()).unwrap(); hmac.update(format!("GET/realtime{expires}").as_bytes());
384 let signature = hex::encode(hmac.finalize().into_bytes());
385
386 return vec![
387 WebSocketMessage::Text(json!({
388 "op": "auth",
389 "args": [key, expires, signature],
390 }).to_string()),
391 ];
392 } else {
393 log::error!("API secret not set.");
394 };
395 } else {
396 log::error!("API key not set.");
397 };
398 }
399 self.message_subscribe()
400 }
401
402 fn handle_message(&mut self, message: WebSocketMessage) -> Vec<WebSocketMessage> {
403 match message {
404 WebSocketMessage::Text(message) => {
405 let message: serde_json::Value = match serde_json::from_str(&message) {
406 Ok(message) => message,
407 Err(_) => {
408 log::warn!("Invalid JSON received");
409 return vec![];
410 },
411 };
412 match message["op"].as_str() {
413 Some("auth") => {
414 if message["success"].as_bool() == Some(true) {
415 log::debug!("WebSocket authentication successful");
416 } else {
417 log::error!("WebSocket authentication unsuccessful; message: {}", message["ret_msg"]);
418 }
419 return self.message_subscribe();
420 },
421 Some("subscribe") => {
422 if message["success"].as_bool() == Some(true) {
423 log::debug!("WebSocket topics subscription successful");
424 } else {
425 log::error!("WebSocket topics subscription unsuccessful; message: {}", message["ret_msg"]);
426 }
427 },
428 _ => (self.message_handler)(message),
429 }
430 },
431 WebSocketMessage::Binary(_) => log::warn!("Unexpected binary message received"),
432 WebSocketMessage::Ping(_) | WebSocketMessage::Pong(_) => (),
433 }
434 vec![]
435 }
436}
437
438impl<H> BybitWebSocketHandler<H> where H: FnMut(serde_json::Value) + Send + 'static, {
439 #[inline(always)]
440 fn message_subscribe(&self) -> Vec<WebSocketMessage> {
441 vec![WebSocketMessage::Text(
442 json!({ "op": "subscribe", "args": self.options.websocket_topics }).to_string(),
443 )]
444 }
445}
446
447impl BybitHttpUrl {
448 #[inline(always)]
450 pub fn as_str(&self) -> &'static str {
451 match self {
452 Self::Bybit => "https://api.bybit.com",
453 Self::Bytick => "https://api.bytick.com",
454 Self::Test => "https://api-testnet.bybit.com",
455 Self::None => "",
456 }
457 }
458}
459
460impl BybitWebSocketUrl {
461 #[inline(always)]
463 pub fn as_str(&self) -> &'static str {
464 match self {
465 Self::Bybit => "wss://stream.bybit.com",
466 Self::Bytick => "wss://stream.bytick.com",
467 Self::Test => "wss://stream-testnet.bybit.com",
468 Self::None => "",
469 }
470 }
471}
472
473impl HandlerOptions for BybitOptions {
474 type OptionItem = BybitOption;
475
476 fn update(&mut self, option: Self::OptionItem) {
477 match option {
478 BybitOption::Default => (),
479 BybitOption::Key(v) => self.key = Some(v),
480 BybitOption::Secret(v) => self.secret = Some(v),
481 BybitOption::HttpUrl(v) => self.http_url = v,
482 BybitOption::HttpAuth(v) => self.http_auth = v,
483 BybitOption::RecvWindow(v) => self.recv_window = Some(v),
484 BybitOption::RequestConfig(v) => self.request_config = v,
485 BybitOption::WebSocketUrl(v) => self.websocket_url = v,
486 BybitOption::WebSocketAuth(v) => self.websocket_auth = v,
487 BybitOption::WebSocketTopics(v) => self.websocket_topics = v,
488 BybitOption::WebSocketConfig(v) => self.websocket_config = v,
489 }
490 }
491}
492
493impl Default for BybitOptions {
494 fn default() -> Self {
495 let mut websocket_config = WebSocketConfig::new();
496 websocket_config.ignore_duplicate_during_reconnection = true;
497 Self {
498 key: None,
499 secret: None,
500 http_url: BybitHttpUrl::Bybit,
501 http_auth: BybitHttpAuth::None,
502 recv_window: None,
503 request_config: RequestConfig::default(),
504 websocket_url: BybitWebSocketUrl::Bybit,
505 websocket_auth: false,
506 websocket_topics: vec![],
507 websocket_config,
508 }
509 }
510}
511
512impl<'a, R: DeserializeOwned + 'a> HttpOption<'a, R> for BybitOption {
513 type RequestHandler = BybitRequestHandler<'a, R>;
514
515 #[inline(always)]
516 fn request_handler(options: Self::Options) -> Self::RequestHandler {
517 BybitRequestHandler::<'a, R> {
518 options,
519 _phantom: PhantomData,
520 }
521 }
522}
523
524impl <H: FnMut(serde_json::Value) + Send + 'static> WebSocketOption<H> for BybitOption {
525 type WebSocketHandler = BybitWebSocketHandler<H>;
526
527 #[inline(always)]
528 fn websocket_handler(handler: H, options: Self::Options) -> Self::WebSocketHandler {
529 BybitWebSocketHandler {
530 message_handler: handler,
531 options,
532 }
533 }
534}
535
536impl HandlerOption for BybitOption {
537 type Options = BybitOptions;
538}
539
540impl Default for BybitOption {
541 fn default() -> Self {
542 Self::Default
543 }
544}