Skip to main content

TypeScript Client

info

A reference client that ties the pieces together: get a bearer token, read markets and server time, use the SDK's order builders to assemble a signed order from a deposited note, submit it, and subscribe to the order and fill streams. The SDK owns the cryptography - note commitments, the input proof, and the anchor pool - so your code works in economic terms.

What the SDK does for you

The hard part of a Darknyx order is its cryptographic backing: the collateral-note commitment, the zero-knowledge input proof, the owner-commitment opening, and the continuation anchor pool (see Place Order). The SDK derives all of it from your seed and a spendable note, and signs the canonical body with your trading key. You supply the intent - side, amount, price, time-in-force - and get back a ready-to-send order.

The SDK also ships:

  • Order builders - presets for market, all-or-none, and good-til-time orders over the native fields.
  • Stream clients - per-account order-lifecycle and fill subscriptions, with the fill-memo verification built in.
  • System helpers - server time (for slot-based expiry) and the degraded-mode status.

Client implementation

import {
marketPolicy,
aonPolicy,
gttLimitPolicy,
OrderSide,
fetchServerTime,
fetchSystemStatus,
subscribeOrderUpdates,
subscribeFills,
} from "@darknyx/sdk";

class DarknyxClient {
private token: string | null = null;

constructor(private gateway: string) {
this.gateway = gateway.replace(/\/$/, "");
}

// ── Auth ──────────────────────────────────────────────────────────────
async login(apiKey: string, apiSecret: string, passphrase: string) {
const r = await fetch(`${this.gateway}/auth/token`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ api_key: apiKey, api_secret: apiSecret, passphrase }),
});
if (!r.ok) throw new Error(`auth ${r.status}: ${await r.text()}`);
const body = await r.json();
this.token = body.access_token;
return body; // { access_token, token_type, expires_in, account_id }
}

private auth() {
if (!this.token) throw new Error("call login() first");
return { Authorization: `Bearer ${this.token}`, "Content-Type": "application/json" };
}

// ── Reference data + health ──────────────────────────────────────────
getInstruments() {
return fetch(`${this.gateway}/instruments`).then((r) => r.json());
}
systemStatus() {
return fetchSystemStatus(this.gateway);
}
serverTime() {
return fetchServerTime(this.gateway);
}

// ── Orders ───────────────────────────────────────────────────────────
// `order` is a fully-built, signed wire body - produced by the SDK's
// order-builder from your keys + a spendable note (it fills the note
// commitment, the VALID_INPUT proof, the anchor pool, and the signature).
placeOrder(order: object) {
return fetch(`${this.gateway}/orders`, {
method: "POST",
headers: this.auth(),
body: JSON.stringify(order),
}).then((r) => r.json());
}

cancelOrder(orderId: string, cancel: object) {
return fetch(`${this.gateway}/orders/${orderId}`, {
method: "DELETE",
headers: this.auth(),
body: JSON.stringify(cancel),
}).then((r) => r.json());
}

modifyOrder(orderId: string, modify: object) {
return fetch(`${this.gateway}/orders/${orderId}`, {
method: "PUT",
headers: this.auth(),
body: JSON.stringify(modify),
}).then((r) => r.json());
}

getOrder(orderId: string) {
return fetch(`${this.gateway}/orders/${orderId}`, { headers: this.auth() }).then((r) => r.json());
}

settlementStatus(batchId: string) {
return fetch(`${this.gateway}/settlement/status/${batchId}`, { headers: this.auth() }).then((r) => r.json());
}
}

Building an order with the SDK builders

The builders set the execution-policy fields (type, price limit, fill size, expiry) for a common intent. You merge a policy with the collateral the SDK derives from a deposited note.

// A resting bid, good for the next 10 minutes (GTT).
const { slot, unix_ms } = await client.serverTime();
const gttPolicy = gttLimitPolicy({
priceLimit: 150_000_000n,
serverSlot: slot,
serverUnixMs: unix_ms,
expiryUnixMs: Date.now() + 10 * 60 * 1000,
});

// A market bid: IOC capped at the worst price you'll pay.
const market = marketPolicy({ side: OrderSide.Bid, priceCap: 155_000_000n });

// An all-or-none resting bid.
const aon = aonPolicy({ amount: 10_000_000n, priceLimit: 150_000_000n });

// The SDK's order-builder takes a policy + a spendable note + your keys and
// returns the full signed wire body (note commitment, input proof, anchor pool,
// signature). Submit it as-is.
const order = await sdk.buildOrder({ symbol: "SOL-USDC", side: OrderSide.Bid, amount: 10_000_000n, policy: gttPolicy, note });
const res = await client.placeOrder(order);
console.log("placed", res.order_id, res.status);

Streaming order and fill events

// Per-account order lifecycle: partial / full fill, cancel, expiry.
const orders = subscribeOrderUpdates({
gatewayWsUrl: WSS,
token: client["token"]!,
onUpdate: (u) => {
if (u.kind === "partially_filled") console.log("partial", u.filled_quantity, "resting", u.new_amount);
if (u.kind === "fully_filled") console.log("filled", u.order_id);
},
onResync: () => console.warn("orders stream lagged - reconcile via GET /orders/:id"),
});

// Per-account fills: verified change-note memos (the SDK checks each memo's
// anchor + commitment binding before handing it to you).
const fills = subscribeFills({
gatewayWsUrl: WSS,
token: client["token"]!,
masterSeed,
ownerCommitment,
store: noteStore,
onFill: (rec) => console.log("change note stored", rec.commitment),
onResync: () => console.warn("fills stream lagged - backfill then reopen"),
});

Submitting over the trading socket

For a high-frequency client, submit orders over the WebSocket trading socket instead of REST - one warm connection, plus cancel-on-disconnect.

const ws = new WebSocket(`${WSS}/ws/trading?token=${TOKEN}&cancel_on_disconnect=true`);
ws.onopen = () => ws.send(JSON.stringify({ op: "order.place", request_id: "r1", params: order }));
ws.onmessage = (e) => {
const msg = JSON.parse(e.data);
if (msg.op === "error") console.error(msg.code, msg.message);
else if (msg.op === "order.place") console.log("accepted", msg.result.order_id);
};

Usage

const client = new DarknyxClient("https://<gateway-host>");
await client.login(API_KEY, API_SECRET, PASSPHRASE);

const status = await client.systemStatus();
if (status.degraded) throw new Error("venue degraded - back off");

const markets = await client.getInstruments();
console.log(markets.instruments.map((m) => m.symbol));

// build + place an order (see above), then watch its lifecycle on the streams.
Verify the engine first

For the full trust guarantee, verify the enclave's attestation against an expected measurement before sending order intent - the SDK ships a helper that runs the attestation chain for you. Skipping it gives you a private channel to a machine, not a verified one.