@sugar-rush/sdk
v0.1.0
Published
TypeScript SDK for the Sugar Rush exchange: streaming market data and order submission over one WebSocket, with a CCXT-familiar surface.
Readme
@sugar-rush/sdk
TypeScript client for the Sugar Rush exchange.
One WebSocket carries everything: streaming market data and your account
(materialized locally so you can read current state synchronously) and order
submission. The primary surface is events + state + a batch subscribe; a
CCXT-familiar watch* layer sits on top so existing bots port with little
change.
npm i @sugar-rush/sdkQuickstart
import { createClient, loadIdentity } from "@sugar-rush/sdk";
const client = createClient({
wsUrl: "wss://api.sugar.rush.preview.sundae.fi/ws",
identity: await loadIdentity("bot.skey"), // omit for public/read-only
});
await client.connect();
// Subscribe to many streams in one call.
await client.subscribe({
orderbook: ["DARK-VAN", "UBE-VAN"],
orders: true,
balance: true,
});
// React with typed events…
client.on("orderbook", ({ symbol, book }) => {
console.log(symbol, "best bid", book.bids[0]?.price, "best ask", book.asks[0]?.price);
});
client.on("order", (order) => console.log("order update", order.id, order.status.tag));
// …or read the current materialized state synchronously, any time.
const book = client.orderBook("DARK-VAN");
const van = client.balanceOf("VAN");
const mine = client.openOrders("DARK-VAN");
// Place an order (built → signed → encrypted → submitted over the socket).
await client.createOrder({
symbol: "DARK-VAN",
side: "buy",
type: "limit",
price: "2.15", // human decimal (quote per base)
size: "1000", // 1000 whole DARK
timeInForce: "GTC",
});
await client.cancelOrder(orderId);
await client.cancelAllOrders();Human units vs. exact wire values
price and size accept human decimals ("2.15", "1000") by default; the
SDK converts them with BigInt math (no floating-point loss) using each asset's
decimals. When you want to hand the wire an exact value, wrap it with exact(...):
import { exact } from "@sugar-rush/sdk";
await client.createOrder({
symbol: "DARK-VAN", side: "buy", type: "limit",
price: exact("2.150000"), // verbatim wire price
size: exact(1_000_000_000n), // 1e9 raw base units, verbatim
});Deposits & withdrawals
// Watch deposit status live (pending → absorbed → rejected)
client.on("deposit", (d) => console.log(d.requestId, d.status));
const mine = client.deposits();
// Withdraw back to L1 (settles to your account's registered destination)
await client.withdraw({ asset: "VAN", amount: "500" }); // human, or exact(...)Depositing is an L1 Cardano transaction (provider-injected). It can also establish a session delegate in the same flow — the account key signs once, then a browser session key trades with no further prompts:
await client.deposit({
amountAda: 10,
blockfrostProjectId: "preview…", // Node: signs with your identity key
// wallet: <blaze CIP-30 wallet>, // browser: the user's wallet is the depositor
delegateTo: sessionKeyHash, // pre-signed now, auto-submitted once the deposit absorbs
});Candles & ticker
await client.subscribe({
candles: [{ symbol: "DARK-VAN", interval: "1m" }],
ticker: ["DARK-VAN"],
});
client.on("candle", ({ symbol, interval, candle }) => {
console.log(symbol, interval, "close", priceToNumber(candle.close));
});
client.on("ticker", ({ symbol, ticker }) => {
console.log(symbol, "last", ticker.lastPrice, "bid/ask", ticker.bestBid, ticker.bestAsk);
});
const series = client.candles("DARK-VAN", "1m"); // materialized, ascending
const t = client.ticker("DARK-VAN");Candle intervals are 1s / 1m / 1h / 1d. Price fields on candles and
tickers are u128 fixed-point — render them with the exported priceToNumber.
CCXT-familiar layer
while (running) {
const book = await client.watchOrderBook("DARK-VAN"); // resolves on the next update
render(book);
}watchOrders(symbol?), watchBalance(), watchTicker(symbol), and
watchOHLCV(symbol, interval) work the same way — thin wrappers over the same
subscription + materialized state.
Time in force
GTC (default) rests until cancelled · IOC fills what crosses now and cancels
the rest · FOK fills fully or rejects · PostOnly rejects if it would cross.
type: "market" orders are always IOC and sweep the top of book within
marketSlippage (default 5%).
Under the hood
Writes go over the streaming API's submit op, which forwards the same
COSE-signed, encrypted payload that POST /head/requests accepts — the exchange
never sees your order before it matches. The low-level pieces (ViewsClient,
buildEncryptedTransactionPayload, createRequestEncryptor, wire types) are
exported too, if you need them.
