use std::collections::HashMap;
use crate::core::{
hmac_sha256_hex, timestamp_seconds,
Credentials, ExchangeResult,
};
#[derive(Clone)]
pub struct PhemexAuth {
api_key: String,
api_secret: String,
}
impl PhemexAuth {
pub fn new(credentials: &Credentials) -> ExchangeResult<Self> {
Ok(Self {
api_key: credentials.api_key.clone(),
api_secret: credentials.api_secret.clone(),
})
}
fn get_expiry(&self) -> u64 {
timestamp_seconds() + 60
}
pub fn sign_request(
&self,
path: &str,
query: &str,
body: &str,
) -> HashMap<String, String> {
let expiry = self.get_expiry();
let expiry_str = expiry.to_string();
let sign_string = format!("{}{}{}{}", path, query, expiry_str, body);
let signature = hmac_sha256_hex(
self.api_secret.as_bytes(),
sign_string.as_bytes(),
);
let mut headers = HashMap::new();
headers.insert("x-phemex-access-token".to_string(), self.api_key.clone());
headers.insert("x-phemex-request-expiry".to_string(), expiry_str);
headers.insert("x-phemex-request-signature".to_string(), signature);
headers.insert("Content-Type".to_string(), "application/json".to_string());
headers
}
pub fn sign_websocket(&self) -> (String, u64, String) {
let expiry = self.get_expiry();
let message = format!("{}{}", self.api_key, expiry);
let signature = hmac_sha256_hex(
self.api_secret.as_bytes(),
message.as_bytes(),
);
(self.api_key.clone(), expiry, signature)
}
pub fn api_key(&self) -> &str {
&self.api_key
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sign_request_get() {
let credentials = Credentials::new("test_key", "test_secret");
let auth = PhemexAuth::new(&credentials).unwrap();
let headers = auth.sign_request("/spot/wallets", "", "");
assert!(headers.contains_key("x-phemex-access-token"));
assert!(headers.contains_key("x-phemex-request-expiry"));
assert!(headers.contains_key("x-phemex-request-signature"));
assert_eq!(headers.get("Content-Type"), Some(&"application/json".to_string()));
assert_eq!(headers.get("x-phemex-access-token"), Some(&"test_key".to_string()));
}
#[test]
fn test_sign_request_with_query() {
let credentials = Credentials::new("test_key", "test_secret");
let auth = PhemexAuth::new(&credentials).unwrap();
let headers = auth.sign_request("/orders/activeList", "symbol=BTCUSD", "");
assert!(headers.contains_key("x-phemex-request-signature"));
}
#[test]
fn test_sign_request_with_body() {
let credentials = Credentials::new("test_key", "test_secret");
let auth = PhemexAuth::new(&credentials).unwrap();
let body = r#"{"symbol":"BTCUSD","side":"Buy"}"#;
let headers = auth.sign_request("/orders", "", body);
assert!(headers.contains_key("x-phemex-request-signature"));
}
#[test]
fn test_sign_websocket() {
let credentials = Credentials::new("test_key", "test_secret");
let auth = PhemexAuth::new(&credentials).unwrap();
let (api_key, expiry, signature) = auth.sign_websocket();
assert_eq!(api_key, "test_key");
assert!(expiry > timestamp_seconds());
assert!(!signature.is_empty());
assert_eq!(signature.len(), 64);
}
}