# Rust

> Install the Rust client, place an order with exact decimal amounts, and turn signing and streaming on or off with cargo features.

`agara-sdk` is an async client built for low latency. Responses are fully typed,
identifiers are newtypes rather than bare strings, and amounts are exact decimals
so nothing is rounded on the way to the wire. It requires a tokio runtime.

Like the Python client it concentrates on trading, and it additionally exposes LP
rewards and rebate balances. It does not wrap market or event discovery, so find
your `token_id` through [Markets](/docs/markets) or the public API first.

<Cards>
<Card title="Source" href="https://github.com/Mirai-Tech-Inc/agara-sdks/tree/main/rust">
The crate, its tests and runnable examples under `examples/`.
</Card>

<Card title="API reference" href="/docs/api-reference">
Per-operation request and response shapes behind each client method.
</Card>
</Cards>

## Quickstart [#quickstart]

<Steps>
<Step>
### Install the SDK [#install-the-sdk]

Not on crates.io yet, so depend on the public repository directly:

<Tabs items={["Cargo.toml","cargo add"]}>
<Tab value="Cargo.toml">
```toml
[dependencies]
agara-sdk = { git = "https://github.com/Mirai-Tech-Inc/agara-sdks", branch = "main" }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
rust_decimal_macros = "1"
```
</Tab>

<Tab value="cargo add">
```bash
cargo add --git https://github.com/Mirai-Tech-Inc/agara-sdks agara-sdk
cargo add tokio --features macros,rt-multi-thread
cargo add rust_decimal_macros
```
</Tab>
</Tabs>

Once the crate is published that first dependency becomes
`agara-sdk = "<version>"`, and nothing else on this page changes. Pin to a commit
rather than a branch for a reproducible build:

```toml
agara-sdk = { git = "https://github.com/Mirai-Tech-Inc/agara-sdks", rev = "<commit>" }
```
</Step>

<Step>
### Choose your features [#choose-your-features]

Default features pull in `signing`, which brings an Ethereum library for EIP-712,
and `streaming`, which brings a WebSocket client. For a REST-only build that
compiles faster and carries fewer dependencies, turn both off:

```toml
agara-sdk = { git = "https://github.com/Mirai-Tech-Inc/agara-sdks", branch = "main", default-features = false }
```
</Step>

<Step>
### Create a client [#create-a-client]

Create a token first, following [Authentication](/docs/authentication):

```bash
export AGARA_TOKEN="agt_..."
```

```rust
use agara_sdk::AgaraClient;

let client = AgaraClient::builder()
    .token(std::env::var("AGARA_TOKEN").expect("AGARA_TOKEN"))
    .base_url(std::env::var("AGARA_BASE_URL").expect("AGARA_BASE_URL"))
    .build()?;
```
</Step>

<Step>
### Read a market and place an order [#read-a-market-and-place-an-order]

```rust
use agara_sdk::{ids::{Side, TokenId}, AgaraClient};
use rust_decimal_macros::dec;

#[tokio::main]
async fn main() -> agara_sdk::Result<()> {
    let client = AgaraClient::builder()
        .token(std::env::var("AGARA_TOKEN").expect("AGARA_TOKEN"))
        .build()?;

    let token = TokenId::new("21742...36455");

    let book = client.get_orderbook(&token).await?;
    println!("best bid {:?} / ask {:?}", book.best_bid(), book.best_ask());

    let accepted = client
        .place_limit_order()
        .token_id(token)
        .side(Side::Buy)
        .price(dec!(0.60))
        .shares(dec!(1))
        .call()
        .await?;

    let final_order = client.wait_for_terminal(&accepted.order_id).await?;
    println!("{:?}", final_order.status);

    Ok(())
}
```

Acceptance is not settlement. `wait_for_terminal` polls until the order reaches a
terminal status, and that status still needs inspecting: a rejected or partially
filled order is a normal terminal outcome, not a transport failure.
</Step>
</Steps>

## Amounts [#amounts]

Prices and sizes take `rust_decimal::Decimal`, so `dec!(0.60)` is exactly sixty
cents rather than the nearest float. Conversion to the wire format happens inside
the client, and a value finer than one micro unit is refused rather than rounded.

Accessors are explicit (`as_decimal`, `as_str`) instead of dereferencing, so a
typed identifier cannot be passed where a plain string was meant. See
[Conventions](/docs/conventions) for ticks and the minimum order value.

## Pagination [#pagination]

List methods take a limit and an optional cursor, and return the cursor for the
next page. Pass `pagination.next_cursor` back until it is empty:

```rust
let mut cursor = None;

loop {
    let page = client.list_trades(Some(500), cursor.clone()).await?;
    for trade in &page.trades {
        println!("{:?}", trade.fill_id);
    }

    cursor = page.pagination.next_cursor.clone();
    if cursor.is_none() {
        break;
    }
}
```

Keep that cursor to resume a scan later rather than replaying from the start.

## Error handling [#error-handling]

The error type carries the HTTP status and the server's structured problem
information, with the recovery guidance already interpreted:

```rust
match client.cancel_all_orders().await {
    Ok(accepted) => println!("{accepted:?}"),
    Err(error) => {
        eprintln!("status {:?}", error.status_code());
        if error.is_retryable() {
            if let Some(after) = error.retry_after() {
                // the server asked you to wait this long
            }
        }
        return Err(error);
    }
}
```

`is_retryable` reflects what the server said, not a guess from the status code,
so an unrecognised problem never authorises an automatic retry. A request that
fails in transit may still have taken effect, so read the state back before
sending it again. [Errors](/docs/errors) has the code list.

## Realtime subscriptions [#realtime-subscriptions]

With the `streaming` feature the client connects to the market and account
channels over one typed WebSocket.

<Tabs items={["Iterator","Callback"]}>
<Tab value="Iterator">
```rust
let mut stream = client.connect().await?;
stream.subscribe(/* channels */).await?;

while let Some(frame) = stream.next().await {
    println!("{frame:?}");
}
```
</Tab>

<Tab value="Callback">
```rust
let mut stream = client.connect().await?;
stream.subscribe(/* channels */).await?;

stream
    .run(|frame| async move {
        println!("{frame:?}");
    })
    .await?;
```
</Tab>
</Tabs>

`list_subscriptions` reports what the server believes you are subscribed to,
which is worth checking after a reconnect. Reconnection restores subscriptions
but does not replay what you missed, so treat it as a prompt to take a fresh
snapshot. [Streaming](/docs/streaming) covers the channels and
[Account stream](/docs/account-stream) the private one.

## Next steps [#next-steps]

<Cards>
<Card title="Pre-signed orders" href="/docs/api-traders/pre-signed-orders">
The one-time setup for signing locally, and why it beats server-side signing.
</Card>

<Card title="Account batches" href="/docs/api-traders/account-batches">
Sign your own split, merge or withdrawal, one per batch.
</Card>

<Card title="Streaming" href="/docs/streaming">
Channels, sequence numbers and what a gap obliges you to do.
</Card>

<Card title="Errors" href="/docs/errors">
Problem codes and the failures you must reconcile rather than retry.
</Card>
</Cards>
