1use ring::hmac;
4
5pub fn sign_hmac_sha256(message: &str, secret: &str) -> String {
9 let key = hmac::Key::new(hmac::HMAC_SHA256, secret.as_bytes());
10 let signature = hmac::sign(&key, message.as_bytes());
11 hex::encode(signature.as_ref())
12}
13
14pub fn sign_rest_request(
22 timestamp: u64,
23 api_key: &str,
24 recv_window: u32,
25 payload: &str,
26 api_secret: &str,
27) -> String {
28 let message = format!("{}{}{}{}", timestamp, api_key, recv_window, payload);
29 sign_hmac_sha256(&message, api_secret)
30}
31
32pub fn sign_ws_auth(expires: u64, api_secret: &str) -> String {
36 let message = format!("GET/realtime{}", expires);
37 sign_hmac_sha256(&message, api_secret)
38}
39
40pub fn current_timestamp_ms() -> u64 {
42 std::time::SystemTime::now()
43 .duration_since(std::time::UNIX_EPOCH)
44 .unwrap_or_else(|err| err.duration())
45 .as_millis() as u64
46}
47
48pub mod headers {
50 pub const API_KEY: &str = "X-BAPI-API-KEY";
52 pub const TIMESTAMP: &str = "X-BAPI-TIMESTAMP";
54 pub const SIGN: &str = "X-BAPI-SIGN";
56 pub const RECV_WINDOW: &str = "X-BAPI-RECV-WINDOW";
58 pub const SIGN_TYPE: &str = "X-BAPI-SIGN-TYPE";
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn test_hmac_signature() {
68 let secret = "test_secret";
69 let message = "test_message";
70 let sig = sign_hmac_sha256(message, secret);
71
72 assert_eq!(sig.len(), 64);
73 assert!(sig.chars().all(|c| c.is_ascii_hexdigit()));
74 }
75
76 #[test]
77 fn test_rest_signature() {
78 let timestamp: u64 = 1658384314791;
79 let api_key = "XXXXXXXX";
80 let recv_window = 5000;
81 let payload = r#"{"category":"linear","symbol":"BTCUSDT","side":"Buy","positionIdx":0,"orderType":"Limit","qty":"0.001","price":"18900","timeInForce":"GTC"}"#;
82 let api_secret = "YYYYYYYY";
83
84 let signature = sign_rest_request(timestamp, api_key, recv_window, payload, api_secret);
85
86 assert_eq!(signature.len(), 64);
87 assert!(signature.chars().all(|c| c.is_ascii_hexdigit()));
88 }
89
90 #[test]
91 fn test_ws_signature() {
92 let expires: u64 = 1658384314791;
93 let api_secret = "test_secret";
94
95 let signature = sign_ws_auth(expires, api_secret);
96
97 assert_eq!(signature.len(), 64);
98 assert!(signature.chars().all(|c| c.is_ascii_hexdigit()));
99 }
100
101 #[test]
102 fn test_signature_consistency() {
103 let sig1 = sign_hmac_sha256("test", "secret");
104 let sig2 = sign_hmac_sha256("test", "secret");
105 assert_eq!(sig1, sig2);
106
107 let sig3 = sign_hmac_sha256("test2", "secret");
108 assert_ne!(sig1, sig3);
109 }
110
111 #[test]
112 fn test_timestamp() {
113 let ts = current_timestamp_ms();
114 assert!(ts > 1577836800000); }
116
117 #[test]
118 fn test_get_signature_format() {
119 let timestamp: u64 = 1658384314791;
120 let api_key = "testkey";
121 let recv_window = 5000;
122 let query = "category=linear&symbol=BTCUSDT";
123 let api_secret = "testsecret";
124
125 let sig = sign_rest_request(timestamp, api_key, recv_window, query, api_secret);
126 assert_eq!(sig.len(), 64);
127 }
128}