polymarket 0.1.0

Rust SDK for Polymarket prediction market - CLOB trading, on-chain operations, and WebSocket streaming
Documentation
use std::collections::HashMap;

use crate::clob::{signing, utilities::get_current_unix_time_secs, ApiCreds, RequestArgs, Result, Signer};

// Header constants
pub const POLY_ADDRESS: &str = "POLY_ADDRESS";
pub const POLY_SIGNATURE: &str = "POLY_SIGNATURE";
pub const POLY_TIMESTAMP: &str = "POLY_TIMESTAMP";
pub const POLY_NONCE: &str = "POLY_NONCE";
pub const POLY_API_KEY: &str = "POLY_API_KEY";
pub const POLY_PASSPHRASE: &str = "POLY_PASSPHRASE";

/// Creates Level 1 Poly headers for a request (wallet signature authentication)
///
/// # Arguments
/// * `signer` - The wallet signer
/// * `nonce` - Optional nonce (defaults to 0)
///
/// # Returns
/// HashMap of headers to add to the HTTP request
pub async fn create_level_1_headers(
    signer: &Signer,
    nonce: Option<u64>,
) -> Result<HashMap<String, String>> {
    let timestamp = get_current_unix_time_secs();
    let nonce = nonce.unwrap_or(0);

    // Sign the CLOB auth message
    let signature = signing::sign_clob_auth_message(
        signer.inner(),
        signer.chain_id(),
        timestamp,
        nonce,
    )
    .await?;

    let mut headers = HashMap::new();
    headers.insert(POLY_ADDRESS.to_string(), signer.address());
    headers.insert(POLY_SIGNATURE.to_string(), signature);
    headers.insert(POLY_TIMESTAMP.to_string(), timestamp.to_string());
    headers.insert(POLY_NONCE.to_string(), nonce.to_string());

    Ok(headers)
}

/// Creates Level 2 Poly headers for a request (API key authentication)
///
/// # Arguments
/// * `signer` - The wallet signer
/// * `creds` - API credentials
/// * `request_args` - Request details (method, path, body)
///
/// # Returns
/// HashMap of headers to add to the HTTP request
pub async fn create_level_2_headers(
    signer: &Signer,
    creds: &ApiCreds,
    request_args: &RequestArgs,
) -> Result<HashMap<String, String>> {
    let timestamp = get_current_unix_time_secs();

    // Build HMAC signature using the pre-serialized Python-compatible JSON string
    let hmac_sig = signing::build_hmac_signature(
        &creds.api_secret,
        &timestamp.to_string(),
        &request_args.method,
        &request_args.request_path,
        request_args.body.as_deref(),
    )?;

    let mut headers = HashMap::new();
    headers.insert(POLY_ADDRESS.to_string(), signer.address());
    headers.insert(POLY_SIGNATURE.to_string(), hmac_sig);
    headers.insert(POLY_TIMESTAMP.to_string(), timestamp.to_string());
    headers.insert(POLY_API_KEY.to_string(), creds.api_key.clone());
    headers.insert(POLY_PASSPHRASE.to_string(), creds.api_passphrase.clone());

    Ok(headers)
}

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

    #[tokio::test]
    async fn test_level_1_headers() {
        let private_key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
        let signer = Signer::new(private_key, 137).unwrap();

        let headers = create_level_1_headers(&signer, None).await;

        assert!(headers.is_ok());
        let h = headers.unwrap();
        assert!(h.contains_key(POLY_ADDRESS));
        assert!(h.contains_key(POLY_SIGNATURE));
        assert!(h.contains_key(POLY_TIMESTAMP));
        assert!(h.contains_key(POLY_NONCE));
    }

    #[tokio::test]
    async fn test_level_2_headers() {
        let private_key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
        let signer = Signer::new(private_key, 137).unwrap();

        let creds = ApiCreds {
            api_key: "test_key".to_string(),
            api_secret: "dGVzdHNlY3JldA==".to_string(), // base64 encoded with proper padding
            api_passphrase: "test_pass".to_string(),
        };

        let request_args = RequestArgs {
            method: "GET".to_string(),
            request_path: "/test".to_string(),
            body: None,
        };

        let headers = create_level_2_headers(&signer, &creds, &request_args).await;

        assert!(headers.is_ok());
        let h = headers.unwrap();
        assert!(h.contains_key(POLY_ADDRESS));
        assert!(h.contains_key(POLY_SIGNATURE));
        assert!(h.contains_key(POLY_TIMESTAMP));
        assert!(h.contains_key(POLY_API_KEY));
        assert!(h.contains_key(POLY_PASSPHRASE));
    }
}