# AsterDex Futures SDK (Rust)

[![Rust](https://img.shields.io/badge/rust-stable-orange)](https://www.rust-lang.org)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

Async Rust SDK for the AsterDex Futures API v3. Covers REST and WebSocket APIs with EIP-712 signing.

## Features

- Full REST API coverage (trading, market data, account, listen key, MMP, asset)
- WebSocket market data streams (13 event types with typed deserialization)
- WebSocket user data stream (order updates, account updates, config updates)
- EIP-712 Ethereum signing via [alloy-rs](https://alloy.rs)
- Typed errors with rate-limit header exposure
- Raw JSON passthrough for untyped access

## Installation

Add to `Cargo.toml`:

```toml
[dependencies]
asterdex-sdk = "0.1.1"
```

## Quick Start

### REST — Authenticate and place an order

```rust,no_run
use asterdex_sdk::{FuturesClient, PlaceOrderParams};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Load credentials from environment variables:
    // ASTERDEX_USER, ASTERDEX_SIGNER_ADDRESS, ASTERDEX_SIGNER_PRIVATE_KEY, ASTERDEX_BASE_URL
    let client = FuturesClient::from_env()?;

    let order = client.place_order(PlaceOrderParams {
        symbol: "BTCUSDT".to_string(),
        side: "BUY".to_string(),
        order_type: "LIMIT".to_string(),
        quantity: "0.001".to_string(),
        price: Some("30000.0".to_string()),
        time_in_force: Some("GTC".to_string()),
        ..Default::default()
    }).await?;

    println!("Order placed: id={}", order.data.order_id);
    Ok(())
}
```

### WebSocket — Subscribe to market streams

```rust,no_run
use asterdex_sdk::{WebSocketClient, WebSocketMessage};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let ws = WebSocketClient::new("wss://fstream.asterdex-testnet.com")?;
    let mut stream = ws.subscribe(vec!["btcusdt@aggTrade", "btcusdt@markPrice"]).await?;

    while let Some(event) = stream.recv().await {
        match event? {
            WebSocketMessage::AggTrade(e) => println!("Trade: {} @ {}", e.symbol, e.price),
            WebSocketMessage::MarkPrice(e) => println!("Mark: {} = {}", e.symbol, e.mark_price),
            _ => {}
        }
    }
    Ok(())
}
```

### WebSocket — User data stream

```rust,no_run
use asterdex_sdk::{FuturesClient, WebSocketClient, UserDataEvent};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let rest = FuturesClient::from_env()?;
    let listen_key = rest.create_listen_key().await?.data.listen_key;

    let ws = WebSocketClient::new("wss://fstream.asterdex-testnet.com")?;
    let mut stream = ws.subscribe_user_data(&listen_key).await?;

    while let Some(event) = stream.next_event().await {
        match event? {
            UserDataEvent::OrderUpdate(e) => {
                println!("Order update: {} {}", e.order.symbol, e.order.order_status)
            }
            _ => {}
        }
    }
    Ok(())
}
```

## Environment Variables

| Variable | Description |
|----------|-------------|
| `ASTERDEX_USER` | 0x-prefixed Ethereum address (trader account) |
| `ASTERDEX_SIGNER_ADDRESS` | 0x-prefixed Ethereum address (signing wallet) |
| `ASTERDEX_SIGNER_PRIVATE_KEY` | 64-char hex private key (no 0x prefix) |
| `ASTERDEX_BASE_URL` | REST base URL (e.g. `https://fapi.asterdex-testnet.com`) |
| `ASTERDEX_WS_BASE_URL` | WebSocket base URL (e.g. `wss://fstream.asterdex-testnet.com`) |
| `ASTERDEX_CHAIN_ID` | EIP-712 domain chainId (testnet: 714) |

Copy `.env.example` to `.env` and fill in your values. Never commit `.env`.

## Architecture

Single-crate Rust library. Key modules:

| Module | Contents |
|--------|----------|
| `auth/` | EIP-712 signing engine, credentials management |
| `rest/` | HTTP client (`RestClient`), response parsing, typed errors |
| `ws/` | WebSocket client, reconnect engine, ping/pong |
| `futures/models/` | All futures request/response types (prices as `String`) |
| `futures/endpoints/` | `impl RestClient` blocks per API group |
| `spot/` | Spot trading domain — placeholder (not yet implemented) |

`FuturesClient` is a type alias for `RestClient`. All futures endpoint methods live under `src/futures/endpoints/` and are automatically available on `FuturesClient`.

See [ARCHITECTURE.md](ARCHITECTURE.md) for full design decisions and ADRs.

## Error Handling

All methods return `Result<ApiResponse<T>, AsterDexError>`. Key error variants:

```rust
pub enum AsterDexError {
    ApiError { code: i64, msg: String },           // API-level error
    RateLimited { retry_after: Option<Duration> },  // HTTP 429
    IpBanned { retry_after: Option<Duration> },     // HTTP 418
    WebSocketError { message: String },
    ConfigError { message: String },
    SerdeError { message: String },
}
```

## Integration Guide

For the complete API reference — all endpoints with request/response structs, usage examples, WebSocket stream types, and error handling patterns — see **[docs/integration_guide.md](docs/integration_guide.md)**.

Topics covered:
- Market data (order book, klines, mark price, funding rate, tickers)
- Trading (place, modify, cancel, query orders; positions; batch orders)
- Account (balance, leverage, margin type, position mode, ADL quantile)
- Listen key lifecycle and user data WebSocket stream
- MMP (Market Maker Protection) endpoints
- Asset / sub-account wallet transfer
- Raw JSON passthrough (`raw_get`, `raw_signed_post`, etc.)
- Full `WebSocketMessage` and `UserDataEvent` type reference

---

## Key Flows by Milestone

| Milestone | Description | Key Flows |
|-----------|-------------|-----------|
| M1 | Authenticate and execute trades via REST | `FuturesClient::from_env()`, `place_order`, `cancel_order`, `get_open_orders`, `get_position_risk`, EIP-712 signing |
| M2 | All market data and key account endpoints via REST | `ping`, `get_exchange_info`, `get_depth`, `get_klines`, `get_balance`, `set_leverage`, `place_batch_orders`, rate limit headers |
| M3 | Real-time market data via WebSocket | `WebSocketClient::new`, `subscribe`, typed stream, auto-reconnect within 3s, transparent ping/pong |
| M4 | Real-time account/order updates via user data stream | `create_listen_key`, `subscribe_user_data`, `OrderUpdate` events, `renew_listen_key` |
| M5 | Full REST coverage and complete polish | `get_all_orders`, `get_user_trades`, `update_mmp`, `raw_get` passthrough, clippy 0 warnings |

## Testing

```bash
# Unit tests (no network required)
cargo test --lib

# Integration tests (requires testnet credentials in environment)
cargo test --test integration -- --ignored
```
