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 or the public API first.
Source
The crate, its tests and runnable examples under examples/.
API reference
Per-operation request and response shapes behind each client method.
Quickstart
Install the SDK
Not on crates.io yet, so depend on the public repository directly:
[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"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:
agara-sdk = { git = "https://github.com/Mirai-Tech-Inc/agara-sdks", rev = "<commit>" }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:
agara-sdk = { git = "https://github.com/Mirai-Tech-Inc/agara-sdks", branch = "main", default-features = false }Create a client
Create a token first, following Authentication:
export AGARA_TOKEN="agt_..."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()?;Read a market and place an order
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.
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 for ticks and the minimum order value.
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:
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
The error type carries the HTTP status and the server's structured problem information, with the recovery guidance already interpreted:
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 has the code list.
Realtime subscriptions
With the streaming feature the client connects to the market and account
channels over one typed WebSocket.
let mut stream = client.connect().await?;
stream.subscribe(/* channels */).await?;
while let Some(frame) = stream.next().await {
println!("{frame:?}");
}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 covers the channels and
Account stream the private one.
Next steps
Pre-signed orders
The one-time setup for signing locally, and why it beats server-side signing.
Account batches
Sign your own split, merge or withdrawal, one per batch.
Streaming
Channels, sequence numbers and what a gap obliges you to do.
Errors
Problem codes and the failures you must reconcile rather than retry.