acta-maker-sdk 0.2.0

Rust SDK for Acta options market makers — WebSocket client, order signing, message types
Documentation

acta-maker-sdk

Rust SDK for Acta options market makers. Handles WebSocket connectivity, authentication, order signing, and message serialization.

Two API levels:

  • ManagedWs — auto-reconnect, auto-auth, send_await() for request-response. Use this for production bots.
  • WsClient — raw WebSocket client. You manage the connection yourself. Good for scripts and testing.

No features enabled by default — lightweight core with opt-in WS and Solana support.

Install

[dependencies]
acta-maker-sdk = { version = "0.1.0", features = ["ws-client"] }
Feature What it enables
ws-client WebSocket client (WsClient, ManagedWs), requires tokio
chain Solana instruction builders (DepositPremium, WithdrawPremium, FundPosition)
chain-rpc Solana RPC queries (extends chain)
test-helpers Test utilities (ManagedWsHandle::test_handle, message injection)

Quick start (ManagedWs)

use acta_maker_sdk::*;
use acta_maker_sdk::ws::managed::*;
use acta_maker_sdk::ws::types::*;
use std::sync::Arc;

let signer = BytesSigner::from_keypair(&keypair_bytes);
let signer_for_auth = signer.clone();

let config = ManagedWsConfig::new(
    "wss://devnet-api.acta.markets/maker",
    HelloData {
        protocol_version: WS_PROTOCOL_VERSION.to_string(),
        features: vec![],
        client_name: Some("my-bot".to_string()),
        client_version: Some("0.1.0".to_string()),
    },
    signer.pubkey_base58(),
    Arc::new(move |challenge: &str| {
        Ok(signer_for_auth.sign_message_base58(challenge.as_bytes()))
    }),
);

let handle = spawn_managed_ws(config);
// handle.send(), handle.subscribe_messages(), handle.send_await()

Quick start (WsClient)

use acta_maker_sdk::ws::{client::WsClient, types::*};
use acta_maker_sdk::WS_PROTOCOL_VERSION;

let mut client = WsClient::connect("wss://devnet-api.acta.markets/maker").await?;

client.send_hello(HelloData {
    protocol_version: WS_PROTOCOL_VERSION.to_string(),
    features: vec!["quote_expired".to_string()],
    client_name: Some("maker-bot".to_string()),
    client_version: Some("0.1.0".to_string()),
}).await?;

while let Some(msg) = client.next().await {
    match msg? {
        ServerMessage::AuthRequest(data) => {
            client.auth_challenge(AuthChallengeData {
                challenge: data.challenge,
                signature: signer.sign_message_base58(data.challenge.as_bytes()),
                pubkey: signer.pubkey_base58(),
            }).await?;
        }
        ServerMessage::AuthSuccess(data) => {
            println!("authenticated: {}", data.session_id);
        }
        other => println!("server: {other:?}"),
    }
}

Order signing

let args = OrderPreimageArgs { /* ... from RfqBroadcast ... */ };
let order_id = compute_order_id(&args);
let signature = sign_order_id_with_signer(&order_id, &signer);

BytesSigner wraps an Ed25519 keypair with Zeroize on drop. Implement SignerLike to plug in HSM/KMS.

Value sets (string enums)

These are String on the wire; the SDK provides enums for known value sets:

  • PositionType: "covered_call" | "cash_secured_put"
  • QuoteStatus: "pending" | "best" | "outbid" | "filled" | "expired"
  • QuoteCancelReason: "requested" | "risk_check" | "rfq_accepted"
  • RfqAvailableAgainReason: "signature_timeout" | "tx_failed" | "tx_build_failed"
  • RfqCloseReason: "expired" | "taker_cancelled" | "filled" | "market_expired" | "ladder_timeout" | "order_failed" | "signature_timeout"
  • QuoteFinalStatus: "expired" | "outbid" | "cancelled" | "filled"
  • PositionUpdateType: "created" | "funded" | "liquidated" | "settled"

Some status fields are raw strings from the backend (e.g. OrderStatusMessage.status, PositionInfo.status).

Examples

Examples require the ws-client feature:

cargo run --example hello_auth --features ws-client

Subscribe to RFQs + chain events

use acta_maker_sdk::ws::{client::WsClient, types::*};

let mut client = WsClient::connect("wss://devnet-api.acta.markets/maker").await?;
client
    .subscribe(SubscribeData {
        channels: vec![WsChannel::Rfqs, WsChannel::ChainEvents],
        underlying_mints: None,
        quote_mints: None,
    })
    .await?;

Send a quote

use acta_maker_sdk::ws::{client::WsClient, types::*};

client
    .quote(QuoteMessage {
        rfq_id: uuid::Uuid::parse_str("00000000-0000-0000-0000-000000000001").unwrap(),
        strike: Strike::new(100_000_000_000),
        price: Price::new(2_000_000_000),
        valid_until: std::time::UNIX_EPOCH + std::time::Duration::from_secs(1_725_000_000),
        nonce: 42,
        order_id: OrderId::from_hex(
            "0000000000000000000000000000000000000000000000000000000000000001",
        )
        .unwrap(),
        signature: "base58-signature".to_string(),
    })
    .await?;

Discovery APIs

use acta_maker_sdk::ws::{client::WsClient, types::*};

client
    .get_my_quotes(GetMyQuotesMessage {
        active_only: true,
    })
    .await?;

client
    .get_maker_positions(GetMakerPositionsMessage {
        market: None,
        underlying_mint: None,
        status: Some(vec!["open".to_string(), "funded".to_string()]),
        min_expiry_ts: None,
    })
    .await?;

Cancel quote(s)

use acta_maker_sdk::ws::{client::WsClient, types::*};

client
    .cancel_quote(uuid::Uuid::parse_str("00000000-0000-0000-0000-000000000001").unwrap())
    .await?;
client
    .cancel_all_quotes(CancelAllQuotesMessage {
        market: Some("market_pda_base58".to_string()),
    })
    .await?;

Documentation

Full documentation is available at docs.acta.markets:

  • Rust SDK integration guide
  • Wire examples (JSON and Rust)
  • Maker API reference (all WS messages)
  • Sandbox / devnet setup
  • FAQ and troubleshooting

License

MIT