SDK reference
@quotient/sdk is a viem-based TypeScript package. It ships typed ABIs generated from the Foundry
artifacts, the chain definitions, the quote codec, a maker client, swap helpers and subscriptions.
pnpm add @quotient/sdk viem
Chains
import { elysiumTestnet, elysiumMainnet, CANONICAL_TOKENS, NATIVE } from '@quotient/sdk';
Do not use viem's built-in elysiumTestnet from viem/chains: that is an unrelated network (chain ID 1338).
Quote codec
| Function | Purpose |
|---|---|
encodeQuote(fields) / decodeQuote(slot) | pack and unpack the 32-byte slot |
quoteFromHuman({ price, decimals0, decimals1, bidBps, askBps, maxFill, … }) | build fields from human units |
stampQuote(slot, l2Block) | the slot as stored on-chain after inclusion |
encodeBatch(entries) / decodeBatch(packed) | the updateQuotes payload |
midFromHuman(price, d0, d1) / midToHuman(mid, d0, d1) | price conversions |
Swap math
fillExactIn, fillExactOut and bestFill reproduce the hook's pricing in TypeScript, bit for bit.
Use them to simulate before you send, or to price from indexed state without an RPC call.
const r = fillExactIn(makerState, true, 10n ** 18n, 1, currentBlock);
if (r.ok) console.log(r.amountOut, r.price, r.spread);
Maker client
import { makerClient } from '@quotient/sdk';
const m = makerClient({ wallet, publicClient, chainId: 99801 });
await m.registerMaker();
await m.setOperator(operator, true);
await m.deposit(poolId, currency, amount);
await m.withdraw(poolId, currency, amount, to);
await m.setPaused(true);
// operator side: build and send one batched update, nonce tracked locally
const q = m.quoteBatcher();
q.set(0, slotForPool0);
q.set(1, slotForPool1);
await q.flush({ priorityFeeWei: 1n });
Swaps
import { swapClient, encodeUniversalSwap } from '@quotient/sdk';
const s = swapClient({ wallet, publicClient, chainId: 99801 });
const quote = await s.quoteExactIn(poolKey, true, amountIn);
await s.swapExactIn(poolKey, true, amountIn, (quote.amountOut * 995n) / 1000n);
Subscriptions
Elysium has no public mempool, so the SDK watches blocks and logs:
import { watchHeads, watchVenueLogs } from '@quotient/sdk';
const stop = watchHeads(wsClient, (block) => { /* every ~100–150 ms */ });
const stopLogs = watchVenueLogs(publicClient, { hook, fromBlock }, (log) => { /* Filled, QuoteUpdated, … */ });
watchVenueLogs paginates backfills in windows that respect the provider's block-range limit (a
10,000-block eth_getLogs window is only ~17–40 minutes of Elysium time).
Data source
import { createDataSource } from '@quotient/sdk';
const ds = createDataSource({ indexerUrl });
const pools = await ds.pools();
createDataSource is the one place the SDK decides between live data and the synthetic venue used
before contracts exist; callers never branch on it.