# Monaco SDK
Typed Rust client for the Monaco DEX REST API, auto-generated at build time from the vendored OpenAPI spec via [progenitor](https://docs.rs/progenitor). The `build.rs` reads `rust-sdk/openapi/openapi.yaml`, generates typed Rust code, and includes it via `include!`.
## Code Generation
```mermaid
graph LR
YAML[openapi/openapi.yaml] --> BS[build.rs]
BS --> Gen[progenitor::Generator]
Gen --> OUT["$OUT_DIR/api.rs"]
OUT --> Lib["src/lib.rs<br/>include!()"]
Lib --> Client[monaco_sdk::Client]
```
## Quick Start
```toml
[dependencies]
monaco-sdk = "0.5"
tokio = { version = "1", features = ["full"] }
```
```rust
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = monaco_sdk::Client::new("https://develop.apimonaco.xyz");
let pairs = client.list_trading_pairs(None, Some(true), None, None, None, None).await?.into_inner();
println!("{:?}", pairs.data);
Ok(())
}
```
## Authentication
Authenticated endpoints require a Bearer JWT from the challenge/verify flow:
```rust
use monaco_sdk::types::{ChallengeRequest, VerifyRequest};
let client = monaco_sdk::Client::new("https://develop.apimonaco.xyz");
// 1. Request a challenge
let challenge = client
.create_challenge(&ChallengeRequest {
address: "0xYourAddress".parse().unwrap(),
chain_id: None,
client_id: None,
})
.await?
.into_inner();
// 2. Sign the challenge message and verify
let verify = client
.verify_signature(&VerifyRequest {
address: "0xYourAddress".parse().unwrap(),
chain_id: None,
client_id: None,
nonce: challenge.nonce.unwrap().parse().unwrap(),
signature: "<signed-message>".parse().unwrap(),
})
.await?
.into_inner();
// 3. Use the JWT for authenticated requests
use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION};
let mut headers = HeaderMap::new();
let token = format!("Bearer {}", verify.access_token.unwrap());
headers.insert(AUTHORIZATION, HeaderValue::from_str(&token).unwrap());
let http = reqwest::ClientBuilder::new().default_headers(headers).build().unwrap();
let authed = monaco_sdk::Client::new_with_client("https://develop.apimonaco.xyz", http);
```
## API Coverage
| **Market data** | `list_trading_pairs`, `get_trading_pair_by_id`, `get_market_metadata`, `get_candles` |
| **Orderbook** | `get_orderbook_snapshot` |
| **Trades** | `get_trades` |
| **Orders** | `create_order`, `replace_order`, `cancel_order`, `get_orders`, `get_order_by_id` |
| **Batch orders** | `batch_create_orders`, `batch_replace_orders`, `batch_cancel_orders`, `batch_cancel_all`, `batch_cancel_all_by_pair` |
| **Accounts** | `get_user_profile`, `get_user_balances`, `get_user_balance_by_asset`, `get_user_movements` |
| **Sub-accounts** | `list_sub_accounts_with_balances`, `create_sub_account_limit`, `get_sub_account_limits`, `update_sub_account_limit`, `delete_sub_account_limit` |
| **Auth** | `create_challenge`, `verify_signature`, `refresh_token`, `revoke_session`, `authenticate_backend` |
| **Fees** | `simulate_fees` |
| **Other** | `submit_whitelist`, `mint_tokens`, `health_check` |
All request/response types are in `monaco_sdk::types`.
## Gotchas
- **Generated code**: The entire client is generated at build time. To update, refresh the vendored `openapi.yaml` from the repo-level docs artifact before publishing.
- **No runtime codegen**: The spec is included via `include!` from `OUT_DIR`, so builds require the OpenAPI YAML to be present.
## Build & Test
By default `cargo test -p monaco-sdk` runs only the static `packaging_regressions`
checks — the rest of the suite requires a live Monaco backend and is gated
behind `#[ignore]`.
```bash
# Static checks only (no backend needed)
cargo test -p monaco-sdk
# Full live suite against an environment
API_BASE_URL=https://develop.apimonaco.xyz \
PRIVATE_KEY=0x...your_funded_test_key \
cargo test -p monaco-sdk -- --include-ignored --test-threads=1
```
The live tests are grouped by responsibility and each file can be targeted
individually (`--test auth`, `--test faucet`, etc.):
| `public.rs` | Unauthenticated market data: `list_trading_pairs`, `get_orderbook_snapshot`, `get_candles`, `get_trades`, `health_check`, … |
| `auth.rs` | Challenge / verify / refresh round-trip, plus 401 rejection on an unauthenticated profile call |
| `accounts.rs` | `get_user_balances`, `get_user_movements`, `simulate_fees` |
| `faucet.rs` | `mint_tokens` contract (minted entries carry asset IDs, amounts, tx hashes) and rate-limit slot accounting |
| `sub_accounts.rs` | `list_sub_accounts_with_balances` + 4xx envelope on an unknown sub-account |
| `orders.rs` | Full order lifecycle (create / replace / cancel), batch create/cancel |
| `end_to_end.rs` | Two user-visible journeys: fresh wallet onboarding (challenge → verify → faucet → simulate fees) and funded wallet order lifecycle |
For published-crate smoke testing, the sibling
[`sdk-release-verify`](../sdk-release-verify/) harness consumes
`monaco-grpc-sdk` from crates.io so it exercises the crate an external
`cargo add` user would see.