@barista-dex/sdk
v0.1.46
Published
TypeScript SDK for interacting with Barista DEX programs on Solana
Maintainers
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
