· BuiltOnBulk · Developers · 4 min read
BULK Exchange SDK Guide: Connect, Quote, Trade, and Stream Fills
The BULK Exchange client SDK is open source and lets you connect directly to the L0 execution layer. This guide covers installation, authentication, placing and cancelling orders, subscribing to the orderbook, and streaming your own fills.
The BULK Exchange client SDK is open source and connects directly to the L0 execution layer. It exposes the same primitives the official UI uses: order placement, cancellation, orderbook subscriptions, fill streams, and account state queries. There is no separate REST gateway sitting in front of validators.
This guide walks through the first hour with the SDK: install, connect, sign, place an order, subscribe to fills.
What “Direct to L0” Means
Most exchange SDKs talk to a REST endpoint that the exchange operates. The exchange’s backend then talks to the matching engine. You are trusting the gateway operator and accepting their latency.
BULK is different. The SDK opens a Noise Protocol session directly to validators. Your orders are signed client-side and submitted to the consensus layer that produces the fill. There is no intermediary.
Concretely:
- Lower latency: one network hop instead of two
- Censorship resistance: no single gateway can drop your orders
- Identical to the UI: the official UI uses this same SDK
Installation
npm install @bulk-exchange/client
# or
pnpm add @bulk-exchange/clientRust and Python clients are in the BULK GitHub org.
Authentication
The SDK signs every request with your Solana keypair. You never share a private key with the exchange.
import { BulkClient } from '@bulk-exchange/client';
import { Keypair } from '@solana/web3.js';
const keypair = Keypair.fromSecretKey(/* your secret */);
const client = new BulkClient({
cluster: 'mainnet', // or 'testnet'
signer: keypair,
});
await client.connect();For browser apps, swap Keypair for a wallet adapter signer (Phantom, Backpack, Solflare).
Place a Limit Order
const order = await client.placeOrder({
market: 'SOL-PERP',
side: 'buy',
type: 'limit',
price: 142.50,
size: 10,
postOnly: true,
reduceOnly: false,
timeInForce: 'GTC',
});
console.log('Order ID:', order.id);
console.log('Submitted at slot:', order.slot);The promise resolves when the order is accepted into the orderbook (typically 5–20ms). If the order is rejected (e.g., would self-trade with postOnly), the promise rejects with a typed error.
Cancel Orders
Cancels are always processed before new orders in the same slot. This is part of BULK’s fair ordering guarantee — you can’t be liquidated by a competing taker before your cancel goes through.
// Cancel by ID
await client.cancelOrder(order.id);
// Cancel all on a market
await client.cancelAll({ market: 'SOL-PERP' });
// Cancel all across markets (account-level)
await client.cancelAll();Subscribe to the Orderbook
const sub = client.orderbook('SOL-PERP').subscribe((book) => {
console.log('Best bid:', book.bids[0]);
console.log('Best ask:', book.asks[0]);
});
// Later
sub.unsubscribe();Updates are pushed as deltas, not full snapshots, and reconciled via minisketch (the same protocol BULK uses internally between validators). Bandwidth stays flat regardless of update frequency.
Stream Your Own Fills
const fills = client.fills().subscribe((fill) => {
console.log(`Filled ${fill.size} @ ${fill.price} on ${fill.market}`);
console.log('Fee:', fill.fee);
console.log('Slot:', fill.slot);
});Every fill is signed by the validator set and includes a state hash, so you can verify the fill happened in the canonical chain state without trusting the SDK or the validator that sent it to you.
Account State
const account = await client.account();
console.log('Equity:', account.equity);
console.log('Margin used:', account.marginUsed);
console.log('Effective leverage:', account.effectiveLeverage);
console.log('Positions:', account.positions);
console.log('Open orders:', account.openOrders);Margin numbers come from BULK’s portfolio margin engine — they reflect correlation-adjusted effective notional, not naive per-position margin.
Follower Mode (HFT Builders)
For market makers and high-frequency strategies, the SDK supports follower mode: you subscribe to a chosen validator’s pre-consensus stream and react to fills before the consensus round completes.
const follower = client.follower({
validator: 'validator-pubkey',
market: 'SOL-PERP',
});
follower.on('pre-fill', (fill) => {
// React 2-5ms earlier than consensus-confirmed fills
});This is the same primitive BULK’s alpha market makers use. It is opt-in and validator-specific — you trust the follower validator for low-latency signal, but consensus is still authoritative.
Rate Limits and Quality Score
There are no naive per-second rate limits. Instead, BULK uses a quality score that tracks:
- Cancel-to-fill ratio
- Toxic flow indicators
- Spam patterns
Quality score is per-account and affects fee tier, market maker eligibility, and validator inclusion priority. Build with reasonable cancel/order discipline and you will not hit it.
Error Handling
The SDK throws typed errors:
InsufficientMargin— order would exceed your marginSelfTrade— would cross your own resting order withpostOnlyMarketHalted— market is in safety-halt stateConsensusReject— validators rejected the order (signature or replay)RateLimited— quality score exceeded
Always wrap order placement in try/catch and check the error type.
Where to Go Next
- Examples repo: github.com/bulk-exchange/sdk-examples — full sample bots (market maker, basis trade, funding farmer)
- API reference: docs.bulk.trade
- Discord developer channel: for direct help from the core team
Related Reading
Ready to start?
Farm the BULK airdrop on testnet — free, no capital required. Mainnet launching soon.
Browse all topics
Every cluster on BulkTrade Guide. Jump to the hub for a deeper read.