Package Exports
- @barista-dex/sdk
- @barista-dex/sdk/dist/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@barista-dex/sdk) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
@barista-dex/sdk
TypeScript SDK for interacting with Barista DEX on Solana, a fork of Percolator DEX by Toly
For CLI Users:
- Traders: Use
@barista-dex/clifor trading operations- DLPs: Use
@barista-dex/cli-dlp(coming soon) for slab management and portfolio operationsThis SDK is for programmatic integration - building custom applications, bots, and integrations.
Installation
npm install @barista-dex/sdk @solana/web3.js bn.jsQuick Start
import { Connection, PublicKey, Keypair, Transaction } from '@solana/web3.js';
import { RouterClient } from '@barista-dex/sdk';
import BN from 'bn.js';
// Setup connection and wallet
const connection = new Connection('https://api.devnet.solana.com');
const wallet = Keypair.fromSecretKey(/* your secret key */);
// Initialize Router client
const routerProgramId = new PublicKey('Your_Router_Program_ID');
const router = new RouterClient(connection, routerProgramId, wallet);
// Create and initialize portfolio
const initIx = router.buildInitializePortfolioInstruction(wallet.publicKey);
const tx = new Transaction().add(initIx);
const signature = await connection.sendTransaction(tx, [wallet]);
await connection.confirmTransaction(signature);
console.log('Portfolio initialized!');Core Concepts
Router Program
The Router is the global coordinator that handles:
- Collateral Management: SOL deposits/withdrawals (v0.5)
- Portfolio Margin: Cross-margin accounts with health-based risk management
- PnL Settlement: Real SOL transfers between portfolios (v0.5 counterparty model)
- Cross-Slab Routing: Single-slab execution (v0.5), multi-slab in v1+
- Liquidations: Automated liquidation of undercollateralized positions
Slab Program
Slabs are LP-run perpetual markets that:
- Run independent order books (v0.5: atomic fills only, v1: resting orders)
- Settle against mark price oracles
- Charge taker fees to traders
- Allow LPs to earn spread and fees
v0.5 PnL Settlement Model
Important: v0.5 implements DLP counterparty settlement:
- Each slab has an LP/DLP owner who provides liquidity
- DLP creates a Portfolio account and deposits SOL capital
- Trades settle with real SOL transfers between User Portfolio ↔ DLP Portfolio
- Zero-sum: User profit = DLP loss (and vice versa)
- Single-slab: Only 1 slab per trade (cross-slab routing disabled in v0.5)
- v1 migration: Same Portfolio account will track LP inventory PnL (order book model)
Complete Usage Guide
1. Setup and Configuration
Network Configuration
import { Connection, Keypair } from '@solana/web3.js';
import { RouterClient, SlabClient } from '@barista-dex/sdk';
// Devnet
const connection = new Connection('https://api.devnet.solana.com', 'confirmed');
// Localnet (for testing)
// const connection = new Connection('http://localhost:8899', 'confirmed');
// Mainnet-beta
// const connection = new Connection('https://api.mainnet-beta.solana.com', 'confirmed');
// Load wallet from file
import fs from 'fs';
const secretKey = Uint8Array.from(JSON.parse(fs.readFileSync('wallet.json', 'utf-8')));
const wallet = Keypair.fromSecretKey(secretKey);
// Initialize clients
const routerProgramId = new PublicKey('YourRouterProgramId');
const slabProgramId = new PublicKey('YourSlabProgramId');
const router = new RouterClient(connection, routerProgramId, wallet);
const slab = new SlabClient(connection, slabProgramId, wallet);Program Initialization (One-time)
import { Transaction, SystemProgram } from '@solana/web3.js';
// Initialize the Router program (creates Registry and Authority)
async function initializeRouter() {
const ix = router.buildInitializeInstruction(wallet.publicKey);
const tx = new Transaction().add(ix);
const signature = await connection.sendTransaction(tx, [wallet]);
await connection.confirmTransaction(signature);
console.log('Router initialized:', signature);
}2. Portfolio Management
Initialize User Portfolio
async function createPortfolio() {
const ix = router.buildInitializePortfolioInstruction(wallet.publicKey);
const tx = new Transaction().add(ix);
const signature = await connection.sendTransaction(tx, [wallet]);
await connection.confirmTransaction(signature);
console.log('Portfolio created:', signature);
}Deposit Collateral (SOL Only in v0)
import { LAMPORTS_PER_SOL } from '@solana/web3.js';
async function depositCollateral(solAmount: number) {
// Convert SOL to lamports
const amount = new BN(solAmount * LAMPORTS_PER_SOL);
// Automatically creates portfolio if it doesn't exist
const ensurePortfolioIxs = await router.ensurePortfolioInstructions(wallet.publicKey);
const depositIx = await router.buildDepositInstruction(amount, wallet.publicKey);
const tx = new Transaction()
.add(...ensurePortfolioIxs)
.add(depositIx);
const signature = await connection.sendTransaction(tx, [wallet]);
await connection.confirmTransaction(signature);
console.log(`Deposited ${solAmount} SOL:`, signature);
}
// Example: Deposit 10 SOL
await depositCollateral(10);Note: v0 supports SOL deposits only. USDC and other SPL tokens will be supported in v1+.
Withdraw Collateral (SOL Only in v0)
import { LAMPORTS_PER_SOL } from '@solana/web3.js';
async function withdrawCollateral(solAmount: number) {
// Convert SOL to lamports
const amount = new BN(solAmount * LAMPORTS_PER_SOL);
const withdrawIx = await router.buildWithdrawInstruction(amount, wallet.publicKey);
const tx = new Transaction().add(withdrawIx);
const signature = await connection.sendTransaction(tx, [wallet]);
await connection.confirmTransaction(signature);
console.log(`Withdrew ${solAmount} SOL:`, signature);
}
// Example: Withdraw 5 SOL
await withdrawCollateral(5);Check Portfolio Health
import { formatUsd, formatHealth } from '@barista-dex/sdk';
async function checkPortfolioHealth() {
const portfolio = await router.getPortfolio(wallet.publicKey);
if (!portfolio) {
console.log('Portfolio not found');
return;
}
console.log('Portfolio Status:');
console.log(' Collateral Value:', formatUsd(portfolio.collateralValue));
console.log(' Unrealized PnL: ', portfolio.unrealizedPnl.toString());
console.log(' Equity: ', formatUsd(portfolio.equity));
console.log(' Maint Margin: ', formatUsd(portfolio.maintMargin));
console.log(' Health Ratio: ', formatHealth(portfolio.health));
const healthNum = portfolio.health.toNumber() / 1e6;
if (healthNum < 100) {
console.log('⚠️ WARNING: Portfolio is undercollateralized!');
} else if (healthNum < 110) {
console.log('⚠️ CAUTION: Close to liquidation threshold');
} else {
console.log('✓ Portfolio is healthy');
}
return portfolio;
}3. Leverage Trading
Barista DEX supports 1-10x leverage on all trades. Leverage allows traders to control larger positions with less collateral.
Leverage Model
- 1 unit = 1 contract = 1 underlying asset (e.g., 1 SOL, 1 BTC)
- Quantity input = Margin to commit (not position size)
- Actual position = Margin × Leverage
- Price is irrelevant for margin - only quantity matters
Collateral Management
Position Open/Increase:
- Calculate margin:
margin = (quantity × 1e9) / leveragelamports - Transfer margin from user portfolio → DLP portfolio
- Store in PositionDetails:
margin_heldandleverage - Example: 5 contracts at 5x = 1 SOL transferred to DLP
Position Close/Reduce:
- Leverage parameter is IGNORED when closing positions
- You can close a 5x position using a 1x order (or any leverage)
- Matches real CEX behavior (Binance, dYdX, etc.)
- Return margin: DLP portfolio → user portfolio
- Full close: return all
margin_held - Partial close: return proportional margin
- Full close: return all
- Settle PnL separately:
- Profit: additional transfer DLP → User
- Loss: additional transfer User → DLP
- Example: Close 5 contracts (opened at 5x) with sell order at 1x
- Margin returned: 1 SOL (DLP → User)
- Plus/minus PnL based on price difference
Position Reversal (over-closing):
- If you sell MORE than your long position, it splits into close + reopen
- Example: Long 5 @ 5x, sell 10 @ 10x:
- Close long 5 (release 1 SOL margin)
- Open short 5 @ 10x (add 0.5 SOL margin)
- Net result: Short 5 contracts, IM = 0.5 SOL
Critical: Margin is ALWAYS returned when closing positions. You get your collateral back regardless of PnL.
Initial Margin Requirements
| Leverage | IMR | Example (5 contracts) |
|---|---|---|
| 1x (spot) | 100% | 5 SOL margin |
| 5x | 20% | 1 SOL margin |
| 10x | 10% | 0.5 SOL margin |
Formula: IMR = 100% / leverage
Example: Leveraged Trading
import { RouterClient } from '@barista-dex/sdk';
import BN from 'bn.js';
// Open 5x leveraged position
async function openLeveragedPosition() {
const slabMarket = new PublicKey('SlabAddress');
const oracle = new PublicKey('OracleAddress');
// Input: 1 unit margin
const quantity = new BN(1_000_000); // 1.0 units in 1e6 scale
const leverage = 5;
// Validate position before executing
const validation = await router.validateLeveragedPosition(
wallet.publicKey,
quantity,
new BN(200_000_000), // $200 price (for display only)
leverage
);
console.log('Margin committed:', validation.marginCommitted.toString()); // 1,000,000 units
console.log('Actual position:', validation.actualQuantity.toString()); // 5,000,000 units (5 contracts)
console.log('Position size:', validation.positionSize.toString()); // $1000 (5 × $200)
console.log('Available equity:', validation.availableEquity.toString()); // e.g., 50,000,000 units (50 SOL)
console.log('Valid:', validation.valid); // true if equity >= margin
if (!validation.valid) {
throw new Error('Insufficient collateral');
}
// Execute buy with leverage
const price = new BN(200_000_000); // $200 limit price
const { instruction, receiptSetup, receiptKeypair } = await router.buildBuyInstruction(
wallet.publicKey,
slabMarket,
validation.actualQuantity, // 5 contracts (leveraged)
price,
oracle,
0, // 0 = market order, 1 = limit order
leverage // 1-10x
);
// Send transaction
const tx = new Transaction()
.add(receiptSetup)
.add(instruction);
const signature = await connection.sendTransaction(tx, [wallet, receiptKeypair]);
await connection.confirmTransaction(signature);
console.log('Position opened!');
console.log('Signature:', signature);
// After execution:
// - 1 SOL transferred from user portfolio → DLP portfolio
// - User controls 5 SOL worth of position
// - IMR locked: ~0.2 SOL (20% of 1 SOL margin)
}Position Sizing Helpers
// Calculate actual quantity for leverage
const marginInput = new BN(1_000_000); // 1 unit
const leverage = 10;
const actualQty = router.calculateActualQuantity(
marginInput,
new BN(100_000_000), // price (not used in calculation)
leverage
);
console.log(actualQty.toString()); // 10,000,000 (10 contracts)
// Calculate position notional value
const marginCommitted = new BN(2_000_000); // 2 units
const positionSize = router.calculatePositionSize(marginCommitted, 5);
console.log(positionSize.toString()); // 10,000,000 (10 units at 5x)Risk Management
// Check portfolio health before trading
const portfolio = await router.getPortfolio(wallet.publicKey);
// Equity must exceed Initial Margin
const hasSufficientMargin = portfolio.equity >= portfolio.im;
// Avoid liquidation - maintain health above Maintenance Margin
const healthRatio = portfolio.equity / portfolio.mm;
console.log('Health ratio:', healthRatio); // Should be > 1.0
if (healthRatio < 1.2) {
console.warn('⚠️ Close to liquidation! Consider reducing leverage.');
}4. Trading
Smart Routing (Automatic Best Execution)
// Find best slab for trading an instrument
async function tradeWithSmartRouting(
instrumentId: PublicKey,
side: 'buy' | 'sell',
quantity: BN
) {
// Smart routing finds best price across all slabs
const bestSlab = await router.findBestSlabForTrade(
instrumentId,
side,
quantity,
slabProgramId
);
console.log(`Best ${side} price: ${bestSlab.price}`);
console.log(`On slab: ${bestSlab.slab.toBase58()}`);
console.log(`Available liquidity: ${bestSlab.availableQty}`);
// Execute on the best slab
const split: SlabSplit = {
slabMarket: bestSlab.slab,
isBuy: side === 'buy',
size: quantity,
price: bestSlab.price,
};
const ix = router.buildExecuteCrossSlabInstruction(
wallet.publicKey,
[split],
slabProgramId
);
const tx = new Transaction().add(ix);
const signature = await connection.sendTransaction(tx, [wallet]);
await connection.confirmTransaction(signature);
return signature;
}
// Example: Buy BTC-PERP with smart routing
const btcInstrument = new PublicKey('BTC...');
await tradeWithSmartRouting(btcInstrument, 'buy', new BN(1_000_000));Get Quote Data from Slabs
// Get detailed quotes from a slab (includes best bid/ask levels)
async function getSlabQuotes(slabMarket: PublicKey) {
const quotes = await router.getSlabQuotes(slabMarket);
console.log('Instrument:', quotes.instrument.toBase58());
console.log('Mark Price:', quotes.markPrice.toString());
console.log('\nBest Bids:');
quotes.cache.bestBids.forEach((level, i) => {
console.log(` ${i + 1}. Price: ${level.price}, Qty: ${level.availableQty}`);
});
console.log('\nBest Asks:');
quotes.cache.bestAsks.forEach((level, i) => {
console.log(` ${i + 1}. Price: ${level.price}, Qty: ${level.availableQty}`);
});
return quotes;
}Manual Cross-Slab Trade
import { SlabSplit } from '@barista-dex/sdk';
async function executeTrade(
side: 'buy' | 'sell',
totalSize: number,
slabMarkets: PublicKey[]
) {
// Split order across multiple slabs for best execution
const sizePerSlab = totalSize / slabMarkets.length;
const splits: SlabSplit[] = slabMarkets.map(market => ({
slabMarket: market,
isBuy: side === 'buy',
size: new BN(sizePerSlab * 1_000_000), // 6 decimals
price: new BN(50_000_000), // $50.00 limit price
}));
const ix = router.buildExecuteCrossSlabInstruction(
wallet.publicKey,
splits,
slabProgramId
);
const tx = new Transaction().add(ix);
const signature = await connection.sendTransaction(tx, [wallet]);
await connection.confirmTransaction(signature);
console.log(`Executed ${side} of ${totalSize}:`, signature);
}
// Example: Buy 10 ETH-PERP across 2 slabs
const slabs = [
new PublicKey('Slab1Address'),
new PublicKey('Slab2Address'),
];
await executeTrade('buy', 10, slabs);Advanced Trading with Price Optimization
async function smartTrade(
side: 'buy' | 'sell',
targetSize: number,
maxSlippage: number = 0.01 // 1%
) {
// Get best prices across all slabs
const slabMarkets = await getAvailableSlabs(); // Your function to fetch slabs
const slabPrices = await Promise.all(
slabMarkets.map(async (slab) => {
const state = await slabClient.getSlabState(slab);
return { slab, markPx: state?.markPx || new BN(0) };
})
);
// Sort by best price
slabPrices.sort((a, b) => {
if (side === 'buy') {
return a.markPx.cmp(b.markPx); // Lowest first for buys
} else {
return b.markPx.cmp(a.markPx); // Highest first for sells
}
});
// Build splits with slippage protection
const splits: SlabSplit[] = slabPrices.slice(0, 3).map((item, idx) => {
const slippageBps = maxSlippage * 10000 * (idx + 1);
const slippageAdjustment = item.markPx.muln(slippageBps).divn(10000);
const limitPrice = side === 'buy'
? item.markPx.add(slippageAdjustment)
: item.markPx.sub(slippageAdjustment);
return {
slabMarket: item.slab,
isBuy: side === 'buy',
size: new BN((targetSize / 3) * 1_000_000),
price: limitPrice,
};
});
const ix = router.buildExecuteCrossSlabInstruction(
wallet.publicKey,
splits,
slabProgramId
);
const tx = new Transaction().add(ix);
const signature = await connection.sendTransaction(tx, [wallet]);
await connection.confirmTransaction(signature);
return signature;
}4. Liquidations (Keeper Bots)
Monitor and Liquidate Undercollateralized Positions
async function liquidationKeeper() {
// Scan for unhealthy portfolios
const registry = await router.getRegistry();
if (!registry) return;
for (let i = 0; i < registry.numPortfolios; i++) {
// Get portfolio data (you'd need to track users)
const user = getUserAtIndex(i); // Your indexing function
const portfolio = await router.getPortfolio(user);
if (!portfolio) continue;
const health = portfolio.health.toNumber() / 1e6;
if (health < 100) {
console.log(`Found liquidation target: ${user.toString()}`);
await liquidateUser(user);
}
}
}
async function liquidateUser(targetUser: PublicKey) {
const [portfolioPDA] = router.derivePortfolioPDA(targetUser);
// Get required oracle and slab accounts
const oracles = [
new PublicKey('OracleAddress1'),
new PublicKey('OracleAddress2'),
];
const slabs = [
new PublicKey('SlabAddress1'),
new PublicKey('SlabAddress2'),
];
const params = {
portfolio: portfolioPDA,
oracles,
slabs,
isPreliq: false,
currentTs: new BN(Math.floor(Date.now() / 1000)),
};
const ix = router.buildLiquidateUserInstruction(params);
const tx = new Transaction().add(ix);
const signature = await connection.sendTransaction(tx, [wallet]);
await connection.confirmTransaction(signature);
console.log('Liquidation executed:', signature);
}
// Run keeper continuously
setInterval(liquidationKeeper, 10000); // Check every 10 secondsPre-liquidation (Warning System)
async function preliquidateUser(targetUser: PublicKey) {
const [portfolioPDA] = router.derivePortfolioPDA(targetUser);
const params = {
portfolio: portfolioPDA,
oracles: [],
slabs: [],
isPreliq: true, // Pre-liquidation flag
currentTs: new BN(Math.floor(Date.now() / 1000)),
};
const ix = router.buildLiquidateUserInstruction(params);
const tx = new Transaction().add(ix);
const signature = await connection.sendTransaction(tx, [wallet]);
console.log('Pre-liquidation warning sent:', signature);
}5. LP Operations
Initialize a Slab Market (LP)
async function createSlabMarket(instrumentPubkey: PublicKey) {
const markPx = new BN(50_000_000); // $50.00 initial mark price
const takerFeeBps = new BN(5_000); // 0.5% taker fee
const contractSize = new BN(1_000_000); // 1.0 contract size
const ix = slabClient.buildInitializeSlabInstruction(
wallet.publicKey, // LP owner
routerProgramId,
instrumentPubkey,
markPx,
takerFeeBps,
contractSize,
wallet.publicKey // payer
);
const tx = new Transaction().add(ix);
const signature = await connection.sendTransaction(tx, [wallet]);
await connection.confirmTransaction(signature);
// Derive slab address
const [slabPDA] = slabClient.deriveSlabPDA(wallet.publicKey, instrumentPubkey);
console.log('Slab created:', slabPDA.toString());
return slabPDA;
}Burn LP Shares
async function burnLpShares(
marketId: PublicKey,
sharesToBurn: number,
currentSharePrice: number
) {
const params = {
user: wallet.publicKey,
marketId,
sharesToBurn: new BN(sharesToBurn * 1_000_000),
currentSharePrice: new BN(currentSharePrice * 1_000_000),
currentTs: new BN(Math.floor(Date.now() / 1000)),
maxStalenessSeconds: new BN(60), // 1 minute
};
const ix = router.buildBurnLpSharesInstruction(params);
const tx = new Transaction().add(ix);
const signature = await connection.sendTransaction(tx, [wallet]);
await connection.confirmTransaction(signature);
console.log('LP shares burned:', signature);
}Cancel LP Orders
async function cancelLpOrders(
marketId: PublicKey,
orderIds: number[]
) {
if (orderIds.length > 16) {
throw new Error('Can only cancel up to 16 orders at once');
}
const params = {
user: wallet.publicKey,
marketId,
orderIds: orderIds.map(id => new BN(id)),
freedQuote: new BN(0), // Updated by program
freedBase: new BN(0), // Updated by program
};
const ix = router.buildCancelLpOrdersInstruction(params);
const tx = new Transaction().add(ix);
const signature = await connection.sendTransaction(tx, [wallet]);
await connection.confirmTransaction(signature);
console.log(`Cancelled ${orderIds.length} orders:`, signature);
}6. Market Data
Get Slab State
async function getMarketInfo(slabAddress: PublicKey) {
const state = await slabClient.getSlabState(slabAddress);
if (!state) {
console.log('Slab not found');
return;
}
console.log('Market Information:');
console.log(' LP Owner: ', state.lpOwner.toString());
console.log(' Instrument: ', state.instrument.toString());
console.log(' Mark Price: ', state.markPx.toString());
console.log(' Taker Fee (bps):', state.takerFeeBps.toString());
console.log(' Contract Size: ', state.contractSize.toString());
console.log(' Sequence Number:', state.seqno);
return state;
}Get Fill Receipt
async function getFillDetails(slabAddress: PublicKey, seqno: number) {
const receipt = await slabClient.getFillReceipt(slabAddress, seqno);
if (!receipt) {
console.log('Fill not found');
return;
}
console.log('Fill Details:');
console.log(' Slab: ', receipt.slab.toString());
console.log(' Sequence: ', receipt.seqno);
console.log(' Side: ', receipt.side === 0 ? 'BUY' : 'SELL');
console.log(' Quantity: ', receipt.qty.toString());
console.log(' Fill Price:', receipt.fillPx.toString());
console.log(' Timestamp: ', new Date(receipt.timestamp.toNumber() * 1000));
return receipt;
}7. Utility Functions
Format and Parse Amounts
import { formatAmount, parseAmount, formatUsd } from '@barista-dex/sdk';
// Format token amounts
const amount = new BN(1_500_000); // 1.5 USDC (6 decimals)
console.log(formatAmount(amount, 6)); // "1.500000"
console.log(formatUsd(amount)); // "$1.500000"
// Parse user input
const userInput = "1.5";
const parsed = parseAmount(userInput, 6);
console.log(parsed.toString()); // "1500000"Display Portfolio Summary
import {
formatUsd,
formatHealth,
formatTimestamp,
truncatePubkey
} from '@barista-dex/sdk';
async function displayPortfolio(userAddress: PublicKey) {
const portfolio = await router.getPortfolio(userAddress);
if (!portfolio) {
console.log('No portfolio found');
return;
}
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('PORTFOLIO SUMMARY');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log(`Owner: ${truncatePubkey(portfolio.owner.toString())}`);
console.log(`Collateral: ${formatUsd(portfolio.collateralValue)}`);
console.log(`Unrealized PnL: ${portfolio.unrealizedPnl.toString()}`);
console.log(`Equity: ${formatUsd(portfolio.equity)}`);
console.log(`Maintenance Margin: ${formatUsd(portfolio.maintMargin)}`);
console.log(`Health Ratio: ${formatHealth(portfolio.health)}`);
console.log(`Last Update: ${formatTimestamp(portfolio.lastUpdate)}`);
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
}Error Handling
import { SendTransactionError, LAMPORTS_PER_SOL } from '@solana/web3.js';
async function safeDeposit(solAmount: number) {
try {
const amount = new BN(solAmount * LAMPORTS_PER_SOL);
// Auto-create portfolio if needed
const ensurePortfolioIxs = await router.ensurePortfolioInstructions(wallet.publicKey);
const depositIx = await router.buildDepositInstruction(amount, wallet.publicKey);
const tx = new Transaction()
.add(...ensurePortfolioIxs)
.add(depositIx);
// Add recent blockhash and fee payer
tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
tx.feePayer = wallet.publicKey;
const signature = await connection.sendTransaction(tx, [wallet], {
skipPreflight: false,
preflightCommitment: 'confirmed',
});
// Wait for confirmation
const confirmation = await connection.confirmTransaction(signature, 'confirmed');
if (confirmation.value.err) {
throw new Error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}`);
}
console.log(`Deposited ${solAmount} SOL successfully:`, signature);
return signature;
} catch (error) {
if (error instanceof SendTransactionError) {
console.error('Transaction error:', error.message);
console.error('Logs:', error.logs);
} else {
console.error('Unexpected error:', error);
}
throw error;
}
}API Reference
RouterClient
Constructor
new RouterClient(connection: Connection, programId: PublicKey, wallet?: Keypair)PDA Derivation
derivePortfolioPDA(user: PublicKey): [PublicKey, number]deriveVaultPDA(mint: PublicKey): [PublicKey, number]deriveRegistryPDA(): [PublicKey, number]deriveAuthorityPDA(): [PublicKey, number]
Account Fetching
getPortfolio(user: PublicKey): Promise<Portfolio | null>getRegistry(): Promise<Registry | null>getVault(mint: PublicKey): Promise<Vault | null>
Instruction Builders
buildInitializeInstruction(payer: PublicKey): TransactionInstructionbuildDepositInstruction(amount: BN, user: PublicKey): Promise<TransactionInstruction>(SOL only in v0)buildWithdrawInstruction(amount: BN, user: PublicKey): Promise<TransactionInstruction>(SOL only in v0)buildInitializePortfolioInstruction(user: PublicKey): TransactionInstructionensurePortfolioInstructions(user: PublicKey): Promise<TransactionInstruction[]>(auto-creates portfolio if needed)buildExecuteCrossSlabInstruction(user, splits, slabProgram): TransactionInstructionbuildLiquidateUserInstruction(params: LiquidationParams): TransactionInstructionbuildBurnLpSharesInstruction(params: BurnLpSharesParams): TransactionInstructionbuildCancelLpOrdersInstruction(params: CancelLpOrdersParams): TransactionInstruction
SlabClient
Constructor
new SlabClient(connection: Connection, programId: PublicKey, wallet?: Keypair)PDA Derivation
deriveSlabPDA(lpOwner: PublicKey, instrument: PublicKey): [PublicKey, number]deriveFillReceiptPDA(slab: PublicKey, seqno: number): [PublicKey, number]
Account Fetching
getSlabState(slab: PublicKey): Promise<SlabState | null>getFillReceipt(slab: PublicKey, seqno: number): Promise<FillReceipt | null>getOrderBook(slab: PublicKey): Promise<OrderBook>
Instruction Builders
buildInitializeSlabInstruction(lpOwner, routerId, instrument, markPx, takerFeeBps, contractSize, payer): TransactionInstructionbuildCommitFillInstruction(slab, expectedSeqno, side, qty, limitPx, routerSigner): TransactionInstruction
Types
Portfolio
interface Portfolio {
owner: PublicKey;
collateralValue: BN;
maintMargin: BN;
unrealizedPnl: BN;
equity: BN;
health: BN;
lastUpdate: BN;
}SlabSplit
interface SlabSplit {
slabMarket: PublicKey;
isBuy: boolean;
size: BN;
price: BN;
}LiquidationParams
interface LiquidationParams {
portfolio: PublicKey;
oracles: PublicKey[];
slabs: PublicKey[];
isPreliq: boolean;
currentTs: BN;
}Best Practices
Always check portfolio health before trading
const portfolio = await router.getPortfolio(wallet.publicKey); if (portfolio.health.toNumber() / 1e6 < 110) { console.warn('Low health - add collateral or reduce position'); }
Use transaction confirmation
const signature = await connection.sendTransaction(tx, [wallet]); await connection.confirmTransaction(signature, 'confirmed');
Handle errors gracefully
try { await executeTrade(); } catch (error) { console.error('Trade failed:', error); // Implement retry logic or alert user }
Monitor for liquidations (keepers)
- Scan portfolios periodically
- React quickly to unhealthy positions
- Ensure sufficient gas for liquidation transactions
Development
# Install dependencies
npm install
# Build TypeScript
npm run build
# Run tests
npm test
# Clean build artifacts
npm run cleanLicense
MIT