Read-only client for the shared order discovery service.
import { SharedOrderbookClient } from "@zkscatter/sdk/orderbook";The shared orderbook aggregates orders across all active relayers so
your app can show liquidity without polling each relayer’s /api/orders
endpoint individually.
Client
const orderbook = new SharedOrderbookClient("https://orderbook.zkscatter.xyz", {
fetchImpl: globalThis.fetch,
timeoutMs: 5000,
});
await orderbook.isOnline(); // boolean (probes /health)
await orderbook.getStats(); // SharedOrderbookStats | null
await orderbook.getRelayers(); // SharedRelayer[]
await orderbook.getOrders(500); // SharedOrder[] — flat, optionally limited
await orderbook.getOrdersByPair("ETH/USDC"); // SharedOrder[] for one pairTypes
interface SharedOrderbookStats {
totalOrders: number;
pairs: number;
relayers: number;
}
interface SharedRelayer {
address: string;
url: string;
name?: string; // optional — relayer may not advertise one
orderCount: number;
lastHeartbeat: number; // unix seconds
}
interface SharedOrder {
id: string;
relayer: string;
relayerUrl: string;
nonce: string;
pubKeyAx: string;
sellToken: string;
buyToken: string;
sellAmount: string; // wire-format strings — parse as bigint when needed
buyAmount: string;
minFillAmount: string;
maxFee: number;
expiry: number;
createdAt: number;
}Usage pattern
The orderbook is a read-only convenience layer — it never holds
custody and cannot match. Submit orders directly to a RelayerClient
chosen from the discovery list:
const [orderbookOrders, relayers] = await Promise.all([
orderbook.getOrders("ETH/USDC"),
loadRelayersWithApiInfo(network.contracts.relayerRegistry, provider),
]);
// Pick a relayer that's online and has the lowest fee.
const target = relayers
.filter((r) => r.online)
.sort((a, b) => Number(a.fee) - Number(b.fee))[0];
const client = new RelayerClient(target.url);
await client.submitOrder(order, signature);Filtering client-side
The shared orderbook does not expose a per-relayer pair endpoint;
fetch the full list with getOrders (or getOrdersByPair) and filter
by order.relayer locally.
const all = await orderbook.getOrders();
const byRelayer = all.filter((o) => o.relayer === relayerAddress);
const distinctPairs = new Set(byRelayer.map((o) => `${o.sellToken}/${o.buyToken}`));Common errors
`isOnline()` returns false but `getStats()` succeeds
isOnline only succeeds for 200 OK on /health. Some deployments
expose /api/health instead — pass a base URL with the suffix or
bypass the check.
Stale `lastHeartbeat`
Treat anything older than ~60s as offline. The shared orderbook expires entries on its own schedule but display logic should not trust the bare value.