asterdex-sdk 0.1.2

AsterDex Futures SDK v3 — Rust async client for REST and WebSocket APIs
Documentation
// US-015: Asset and sub-account endpoints

use crate::futures::models::account::{WalletTransferParams, WalletTransferResponse};
use crate::rest::client::RestClient;
use crate::rest::error::AsterDexError;
use crate::rest::response::ApiResponse;

impl RestClient {
    /// Transfer assets between wallets.
    ///
    /// The `transfer_type` field identifies the wallet pair (e.g. spot→futures, futures→spot).
    /// Consult the API docs for valid values.
    ///
    /// # Errors
    /// - `ConfigError` — credentials not set
    /// - `ApiError` — invalid asset, amount, or transfer type
    /// - `RateLimited` — HTTP 429
    /// - `IpBanned` — HTTP 418
    pub async fn wallet_transfer(
        &self,
        params: WalletTransferParams,
    ) -> Result<ApiResponse<WalletTransferResponse>, AsterDexError> {
        let transfer_type_str = params.transfer_type.to_string();
        self.signed_post(
            "/fapi/v3/asset/wallet/transfer",
            &[
                ("asset", params.asset.as_str()),
                ("amount", params.amount.as_str()),
                ("type", &transfer_type_str),
            ],
        )
        .await
    }

    /// Bind a sub-account to the current account.
    ///
    /// The response structure is not yet documented — returned as raw JSON until API docs
    /// are clarified. Refactor to a typed struct in a follow-up story.
    ///
    /// # Errors
    /// - `ConfigError` — credentials not set
    /// - `ApiError` — invalid sub-account UID or binding already exists
    /// - `RateLimited` — HTTP 429
    /// - `IpBanned` — HTTP 418
    pub async fn sub_account_bind(
        &self,
        sub_uid: &str,
    ) -> Result<ApiResponse<serde_json::Value>, AsterDexError> {
        self.signed_post("/fapi/v3/apiKeySubAccountBindings", &[("subUid", sub_uid)])
            .await
    }
}

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

    // US-015: wallet_transfer without credentials returns ConfigError
    #[tokio::test]
    async fn wallet_transfer_without_credentials_returns_config_error() {
        let server = mockito::Server::new_async().await;
        // No mock needed — error is returned before any HTTP call
        let client = RestClient::new_public(&server.url()).unwrap();
        let params = WalletTransferParams {
            asset: "USDT".to_string(),
            amount: "100.0".to_string(),
            transfer_type: 1,
        };
        let result = client.wallet_transfer(params).await;
        assert!(
            matches!(result, Err(AsterDexError::ConfigError { .. })),
            "expected ConfigError, got: {:?}",
            result
        );
        drop(server);
    }
}