> ## Documentation Index
> Fetch the complete documentation index at: https://docs.0xmonaco.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Rust REST SDK

> Get started with Monaco's HTTP/JSON Rust SDK

`monaco-sdk` is generated from Monaco's OpenAPI spec and covers the REST API surface. Use it when you want HTTP/JSON compatibility and typed Rust methods for market data, account data, and trading workflows.

## Add Dependencies

```toml theme={null}
[dependencies]
monaco-sdk    = "1.0"
alloy         = { version = "1", features = ["signers", "signer-local"] }
reqwest       = { version = "0.13", features = ["json"] }
tokio         = { version = "1", features = ["full"] }
ed25519-dalek = "2"
sha2          = "0.10"
rand          = "0.8"
serde_json    = "1"
uuid          = { version = "1", features = ["v4"] }
```

See [`crates.io/crates/monaco-sdk`](https://crates.io/crates/monaco-sdk) for the published crate.

## Market Data

No authentication is required to read trading pairs.

```rust theme={null}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = monaco_sdk::Client::new("https://staging.apimonaco.xyz");

    let pairs = client
        .list_trading_pairs(None, Some(true), None, None, None, None)
        .await?
        .into_inner();

    let pairs = pairs.trading_pairs.unwrap_or_default();
    for pair in pairs.iter().take(5) {
        println!("{}", pair.symbol.as_deref().unwrap_or("?"));
    }

    let sei_pair = pairs
        .iter()
        .find(|pair| pair.symbol.as_deref() == Some("SEI/USDC"))
        .expect("SEI/USDC pair not found");

    let pair_id = sei_pair.id.unwrap();
```

## Authenticate

Monaco authenticates with a wallet-authorized ed25519 **session key**. Generate a keypair, have the wallet sign a challenge that commits to it, then sign every authenticated request with the session key.

```rust theme={null}
    use alloy::hex;
    use alloy::signers::local::PrivateKeySigner;
    use ed25519_dalek::{Signer as _, SigningKey};
    use rand::rngs::OsRng;
    use serde_json::json;

    let signer: PrivateKeySigner = "0xYOUR_PRIVATE_KEY".parse()?;
    let address = format!("{:?}", signer.address());
    let base = "https://staging.apimonaco.xyz";
    let api_prefix = "/api/v1";
    let http = reqwest::Client::new();

    let session_key = SigningKey::generate(&mut OsRng);
    let session_public_key = hex::encode(session_key.verifying_key().to_bytes());

    let challenge: serde_json::Value = http
        .post(format!("{base}{api_prefix}/auth/challenge"))
        .json(&json!({
            "address": address,
            "clientId": "YOUR_CLIENT_ID",
            "sessionPublicKey": session_public_key,
        }))
        .send().await?.json().await?;

    let message = challenge["message"].as_str().unwrap();
    let nonce = challenge["nonce"].as_str().unwrap();

    let sig = alloy::signers::Signer::sign_message(&signer, message.as_bytes()).await?;
    let sig_hex = format!("0x{}", hex::encode(sig.as_bytes()));

    http.post(format!("{base}{api_prefix}/auth/verify"))
        .json(&json!({
            "address": address,
            "clientId": "YOUR_CLIENT_ID",
            "nonce": nonce,
            "signature": sig_hex,
            "sessionPublicKey": session_public_key,
        }))
        .send().await?.error_for_status()?;
```

## Sign Authenticated Requests

On native targets, the SDK can sign session-authenticated requests automatically:

```rust theme={null}
use monaco_sdk::{Client, RequestSigning, SessionSigner};

let client = Client::new_authenticated(
    base,
    SessionSigner::new(session_key.to_bytes(), RequestSigning::Rfc9421),
);
```

Use your registered session signing key as `session_key`. RFC 9421 signing is opt-in and requires a server that supports it. Use `RequestSigning::Legacy` for existing servers. The authenticated client disables redirects; public operations remain unsigned. See [HTTP message signatures](/reference/http-message-signatures) for the signing profile and migration steps.

The manual example below uses legacy signing. Legacy authenticated REST requests carry `X-Monaco-PublicKey`, `X-Monaco-Timestamp`, and `X-Monaco-Signature`. The signature covers:

```text theme={null}
METHOD
path?query
timestamp_ms
SHA256_hex(body)
```

```rust theme={null}
    use sha2::{Digest, Sha256};
    use std::time::{SystemTime, UNIX_EPOCH};

    async fn signed_post(
        http: &reqwest::Client,
        base: &str,
        api_prefix: &str,
        key: &SigningKey,
        public_key: &str,
        path: &str,
        body: &serde_json::Value,
    ) -> Result<serde_json::Value, Box<dyn std::error::Error>> {
        let request_path = format!("{api_prefix}{path}");
        let body_bytes = serde_json::to_vec(body)?;
        let ts = SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis().to_string();
        let body_hash = hex::encode(Sha256::digest(&body_bytes));
        let signing_string = format!("POST\n{request_path}\n{ts}\n{body_hash}");
        let signature = hex::encode(key.sign(signing_string.as_bytes()).to_bytes());

        let resp = http
            .post(format!("{base}{request_path}"))
            .header("X-Monaco-PublicKey", public_key)
            .header("X-Monaco-Timestamp", ts)
            .header("X-Monaco-Signature", signature)
            .header("Content-Type", "application/json")
            .body(body_bytes)
            .send().await?
            .error_for_status()?;

        Ok(resp.json().await?)
    }
```

## Place a Limit Order

The raw generated client keeps `idempotency_key` optional and does not generate it. Set it yourself (`idempotencyKey` in JSON) before serialization and signing. Retain the prepared payload and key; persist them before sending if recovery must survive a process restart.

```rust theme={null}
    let submission_key = uuid::Uuid::new_v4().to_string();
    let create = json!({
        "idempotencyKey": submission_key,
        "tradingPairId": pair_id,
        "orderType": "LIMIT",
        "side": "BUY",
        "price": "0.0100",
        "quantity": "0.1",
        "timeInForce": "GTC",
    });
    // Save `create` before sending. A recovery attempt reuses this payload.
    let order = signed_post(&http, base, api_prefix, &session_key, &session_public_key, "/orders", &create).await?;

    let order_id = order["orderId"].as_str().unwrap().to_string();
    println!("order {order_id}: {}", order["status"].as_str().unwrap_or("?"));
```

For a lost response, reuse `create` unchanged and sign again with a fresh timestamp. Use a distinct key for each batch-create item, generated before signing the batch. The key protects the original submission for 24 hours after acceptance; retries do not extend that window. Do not retry an unresolved submission after expiry. This example does not retry automatically.

A `GTC` order rests on the book until filled or cancelled. See [Order Management](/developers/trade) for other time-in-force options.

## Cancel an Order

```rust theme={null}
    let cancel = signed_post(&http, base, api_prefix, &session_key, &session_public_key, "/orders/cancel", &json!({
        "orderId": order_id,
    })).await?;

    println!("cancel: {}", cancel["status"].as_str().unwrap_or("?"));

    Ok(())
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.