> ## 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 gRPC SDK

> Get started with Monaco's protobuf-generated Rust gRPC SDK

`monaco-grpc-sdk` is generated from Monaco's protobuf module published to the Buf Schema Registry. Use it when you want protobuf-native service clients over gRPC.

## Add Dependencies

```toml theme={null}
[dependencies]
monaco-grpc-sdk = "1.0"
tonic = { version = "0.14", features = ["transport", "tls-ring", "tls-webpki-roots"] }
tokio = { version = "1", features = ["full"] }

# Request signing for authenticated calls (see "Authenticate requests").
ed25519-dalek = "2"
prost = "0.14"
sha2 = "0.10"
hex = "0.4"
uuid = { version = "1", features = ["v4"] }
```

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

### Optional `serde` feature

Turn on the `serde` feature to derive `serde::Serialize` and `serde::Deserialize` on every generated message and enum, in the same JSON shape the REST API (`/api/v1/...`) sends and accepts:

```toml theme={null}
monaco-grpc-sdk = { version = "1.0", features = ["serde"] }
```

* Field names are camelCase, and the REST snake\_case aliases are accepted on input.
* The uint64 lease and quote fences (`generation`, `quoteRevision`, `expectedGeneration`) are decimal strings.
* An unset optional field is omitted where REST omits it and serialized as `null` elsewhere (an unset `Position.opened_at` serializes as `"openedAt": null`). A key the server omits reads as unset.
* The request types that REST refuses unknown fields on refuse them here too.

The feature is off by default and changes nothing on the gRPC wire.

## Connect and List Pairs

The gRPC service is exposed on the same staging host as the REST API. Connect to the host on port `443`; the load balancer routes gRPC method paths such as `/monaco.api.*` to Monaco's gRPC API.

```rust theme={null}
use monaco_grpc_sdk::monaco::api::market::{
    market_service_client::MarketServiceClient,
    ListTradingPairsRequest,
};
use tonic::transport::{Channel, ClientTlsConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let channel = Channel::from_static("https://staging.apimonaco.xyz")
        .tls_config(ClientTlsConfig::new().with_webpki_roots())?
        .connect()
        .await?;

    let mut market = MarketServiceClient::new(channel);

    let response = market
        .list_trading_pairs(ListTradingPairsRequest {
            page: Some(1),
            page_size: Some(20),
            market_type: None,
            base_token: None,
            quote_token: None,
            is_active: Some(true),
        })
        .await?
        .into_inner();

    for pair in response.trading_pairs {
        println!("{} {}", pair.symbol, pair.id);
    }

    Ok(())
}
```

## Authenticate requests

Market data is public. Trading and account RPCs are **tokenless**: sign every
request with the ed25519 **session key** you registered through
`AuthService.Challenge`/`Verify`, and attach the signature as metadata. The
server verifies it against your stored public key.

The signature covers the canonical string `POST\n<method-path>\n<timestamp-ms>\nSHA256(request bytes)`,
where the request bytes are the **prost-encoded message** — the same bytes tonic
sends — so hash `msg.encode_to_vec()` and bind the full leading-slash method
path. Add `ed25519-dalek`, `prost`, `sha2`, and `hex` alongside the SDK.

The raw generated client keeps `idempotency_key` optional and does not generate it. Create and retain the key before protobuf encoding or signing. Persist the prepared message and key before sending if recovery must survive a process restart.

