SDK Reference

The TypeScript SDK (perk-protocol) wraps all on-chain instructions and provides helper functions for building on Perk.


Installation

npm install perk-protocol

Peer dependencies: @solana/web3.js, @coral-xyz/anchor, @solana/spl-token, bn.js


PerkClient

The main entry point. Wraps the Anchor program and exposes typed methods for every instruction.

Constructor

import { PerkClient } from "perk-protocol";
import { Connection, PublicKey } from "@solana/web3.js";

const client = new PerkClient({
  connection: new Connection("https://your-rpc-endpoint.com"),
  wallet: yourWallet,              // Anchor Wallet interface
  programId: PERK_PROGRAM_ID,     // Optional, defaults to mainnet program
  commitment: "confirmed",         // Optional
  preInstructions: [],             // Optional, e.g. ComputeBudget instructions
});

Account Fetchers

// Protocol config
const protocol = await client.fetchProtocol();

// Single market (by token mint and creator)
const market = await client.fetchMarket(tokenMint, creatorPubkey);

// All markets
const markets = await client.fetchAllMarkets();
// Returns: { address: PublicKey, account: MarketAccount }[]

// User position
const position = await client.fetchPosition(marketAddress, userPublicKey);

// All positions for a user
const positions = await client.fetchAllPositions(userPublicKey);

// Trigger orders for a user on a market
const orders = await client.fetchTriggerOrders(marketAddress, userPublicKey);

// PerkOracle price feed
const oracle = await client.fetchPerkOracle(tokenMint);
const oracleOrNull = await client.fetchPerkOracleNullable(tokenMint);

Trading

Deposit Collateral

import BN from "bn.js";

// First interaction: initialize position account
await client.initializePosition(tokenMint, creatorPubkey);

// Deposit 10 USDC
await client.deposit(
  tokenMint,
  creatorPubkey,
  oracleAddress,
  new BN(10_000_000),          // 10 USDC (6 decimals)
  fallbackOracleAddress,        // optional
);

Open Position

import { Side } from "perk-protocol";

// Open a 5x long position, 1 SOL size
await client.openPosition(
  tokenMint,
  creatorPubkey,
  oracleAddress,
  Side.Long,
  new BN(1_000_000_000),       // 1 SOL base size
  500,                          // 5x leverage (leverage × 100)
  300,                          // max slippage: 3% (300 bps)
  fallbackOracleAddress,        // optional
);

Leverage is scaled by 100: 200 = 2x, 500 = 5x, 1000 = 10x, 2000 = 20x.

Close Position

// Close entire position
await client.closePosition(tokenMint, creatorPubkey, oracleAddress);

// Close partial (0.5 SOL)
await client.closePosition(
  tokenMint,
  creatorPubkey,
  oracleAddress,
  new BN(500_000_000),          // 0.5 SOL
);

Withdraw

await client.withdraw(
  tokenMint,
  creatorPubkey,
  oracleAddress,
  new BN(5_000_000_000),        // 5 SOL
);

Trigger Orders

Place a Limit Order

import { TriggerOrderType, Side } from "perk-protocol";

await client.placeTriggerOrder(tokenMint, creatorPubkey, {
  orderType: TriggerOrderType.Limit,
  side: Side.Long,
  size: new BN(1_000_000_000),       // 1 SOL
  triggerPrice: new BN(140_000_000),  // $140 (1e6 scale)
  leverage: 500,                      // 5x
  reduceOnly: false,
  expiry: new BN(0),                  // GTC (0 = no expiry)
});

Place a Stop Loss

await client.placeTriggerOrder(tokenMint, creatorPubkey, {
  orderType: TriggerOrderType.StopLoss,
  side: Side.Long,                     // Side of position to close
  size: new BN(0),                     // 0 = close entire position
  triggerPrice: new BN(130_000_000),   // $130
  leverage: 0,                         // Ignored for reduce-only
  reduceOnly: true,
  expiry: new BN(0),
});

Place a Take Profit

await client.placeTriggerOrder(tokenMint, creatorPubkey, {
  orderType: TriggerOrderType.TakeProfit,
  side: Side.Long,
  size: new BN(0),
  triggerPrice: new BN(170_000_000),   // $170
  leverage: 0,
  reduceOnly: true,
  expiry: new BN(0),
});

Cancel a Trigger Order

await client.cancelTriggerOrder(tokenMint, creatorPubkey, orderId);

Market Creation

import { OracleSource } from "perk-protocol";

await client.createMarket(
  tokenMint,
  oracleAddress,
  {
    oracleSource: OracleSource.PerkOracle,
    maxLeverage: 1000,                              // 10x
    tradingFeeBps: 30,                               // 0.3%
    initialK: new BN("1000000000000000000"),         // 1e18
  }
);

PerkOracle (Permissionless / Admin / Cranker)

Initialize Oracle (Permissionless)

Anyone can initialize a PerkOracle for any SPL token by paying rent. The oracle authority is inherited from Protocol.oracle_authority.

await client.initializePerkOracle(
  tokenMint,
  {
    minSources: 2,
    maxStalenessSeconds: 15,
    maxPriceChangeBps: 0,               // 0 = no banding (memecoin)
    circuitBreakerDeviationBps: 0,      // 0 = disabled
  }
);

