limitless-exchange-rust-sdk 1.0.8

Rust SDK for Limitless Exchange CLOB and NegRisk trading
Documentation
use serde::{Deserialize, Serialize};

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

#[derive(Clone)]
pub struct ApiTokenService {
    client: HttpClient,
}

impl ApiTokenService {
    pub fn new(client: HttpClient) -> Self {
        Self { client }
    }

    pub async fn derive_token(
        &self,
        identity_token: &str,
        input: &DeriveApiTokenInput,
    ) -> Result<DeriveApiTokenResponse> {
        if identity_token.trim().is_empty() {
            return Err(LimitlessError::invalid_input(
                "identity token is required for derive_token",
            ));
        }
        self.client
            .post_with_identity("/auth/api-tokens/derive", identity_token, input)
            .await
    }

    pub async fn list_tokens(&self) -> Result<Vec<ApiToken>> {
        self.client.require_auth("list_tokens")?;
        self.client.get("/auth/api-tokens").await
    }

    pub async fn get_capabilities(&self, identity_token: &str) -> Result<PartnerCapabilities> {
        if identity_token.trim().is_empty() {
            return Err(LimitlessError::invalid_input(
                "identity token is required for get_capabilities",
            ));
        }
        self.client
            .get_with_identity("/auth/api-tokens/capabilities", identity_token)
            .await
    }

    pub async fn revoke_token(&self, token_id: &str) -> Result<String> {
        self.client.require_auth("revoke_token")?;
        let response: MessageResponse = self
            .client
            .delete(&format!(
                "/auth/api-tokens/{}",
                urlencoding::encode(token_id)
            ))
            .await?;
        Ok(response.message)
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DeriveApiTokenInput {
    #[serde(default)]
    pub label: Option<String>,
    #[serde(default)]
    pub scopes: Vec<String>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ApiTokenProfile {
    pub id: i32,
    pub account: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DeriveApiTokenResponse {
    #[serde(rename = "apiKey")]
    pub api_key: String,
    pub secret: String,
    #[serde(rename = "tokenId")]
    pub token_id: String,
    #[serde(rename = "createdAt")]
    pub created_at: String,
    pub scopes: Vec<String>,
    pub profile: ApiTokenProfile,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ApiToken {
    #[serde(rename = "tokenId")]
    pub token_id: String,
    #[serde(default)]
    pub label: Option<String>,
    pub scopes: Vec<String>,
    #[serde(rename = "createdAt")]
    pub created_at: String,
    #[serde(rename = "lastUsedAt", default)]
    pub last_used_at: Option<String>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PartnerCapabilities {
    #[serde(rename = "partnerProfileId")]
    pub partner_profile_id: i32,
    #[serde(rename = "tokenManagementEnabled")]
    pub token_management_enabled: bool,
    #[serde(rename = "allowedScopes")]
    pub allowed_scopes: Vec<String>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
struct MessageResponse {
    message: String,
}

pub const SCOPE_TRADING: &str = "trading";
pub const SCOPE_ACCOUNT_CREATION: &str = "account_creation";
pub const SCOPE_DELEGATED_SIGNING: &str = "delegated_signing";
pub const SCOPE_WITHDRAWAL: &str = "withdrawal";