@ostium/builder-sdk
v0.5.0
Published
Ostium builder SDK
Maintainers
Keywords
Readme
Ostium Builder SDK

TypeScript SDK for trading Stocks, Commodities, Forex, Indices, Crypto and ETF perps on Ostium.
npm install @ostium/builder-sdk viem
# or
bun add @ostium/builder-sdk viemGuides:
- BUILDER.md — Integrating Ostium into your app with builder fees
- TRADER.md — Building trading strategies programmatically on Ostium
Project Structure
src/
client.ts # OstiumClient — public API
config.ts # Named param interfaces for each mode
types.ts # OpenTradeParams, CloseTradeParams, etc.
errors.ts # OstiumError + OstiumErrorCode
encoder.ts # ABI encoding helpers (openTrade, closeTrade, ...)
cli.ts # Interactive CLI (bunx ostium)
signer/ # SignerStrategy — Self vs Delegated wrapping
submitter/ # SubmissionStrategy — EOA vs Pimlico UserOp
internal/
contracts.ts # On-chain addresses (mainnet + testnet)
decimal.ts # parseUsdc, parsePrice, parseLeverage
erc20.ts # Minimal ERC-20 ABI
contract.ts # Ostium Trading ABI
open-builder.ts # Trade struct construction
precision.ts # Precision constants
validation.ts # Input validation helpers
data/ # Read-only subgraph + price feed client
client.ts # OstiumSubgraphClient
types.ts # Pair, Position, Fill, OpenOrder, Order, …
queries.ts # GraphQL queries
internal/ # Formatters, calculations, pagination
dist/ # Compiled output (bun run build)Development
bun install # install dependencies
bun run build # compile to dist/
bun run typecheck # tsc --noEmitModes
The four client modes differ in who signs and how the transaction is submitted.
Signing
| Mode | Who owns USDC + positions | Who signs transactions |
|:--|:--|:--|
| Self | Your EOA | Your EOA |
| Delegated | Trader address (separate EOA) | Delegate EOA, wrapped in delegatedAction(traderAddress, data) |
Submission
| Mode | How the tx is sent | Gas |
|:--|:--|:--|
| Self (EOA) | Standard eth_sendRawTransaction | ETH required per tx |
| Gasless | Pimlico ERC-4337 UserOperation via Safe smart account | ETH-free after one-time setup |
The Four Combinations
| Constructor | Signer | Submitter | Notes |
|:--|:--|:--|:--|
| createSelfAndSelf | EOA | EOA | Simplest — one key, pays gas |
| createSelfAndGasless | EOA (owner) + Safe (delegate) | Pimlico UserOp | One-time setup, then free |
| createDelegatedAndSelf | Delegate EOA | Delegate EOA | Delegate holds ETH, trader holds USDC |
| createDelegatedAndGasless | Delegate EOA (owner) + Safe (delegate) | Pimlico UserOp | No ETH after trader calls setDelegate(safeAddress) |
import { OstiumClient, OrderType } from '@ostium/builder-sdk';
// Self + Self
const client = await OstiumClient.createSelfAndSelf({
traderPrivateKey: '0x...',
rpcUrl: 'https://arb-mainnet.g.alchemy.com/v2/...',
});
// Self + Gasless
const client = await OstiumClient.createSelfAndGasless({
traderPrivateKey: '0x...',
pimlicoUrl: 'https://builder.ostium.io/v1/pimlico/sponsor?chainId=42161',
});
// Delegated + Self
const client = await OstiumClient.createDelegatedAndSelf({
delegatePrivateKey: '0xDelegateKey',
traderAddress: '0xTraderAddress',
rpcUrl: 'https://arb-mainnet.g.alchemy.com/v2/...',
});
// Delegated + Gasless
const client = await OstiumClient.createDelegatedAndGasless({
delegatePrivateKey: '0xDelegateKey',
traderAddress: '0xTraderAddress',
pimlicoUrl: 'https://builder.ostium.io/v1/pimlico/sponsor?chainId=42161',
});Common Options
All OstiumClient.create* factories and OstiumClient.createReadOnly() accept shared read/stream options:
const client = await OstiumClient.createSelfAndSelf({
traderPrivateKey: '0x...',
rpcUrl: 'https://arb-mainnet.g.alchemy.com/v2/...',
alchemyApiKey: 'alchemy-key', // streamAccountUpdates()
subgraphUrl: 'https://...', // optional subgraph override
builderApiUrl: 'https://builder.ostium.io',
testnet: false,
});alchemyApiKey is only required when starting streamAccountUpdates(). You can
also pass it per stream call to override the constructor value.
Build Unsigned Transactions
Every write method now has a get*Tx() counterpart that returns transaction
data without signing or submitting it.
const client = await OstiumClient.createSelfAndSelf({
traderAddress: '0xTraderAddress',
});
const tx = client.getOpenTradeTx({
pairId: 0,
buy: true,
price: '65000',
collateral: '100',
leverage: '5',
type: OrderType.Market,
});
if (tx.kind === 'eoa') {
await window.ethereum.request({
method: 'eth_sendTransaction',
params: [{
from: tx.from,
to: tx.to,
data: tx.data,
value: `0x${tx.value.toString(16)}`,
}],
});
}Gasless build-only clients return a Safe-style request instead:
const gaslessClient = await OstiumClient.createSelfAndGasless({
traderAddress: '0xTraderAddress',
safeAddress: '0xSafeAddress',
});
const safeTx = gaslessClient.getOpenTradeTx({ ... });
// safeTx.kind === 'safe'
// safeTx.safeAddress identifies the Safe that should execute safeTx.calls[0]The existing write methods (openTrade, closeTrade, approveUsdc, etc.)
still work the same way, but they now submit the transaction built by the
corresponding get*Tx() method.
Read-only (no private key)
Use createReadOnly to access market data without a signer. All read methods are available; write methods throw INVALID_CONFIG.
const reader = await OstiumClient.createReadOnly();
const { pairs } = await reader.getPairs();
const positions = await reader.getOpenPositions({ user: '0xTrader...' });
const balances = await reader.getBalances('0xTrader...');Stream Position Updates
If you already have an OpenPositionsResponse, you can stream live price-driven
updates for those positions without re-fetching the full position set.
const positions = await client.getOpenPositions();
const stream = client.streamPositionUpdates(positions);
stream.onUpdate(next => {
console.log(next.marginSummary.totalRawPnlUsd);
console.log(next.pairPositions[0]?.position.unrealizedPnl);
});
stream.onError(err => console.error(err));
stream.close();The SDK subscribes once per unique pairId in the response, then updates the
full OpenPositionsResponse as prices change.
Common Options
All constructors (including createReadOnly) accept these optional overrides:
{
testnet?: boolean; // Arbitrum Sepolia when true (default false)
subgraphUrl?: string; // override the default subgraph endpoint
builderApiUrl?: string; // override the builder API base URL (prices, OHLC, WebSocket)
}Example:
const client = await OstiumClient.createSelfAndSelf({
traderPrivateKey: '0x...',
rpcUrl: '...',
subgraphUrl: 'https://your-subgraph.example.com/graphql',
builderApiUrl: 'https://your-builder-api.example.com',
});
const reader = await OstiumClient.createReadOnly({
subgraphUrl: 'https://your-subgraph.example.com/graphql',
builderApiUrl: 'https://your-builder-api.example.com',
});Self + Gasless: One-Time Setup
A Safe smart account is derived deterministically from your private key. You need to register it as your on-chain delegate once:
const client = await OstiumClient.createSelfAndGasless({
traderPrivateKey: '0x...',
pimlicoUrl: 'https://builder.ostium.io/v1/pimlico/sponsor?chainId=42161',
});
console.log('EOA (trader):', client.getTraderAddress());
console.log('Safe (delegate):', client.getSmartAccountAddress());
await client.approveUsdc('max'); // EOA → TradingStorage approval (gas, once)
await client.setupGaslessDelegation(); // EOA → setDelegate(Safe) (gas, once)
// All trades from here are gasless UserOps
await client.openTrade({ ... });Delegated Modes: Setup Checklist
The trader account must perform two one-time operations before the delegate can trade:
// 1. Trader approves USDC
const traderClient = await OstiumClient.createSelfAndSelf({
traderPrivateKey: '0xTraderKey',
rpcUrl: '...',
});
await traderClient.approveUsdc('max');
// 2a. Delegated + Self: trader registers the delegate EOA
await traderClient.setDelegate('0xDelegateEOA');
// 2b. Delegated + Gasless: trader registers the delegate's Safe
const delegateClient = await OstiumClient.createDelegatedAndGasless({
delegatePrivateKey: '0xDelegateKey',
traderAddress: '0xTraderAddress',
pimlicoUrl: '...',
});
await traderClient.setDelegate(delegateClient.getSmartAccountAddress()!);CLI — Environment Variable Configuration
The ostium CLI can load all credentials from a .env file (via dotenv). Create a .env file in your working directory with the variables relevant to your mode:
# ── Universal ─────────────────────────────────────────────────────────────────
# Self / main EOA private key (required for Self modes; also used as the main key in Test + Delegated)
PRIVATE_KEY=0x...
# Arbitrum RPC URL — required for Self+Self and Delegated+Self; optional for gasless modes
ARB_RPC_URL=https://arb-mainnet.g.alchemy.com/v2/YOUR_KEY
# ── Delegated modes ───────────────────────────────────────────────────────────
# Delegate EOA private key (falls back to PRIVATE_KEY if not set)
DELEGATE_PRIVATE_KEY=0x...
# Trader address the delegate acts on behalf of (required for Trade + Delegated)
TRADER_ADDRESS=0x...
# ── Gasless modes ─────────────────────────────────────────────────────────────
# Pimlico sponsor URL (required for Self+Gasless and Delegated+Gasless)
PIMLICO_URL=https://builder.ostium.io/v1/pimlico/sponsor?chainId=42161When the CLI detects a .env file it skips the interactive key/URL prompts and loads all values automatically. Fields not present in the env file are prompted interactively as normal.
Networks
| Network | Chain ID | testnet |
|:--|:-:|:-:|
| Arbitrum One | 42161 | false (default) |
| Arbitrum Sepolia | 421614 | true |
const client = await OstiumClient.createSelfAndSelf({
traderPrivateKey: '0x...',
rpcUrl: 'https://sepolia-rollup.arbitrum.io/rpc',
testnet: true,
});API Reference
Enums
import { OrderType, CancelOrderType } from '@ostium/builder-sdk';
OrderType.Market // 'market'
OrderType.Limit // 'limit'
OrderType.Stop // 'stop'
CancelOrderType.Limit // 'limit'
CancelOrderType.PendingOpen // 'pendingOpen'
CancelOrderType.PendingClose // 'pendingClose'Trading Methods
All return Promise<SubmissionResult> as soon as the transaction is submitted (UserOp or EOA). The hash is available immediately; the SDK does not wait for receipt or parse oracle events.
interface SubmissionResult {
txHash: `0x${string}`;
smartAccountAddress?: `0x${string}`; // gasless modes only
}Use getOrders({ initiatedTxHashes: [result.txHash] }) to poll by submission hash, or orderIds / recent orders for the trader.
Submissions that target the Trading contract also report their transaction hash to the builder API for SDK usage attribution. This is a fire-and-forget background request — it is never awaited and never affects trading calls.
openTrade(params)
const result = await client.openTrade({
pairId: 0, // trading pair (use getPairs() to list all)
buy: true, // true = long, false = short
price: '65000', // entry price
collateral: '100', // USD; fixed minimum is MIN_OPEN_SIZE_USD ($5)
leverage: '10', // multiplier; contract-side limits apply
type: OrderType.Market,
takeProfit?: '70000',
stopLoss?: '60000',
slippage?: 50, // bps, market orders only
isDayTrade?: false, // true when leverage > pair.overnightMaxLeverage
});
console.log(result.txHash);
// Override fee only (address from client config):
await client.openTrade({ /* ... */, builder: { feeBps: 10 } });
// Override address only (fee from client config):
await client.openTrade({ /* ... */, builder: { address: '0x...' } });Day trades are auto-closed before market close. Set
isDayTrade: truewhen the desired leverage exceedspair.overnightMaxLeverage(andovernightMaxLeverage > 0). UsegetPairs()to read both thresholds per pair.
closeTrade(params)
pairId and idx come from getOpenPositions():
const { pairPositions } = await client.getOpenPositions();
const { pairId, idx } = pairPositions[0].position;
await client.closeTrade({
pairId, // from Position.pairId
idx, // from Position.idx
price: '66000',
closePercent: 100, // 1–100, partial closes supported
slippage?: 50,
});cancelOrder(params)
TypeScript narrows the required fields by type:
// Cancel a pending limit order
const [order] = await client.getOpenOrders();
await client.cancelOrder({ type: CancelOrderType.Limit, pairId: order.pairId, idx: order.idx });
// Cancel a timed-out market open (orderId from chain / indexer)
await client.cancelOrder({ type: CancelOrderType.PendingOpen, orderId: 123 });
// Cancel a timed-out market close — retry: true re-submits the close
await client.cancelOrder({ type: CancelOrderType.PendingClose, orderId: 123, retry: true });modifyOrder(params)
pairId and idx come from getOpenPositions() (for TP/SL) or getOpenOrders() (for limit order price):
// Update TP on an open trade
await client.modifyOrder({ pairId, idx, takeProfit: '72000' });
// Update SL on an open trade
await client.modifyOrder({ pairId, idx, stopLoss: '61000' });
// Reprice a limit order (with optional new TP/SL)
await client.modifyOrder({ pairId, idx, price: '64000', takeProfit: '70000' });Note: you cannot update both takeProfit and stopLoss in a single call without also providing price. Send two separate calls instead.
updateCollateral(params)
pairId and idx come from getOpenPositions():
await client.updateCollateral({ pairId, idx, amount: '50' }); // add $50
await client.updateCollateral({ pairId, idx, amount: '-30' }); // remove $30USDC + Balance Helpers
const { current, required, sufficient } = await client.checkUsdcAllowance('100');
await client.approveUsdc('1000'); // approve $1000
await client.approveUsdc('max'); // approve MaxUint256
// All three values are decimal strings produced by formatUnits (e.g. "1234.56")
// Parse with Number() / parseFloat() only when you need numeric arithmetic.
const { usdc, eth, allowance } = await client.getBalances();
// In read-only mode, pass the address explicitly:
const { usdc, eth, allowance } = await client.getBalances('0xTrader...');Read Methods
All read methods are available on both OstiumClient and OstiumSubgraphClient. On OstiumClient the connected trader address is used by default wherever user is optional.
getPairs(params?)
All trading pairs with live prices, market-status flags, computed size limits, and rollover rates.
const { pairs } = await client.getPairs();
// Optional — filter to specific pairs:
const { pairs } = await client.getPairs({ pairIds: [0, 1, 4] });
// Pair fields:
// pairId, pairTo, pairFrom, category
// maxLeverage, overnightMaxLeverage
// openFee, closeFee — bps; openFee includes configured builder fee
// minSz, maxBSz, maxSSz, minNtl
// openInterest, buyOpenInterest, sellOpenInterest, maxOpenInterest
// rolloverRate: { long, short } — 8hr % by side
// rolloverFeePerBlock
// midPx, askPx, bidPx
// isMarketOpen, isDayTradingClosed, secondsToToggleIsDayTradingClosed
// schedule — market hours (see below)Pair symbol names from the subgraph are normalized automatically (e.g. CL → WTI, FTSE → UK100, SPX → US500).
schedule describes the pair's market hours as reported by the live price feed
(omitted when the feed is unavailable):
// 24/7 markets (crypto):
// schedule → { id: 1, alwaysOpen: true }
// Scheduled markets:
// schedule → {
// id: 3,
// timezone: 'America/New_York',
// openingHours: ['Mo-Th 00:00-16:59,18:00-24:00', 'Fr 00:00-16:59', 'Su 18:00-24:00'],
// }getAllPrices()
Live mid/bid/ask prices for every pair, keyed by pairId.
const { prices } = await client.getAllPrices();
// prices['0'] → { mid: '65000', bid: '64990', ask: '65010' }getOpenPositions(params?)
Open positions, margin summary, and per-position live PnL. Block number is fetched automatically on OstiumClient.
const { pairPositions, marginSummary, time } =
await client.getOpenPositions();
// Each position:
const { pairId, pid, idx, side, szi, entryPx, leverage, ntl, trader,
unrealizedPnl, returnOnEquity, liquidationPx,
collateralUsed, cumRollover, tpPx, slPx,
openTimestamp, isDayTrade } = pairPositions[0].position;
// marginSummary: accountValue, totalCollateralUsed, totalNtlPos, totalRawPnlUsd
// marginSummary.totalWithdrawable: max collateral removable across all positions
// time: server time (Unix ms)getFills(params?)
Executed fills, newest first.
const fills = await client.getFills();
// Filter options:
const fills = await client.getFills({ pairId: 0, limit: 50 });
// All traders (no address filter):
const fills = await client.getFills({ user: 'ALL' });getFillsByTime(params)
Same as getFills but with a time range filter (Unix milliseconds).
const fills = await client.getFillsByTime({
startTime: Date.now() - 7 * 86_400_000, // last 7 days
endTime: Date.now(), // optional, defaults to now
pairId: 0, // optional pair filter
});getOpenOrders(params?)
Active limit orders for a trader.
const orders = await client.getOpenOrders();
// Each order: pairId, pairTo, pairFrom, idx, side, limitPx, szi, orderType, tpPx?, slPx?, timestampgetOrders(params?)
Orders at any status — pending, executed, or cancelled. For fast user-facing confirmations after openTrade / closeTrade, prefer streamAccountUpdates(); use getOrders() as a lower-level lookup or fallback when you need specific order ids or submission transaction hashes.
const result = await client.openTrade({ /* … */ });
// Poll by the submission tx (matches subgraph `initiatedTx`):
const [order] = await client.getOrders({ initiatedTxHashes: [result.txHash] });
console.log(order.isPending, order.isCancelled);
// Or by on-chain order id:
const [byId] = await client.getOrders({ orderIds: [123] });
// Recent orders for the connected trader (no filter args):
const orders = await client.getOrders();
// Global orders and additional filters:
const globalOrders = await client.getOrders({ user: 'ALL' });
const builderOrders = await client.getOrders({ builder: '0xBuilder...' });
const pendingBtcOrders = await client.getOrders({
isPending: true,
pairIds: ['0'],
});
const cancelledBuilderOrders = await client.getOrders({
builder: '0xBuilder...',
isCanceled: true,
});
const recentBuilderOrders = await client.getOrders({
builder: '0xBuilder...',
start: Math.floor(Date.now() / 1000) - 7 * 86_400,
end: Math.floor(Date.now() / 1000),
});Each Fill includes builder, ntl (USD notional), trader, and timestamp (execution time, Unix seconds UTC — subgraph executedAt); each Order extends Fill with initiatedTx, initiatedTime, isPending, isCancelled, and optional cancelReason. time is retained as an alias for timestamp.
start / end are inclusive bounds on executedAt (matching getFillsByTime). Only executed orders have an execution time, so combining them with isPending: true matches nothing.
streamAccountUpdates(params?)
Low-latency account confirmation stream. It emits the full processed account snapshot keyed by normalized trader address whenever pending orders, active limit/stop orders, or open positions change:
const stream = client.streamAccountUpdates();
stream.onUpdate(snapshot => {
const account = snapshot[client.getTraderAddress().toLowerCase()];
console.log(account.positions); // open PairPosition[]
console.log(account.orders); // pending Order[]
console.log(account.limits); // active OpenOrder[]
});
stream.onError(console.error);
// Clean up:
stream.close();For frontend-style latency, insert a local optimistic market open before or while
submitting, then attach the on-chain orderId as soon as your receipt watcher
sees the oracle request log:
import { extractOrderIdFromReceipt } from '@ostium/builder-sdk';
const localId = stream.addOptimisticOpen(openParams);
const result = await client.openTrade(openParams);
// Wherever your app waits for the EOA receipt or UserOp receipt:
const orderId = extractOrderIdFromReceipt(receipt);
if (orderId) {
stream.attachOrderId(localId, orderId, { initiatedTx: result.txHash });
}Optimistic stream positions include position.confirmationStatus
("optimistic", "initiated", or "executed") plus orderId once known.
Indexed positions returned by normal read methods are unchanged.
The stream polls the account subgraph snapshot every 3 s by default (one
batched query regardless of how many traders are subscribed — override with
pollIntervalMs), opens a live price subscription immediately, and uses
Alchemy contract logs to show market opens and full closes before the subgraph
indexes them, so confirmations stay sub-second at any poll interval. Open trades are
repriced on live ticks so PnL stays fresh between subgraph polls. Configure
alchemyApiKey on the client or override it per stream:
const stream = client.streamAccountUpdates({
user: ['0xTraderA...', '0xTraderB...'],
alchemyApiKey: 'override-key',
pollIntervalMs: 1000,
});Read-only clients and standalone OstiumSubgraphClient calls require an
explicit user address array:
const reader = await OstiumClient.createReadOnly({ alchemyApiKey: 'alchemy-key' });
const stream = reader.streamAccountUpdates({ user: ['0xTrader...'] });getBuilderOrders(builder, params?)
Fetch the full order history for trades routed through a builder. The subgraph only stores builder on open orders; this method supplements those with sibling close/TP/SL orders on the same positions (pid).
const orders = await client.getBuilderOrders('0xBuilder...');
// Inherits getOrders filters (user, isPending, isCanceled, pairIds, start, end, limit)
const pending = await client.getBuilderOrders('0xBuilder...', {
isPending: true,
pairIds: ['0'],
limit: 50, // caps phase-1 builder-tagged orders; siblings are appended without cap
});Sibling orders from phase 2 have builder stamped to the requested address. Use getOrders({ builder }) when you only need orders that have builder set on the subgraph (typically opens).
getSimSlippage(params)
Simulate price impact for a list of pairs and notionals.
const result = await client.getSimSlippage({
pairIds: [0, 1],
ntls: ['1000', '10000', '100000'],
});
// result['0'].long → [{ ntl, slippage }, ...]
// result['0'].short → [{ ntl, slippage }, ...]getSimOrderbook(params)
Synthetic bid/ask orderbook for a pair, following Hyperliquid L2Book format. Levels are log-spaced from the minimum order size to the remaining OI capacity on each side.
const book = await client.getSimOrderbook({ pairId: 0, levels: 20 });
// book.levels[0] → bids (short entries), best bid first
// book.levels[1] → asks (long entries), best ask first
// Each level: { px: string, sz: string, n: 1 }getCandles(params)
OHLC candles from the builder API. from/to are Unix milliseconds. pairId is the same value returned by getPairs(). Pass sets to fetch multiple candle pages — only needed if a single response doesn't cover the requested range; the API currently returns the full allowed range in one page, so sets > 1 is rarely required.
const candles = await client.getCandles({
pairId: 0,
from: Date.now() - 30 * 86_400_000, // last 30 days
to: Date.now(), // optional, defaults to now
resolution: '1D', // '1' | '5' | '15' | '60' | '240' | '1D'
sets: 3, // optional, defaults to 1 page
});
// Each candle: { pairFrom, pairTo, time, open, high, low, close }
// pairFrom / pairTo use normalized display names (e.g. "WTI" not "CL")streamPrices(pairIds?)
WebSocket connection to the live price feed. Fires a snapshot on connect, then a tick on every update. Accepts the same pairId values as everywhere else in the SDK — no pair name strings needed.
const stream = client.streamPrices([0, 1]); // BTC and ETH by pairId
stream.onOpen(() => console.log('connected'));
stream.onSnapshot(ticks => console.log('initial snapshot:', ticks.length));
stream.onTick(tick => console.log(tick.pair, tick.mid));
// tick.pair / tick.from / tick.to use normalized display names
// ticks also carry isMarketOpen / isDayTradingClosed / secondsToToggleIsDayTradingClosed,
// plus the pair's market-hours `schedule` when reported by the feed
// Dynamically add / remove by pairId:
stream.subscribe([2]);
stream.unsubscribe([0]);
// Clean up:
stream.close();Requires Node.js 18+, Bun, or a browser environment.
Getters
client.getTraderAddress(); // on-chain trader address (throws in read-only mode)
client.getSmartAccountAddress(); // Safe address — gasless modes only, else undefined
client.isReadOnly(); // true when created via createReadOnly()Error Codes
import { OstiumError, OstiumErrorCode } from '@ostium/builder-sdk';| Code | When thrown |
|:--|:--|
| INVALID_CONFIG | Missing or malformed config (privateKey format, address format, fee range) |
| VALIDATION_FAILED | Invalid trade params (TP/SL direction, leverage range, close percent) |
| ALLOWANCE_INSUFFICIENT | USDC allowance too low for openTrade / updateCollateral |
| DELEGATION_FAILED | approveUsdc() called in delegated mode |
| CONTRACT_ERROR | Contract revert (e.g. WrongLeverage, NoDelegate) |
| NETWORK_ERROR | RPC / Pimlico connectivity issues or rate limits |
| SUBMISSION_FAILED | Transaction rejected for other reasons |
Decimal Utilities
import { parseUsdc, parsePrice, parseLeverage, MIN_OPEN_SIZE_USD, MAX_COLLATERAL_USD } from '@ostium/builder-sdk';
parsePrice('65000.50'); // 65000500000000000000000n (18 decimals)
parseUsdc('100.50'); // 100500000n (6 decimals)
MIN_OPEN_SIZE_USD; // 5
MAX_COLLATERAL_USD; // 2_000_000Standalone Subgraph Client
For read-only market data without a trading key, use OstiumSubgraphClient directly:
import { OstiumSubgraphClient } from '@ostium/builder-sdk';
const subgraph = await OstiumSubgraphClient.create({ testnet: false });
const { pairs } = await subgraph.getPairs();
const { prices } = await subgraph.getAllPrices();
const positions = await subgraph.getOpenPositions({ user: '0xTrader...' });
const fills = await subgraph.getFills({ user: '0xTrader...', limit: 50 });OstiumSubgraphClient.getOpenPositions accepts an optional blockNumber (required for accurate PnL). On OstiumClient this is fetched automatically.
