limitless-exchange-rust-sdk 1.0.13

Rust SDK for Limitless Exchange CLOB and NegRisk trading
Documentation
use base64::Engine;
use hmac::{Hmac, Mac};
use serde::{Deserialize, Serialize};
use sha2::Sha256;
use zeroize::{Zeroize, Zeroizing};

use crate::errors::{LimitlessError, Result};

type HmacSha256 = Hmac<Sha256>;

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct HmacCredentials {
    pub token_id: String,
    pub secret: String,
}

impl Drop for HmacCredentials {
    fn drop(&mut self) {
        self.secret.zeroize();
    }
}

pub fn build_hmac_message(timestamp: &str, method: &str, path: &str, body: &str) -> String {
    format!(
        "{}\n{}\n{}\n{}",
        timestamp,
        method.to_uppercase(),
        path,
        body
    )
}

pub fn compute_hmac_signature(
    secret: &str,
    timestamp: &str,
    method: &str,
    path: &str,
    body: &str,
) -> Result<String> {
    let key = Zeroizing::new(base64::engine::general_purpose::STANDARD.decode(secret)?);
    let mut mac =
        HmacSha256::new_from_slice(&key).map_err(|err| LimitlessError::Signing(err.to_string()))?;
    mac.update(build_hmac_message(timestamp, method, path, body).as_bytes());
    Ok(base64::engine::general_purpose::STANDARD.encode(mac.finalize().into_bytes()))
}

#[cfg(test)]
mod tests {
    use super::build_hmac_message;

    #[test]
    fn builds_hmac_message() {
        let message = build_hmac_message("ts", "post", "/orders", r#"{"x":1}"#);
        assert_eq!(message, "ts\nPOST\n/orders\n{\"x\":1}");
    }
}