```rust theme={null}
use ed25519_dalek::{Signer, SigningKey};
use monaco_grpc_sdk::monaco::api::orders::{
    orders_service_client::OrdersServiceClient, CreateOrderRequest,
};
use prost::Message;
use sha2::{Digest, Sha256};
use std::time::{SystemTime, UNIX_EPOCH};
use tonic::transport::{Channel, ClientTlsConfig};
use tonic::Request;

/// Wrap a request with the three `x-monaco-*` signing metadata entries.
fn signed_request<T: Message>(msg: T, method_path: &str, key: &SigningKey) -> Request<T> {
    let hash = hex::encode(Sha256::digest(msg.encode_to_vec()));
    // Real current time in milliseconds; a stale or far-future value is rejected.
    let timestamp = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .expect("system clock before epoch")
        .as_millis()
        .to_string();
    let signing_string = format!("POST\n{method_path}\n{timestamp}\n{hash}");
    let signature = hex::encode(key.sign(signing_string.as_bytes()).to_bytes());
    let public_key = hex::encode(key.verifying_key().to_bytes());

    let mut request = Request::new(msg);
    let md = request.metadata_mut();
    md.insert("x-monaco-publickey", public_key.parse().expect("ascii metadata"));
    md.insert("x-monaco-timestamp", timestamp.parse().expect("ascii metadata"));
    md.insert("x-monaco-signature", signature.parse().expect("ascii metadata"));
    request
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let channel = Channel::from_static("https://staging.apimonaco.xyz")
        .tls_config(ClientTlsConfig::new().with_webpki_roots())?
        .connect()
        .await?;

    // ed25519 session key registered via AuthService.Challenge/Verify (32-byte
    // hex seed). The server verifies against its public key — there is no token.
    let seed: [u8; 32] = hex::decode(std::env::var("MONACO_SESSION_SEED")?)?
        .try_into()
        .expect("MONACO_SESSION_SEED must be 32-byte hex");
    let key = SigningKey::from_bytes(&seed);

    let mut orders = OrdersServiceClient::new(channel);
    const CREATE_ORDER: &str = "/monaco.api.orders.OrdersService/CreateOrder";

    let submission_key = uuid::Uuid::new_v4().to_string();
    let create = CreateOrderRequest {
        idempotency_key: Some(submission_key),
        trading_pair_id: "TRADING_PAIR_UUID".into(), // from MarketService.ListTradingPairs
        order_type: "LIMIT".into(),
        side: "BUY".into(),
        quantity: "0.01".into(),
        price: Some("60000".into()),
        trading_mode: Some("SPOT".into()),
        time_in_force: Some("GTC".into()),
        ..Default::default()
    };
    // Save `create` before sending; retain it for recovery.
    let response = orders
        .create_order(signed_request(create.clone(), CREATE_ORDER, &key))
        .await?
        .into_inner();
    println!("created order: {response:?}");

    Ok(())
}
```

For a lost create response, re-sign `create.clone()` with a fresh timestamp; keep the same payload and key. Give every batch-create item a distinct key before encoding and signing the batch. Protection lasts 24 hours after acceptance and retries do not extend it. Do not retry an unresolved submission after expiry. This example does not retry automatically.

Each call re-signs with a fresh timestamp and body hash, so signatures are never
reused. A `UNAUTHENTICATED` status on a previously working call means the session
expired or was revoked — re-run challenge/verify and retry; do not loop against a
persistent `UNAUTHENTICATED`. The [authenticated gRPC quickstart](/reference/grpc#authenticate-requests)
covers the metadata, error handling, and [golden signing vectors](/reference/grpc#golden-signing-vectors)
you can check a Rust signer against.

## Release Channels

| Version pattern | Source | Purpose |
| - | - | - |
| `X.Y.Z` | every SDK release on `main` | Latest stable |
| `X.Y.Z-prerelease.<sha>` | a `release/*` branch | Release candidate, opt in via exact pin |
| `X.Y.Z-develop.<sha>` | every SDK-affecting merge to `develop` | Dev snapshot, opt in via exact pin |

Release candidates and dev snapshots use a SemVer pre-release suffix, so a default `cargo add monaco-grpc-sdk` ignores them. Pin explicitly to opt in:

```toml theme={null}
monaco-grpc-sdk = "=1.0.74-develop.367860f"
```

See the [gRPC Protos reference](/grpc/protos) for the complete service list and message schemas.


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