Set Oracle Authority (Admin, One-Time)

Admin sets the oracle authority (cranker pubkey) on the Protocol account. All newly initialized oracles inherit this authority.

await client.adminSetOracleAuthority(
  newOracleAuthorityPubkey
);

Update Oracle Price (Cranker)

await client.updatePerkOracle(tokenMint, {
  price: new BN(150_320_000),          // $150.32
  confidence: new BN(50_000),          // $0.05 spread
  numSources: 2,
});

Freeze / Unfreeze (Admin)

await client.freezePerkOracle(tokenMint, true);   // freeze
await client.freezePerkOracle(tokenMint, false);   // unfreeze

Update Oracle Config (Admin)

await client.updateOracleConfig(tokenMint, {
  maxPriceChangeBps: 500,              // 5% band
  minSources: null,                     // don't change
  maxStalenessSeconds: null,            // don't change
  circuitBreakerDeviationBps: 1000,    // 10% circuit breaker
  maxConfidenceBps: 500,               // 5% confidence (null = don't change)
});

Cranker Instructions

These are permissionless — anyone can call them.

// Crank funding rate
await client.crankFunding(marketAddress, oracleAddress);

// Liquidate an underwater position
await client.liquidate(
  marketAddress,
  oracleAddress,
  targetUserPubkey,
  liquidatorTokenAccount,
);

// Execute a triggered order
await client.executeTriggerOrder(
  marketAddress,
  oracleAddress,
  targetUserPubkey,
  orderId,
  executorTokenAccount,
);

// Re-peg the vAMM
await client.updateAmm(marketAddress, oracleAddress);

// Reclaim an empty position account
await client.reclaimEmptyAccount(
  marketAddress,
  oracleAddress,
  positionOwnerPubkey,
);

Fee Claims

// Creator or protocol admin claims accumulated fees
await client.claimFees(tokenMint, creatorPubkey, recipientTokenAccount);

Math Helpers

The SDK exports pure functions for client-side calculations:

import {
  calculateMarkPrice,
  estimateExecutionPrice,
  calculateSlippageBps,
  effectivePositionQ,
  calculateNotional,
  marginRatio,
  isLiquidatable,
  estimateLiquidationPrice,
  calculateFee,
  fundingRateAnnualized,
  warmupProgress,
  haircutRatio,
  accountEquity,
  calculateNotionalBN,
  isAboveMaintenanceMargin,
  amountToNumber,
  priceToNumber,
  numberToPrice,
} from "perk-protocol";

// Mark price from vAMM state
const mark = calculateMarkPrice(market);

// Estimated execution price for a trade
const execPrice = estimateExecutionPrice(market, side, baseSize);

// Slippage in basis points
const slippage = calculateSlippageBps(executionPrice, markPrice);

// Is the position liquidatable?
const underwater = isLiquidatable(position, market, oraclePrice);

// Liquidation price estimate
const liqPrice = estimateLiquidationPrice(position, market, oraclePrice);

// Account equity (max(0, collateral + pnl - feeDebt))
const equity = accountEquity(position);

// Warmup progress (0 to 1)
const progress = warmupProgress(position, market, currentSlot);

// Haircut ratio
const h = haircutRatio(market);

PDA Helpers

import {
  findProtocolAddress,
  findMarketAddress,
  findPositionAddress,
  findVaultAddress,
  findTriggerOrderAddress,
  findPerkOracleAddress,
} from "perk-protocol";

const [protocolPda] = findProtocolAddress(programId);
const [marketPda] = findMarketAddress(tokenMint, creatorPubkey, programId);
const [positionPda] = findPositionAddress(marketPda, userPubkey, programId);
const [vaultPda] = findVaultAddress(marketPda, programId);
const [triggerPda] = findTriggerOrderAddress(marketPda, userPubkey, orderId, programId);
const [oraclePda] = findPerkOracleAddress(tokenMint, programId);

Types

All account types and enums are exported:

import type {
  ProtocolAccount,
  MarketAccount,
  UserPositionAccount,
  TriggerOrderAccount,
  PerkOracleAccount,
  CreateMarketParams,
  TriggerOrderParams,
  PositionInfo,
  MarketInfo,
} from "perk-protocol";

import {
  Side,
  OracleSource,
  SideState,
  TriggerOrderType,
} from "perk-protocol";

See types.ts for the full type definitions.


Constants

All on-chain constants are mirrored in the SDK:

import {
  PERK_PROGRAM_ID,        // 3L72e4b8wKJ8ReMpLUeXxVNrRGpiK6m4VYxeSnecpNW2
  PRICE_SCALE,             // 1_000_000
  CREATOR_FEE_SHARE_BPS,   // 1000 (10%)
  MAINTENANCE_MARGIN_BPS,  // 500 (5%)
  MAX_LEVERAGE,            // 2000 (20x)
  MIN_TRADING_FEE_BPS,     // 3 (0.03%)
  MAX_TRADING_FEE_BPS,     // 100 (1%)
  WARMUP_PERIOD_SLOTS,     // 1000
  MAX_TRIGGER_ORDERS,      // 8
  // ... see constants.ts for full list
} from "perk-protocol";