SDK Reference
orbit-dlmm is the official TypeScript SDK for the Orbit Finance DLMM protocol on Solana.
All write methods return unsigned VersionedTransaction[], your app signs and sends them.
npm
orbit-dlmm · v0.1.0 · MIT
Learn moreGitHub
Full source, examples, and issue tracker
Learn moreREST API
Pool data, prices, trades, and WebSocket streaming
Learn moreInstall
npm install orbit-dlmm @coral-xyz/anchor @solana/web3.jsQuick Start
Three lines from zero to a live swap:
import { Connection, PublicKey } from "@solana/web3.js";
import { CipherDlmm } from "orbit-dlmm";
// 1. Connect to the CIPHER/USDC pool
const connection = new Connection("https://api.mainnet-beta.solana.com");
const dlmm = await CipherDlmm.create(
connection,
new PublicKey("EoLGqHKvtK9NcxjjnvSxTYYuFMYDeWTFFyKYj1DcJyPB")
);
// 2. Build a swap transaction (buy CIPHER with 1 USDC)
const tx = await dlmm.swap({
user: walletPubkey,
amountIn: 1_000_000n, // 1 USDC (6 decimals)
minAmountOut: 200_000_000n, // min 0.2 CIPHER (9 decimals)
swapForBase: true,
});
// 3. Sign and send
const signed = await wallet.signTransaction(tx);
await connection.sendRawTransaction(signed.serialize());CipherDlmm Class
The main entry point. Create one instance per pool.
Create
import { CipherDlmm, type CipherDlmmConfig } from "orbit-dlmm";
// Default program ID (mainnet)
const dlmm = await CipherDlmm.create(connection, poolAddress);
// Custom program ID (devnet / forked)
const config: CipherDlmmConfig = {
programId: new PublicKey("your_program_id"),
};
const dlmm = await CipherDlmm.create(connection, poolAddress, config);Refresh pool state
The class caches pool state on creation. Call refetch() before operations that depend on current price or reserves:
await dlmm.refetch();
const { binId, price } = dlmm.getActiveBin();Read pool state
// Current active bin and price (uses cache)
const { binId, price, priceQ64_64 } = dlmm.getActiveBin();
// Bin liquidity data. Pass a range or fetch all.
const bins = await dlmm.getBinArrays(-1100, -900);
const allBins = await dlmm.getBinArrays(); // no args = all bins
// Positions for a wallet on this pool
const positions = await dlmm.getPositionsByUser(walletPubkey);
// Positions across ALL pools (static method)
const all = await CipherDlmm.getAllPositionsByUser(connection, walletPubkey);Add liquidity, by strategy
Returns VersionedTransaction[]. Multiple transactions are returned automatically if the bin range exceeds 32 bins.
const txs = await dlmm.addLiquidityByStrategy({
user: walletPubkey,
positionNonce: 0n, // increment for each new position
totalBaseAmount: 1_000_000_000n, // 1 CIPHER (9 decimals)
totalQuoteAmount: 5_000_000n, // 5 USDC (6 decimals)
strategy: "concentrated", // see Distribution Strategies below
decay: 0.3, // tightness (0 = wide, 1 = narrow)
minBinId: -1020,
maxBinId: -960,
slippageBps: 100, // optional, default no check
});
for (const tx of txs) {
const signed = await wallet.signTransaction(tx);
await connection.sendRawTransaction(signed.serialize());
}Add liquidity, raw (per-bin)
For precise control over individual bin amounts:
import { buildAddLiquidityIxs } from "orbit-dlmm";
const ixChunks = await buildAddLiquidityIxs(connection, poolAddress, user, nonce, [
{ binIndex: -1000, baseAtoms: 500_000_000n, quoteAtoms: 0n },
{ binIndex: -999, baseAtoms: 300_000_000n, quoteAtoms: 2_500_000n },
{ binIndex: -998, baseAtoms: 0n, quoteAtoms: 2_500_000n },
]);Remove liquidity
// Withdraw everything
const tx = await dlmm.removeLiquidity({
user: walletPubkey,
position: positionPubkey,
});
// Partial withdrawal. 50% from specific bins.
const tx = await dlmm.removeLiquidity({
user: walletPubkey,
position: positionPubkey,
binIndices: [-1000, -999, -998],
bpsPct: 5000, // 50% (10000 = 100%)
});
// Full withdrawal + claim fees + close position in one shot
const tx = await dlmm.removeLiquidity({
user: walletPubkey,
position: positionPubkey,
shouldClaimAndClose: true,
});Swap
// Buy base token (swapForBase: true = spend quote, receive base)
const tx = await dlmm.swap({
user: walletPubkey,
amountIn: 1_000_000n, // 1 USDC
minAmountOut: 200_000_000n, // min 0.2 CIPHER
swapForBase: true,
});
// Sell base token
const tx = await dlmm.swap({
user: walletPubkey,
amountIn: 500_000_000n, // 0.5 CIPHER
minAmountOut: 1_500_000n, // min 1.5 USDC
swapForBase: false,
});Positions
// Create a new position (increment nonce for each one)
const initTx = await dlmm.initializePosition(walletPubkey, 0n);
// Close an empty position (must remove all liquidity first)
const closeTx = await dlmm.closePosition(walletPubkey, positionPubkey);CIPHER staker rewards
CIPHER staked on Streamflow earns USDC from protocol fee revenue. The initHolderState call is a one-time setup per wallet.
// One-time init. Required before first claim.
const initTx = await dlmm.initHolderState(walletPubkey);
// Claim accumulated USDC rewards
const claimTx = await dlmm.claimHolderRewards(walletPubkey);Distribution Strategies
Seven strategies control how liquidity is spread across bins. decay (0–1) controls tightness for bell-curve shapes.
Pass the strategy name to addLiquidityByStrategy, or import individual weight functions for custom builds:
import {
weightsUniform,
weightsBalanced,
weightsConcentrated,
weightsSkewBid,
weightsSkewAsk,
weightsBidAsk,
weightsCurve,
calculateDistributionWeights,
allocateToBins,
validateDistribution,
} from "orbit-dlmm";
// Manual weight generation
const weights = weightsConcentrated(20, 0.4); // 20 bins, decay 0.4
// Or via the dispatcher:
const weights = calculateDistributionWeights("concentrated", 20, 0.4);
// Allocate to bins
const allocs = allocateToBins(binIndices, weights, baseAtoms, quoteAtoms, activeBin);
// Validate before submitting
const { valid, errors, warnings } = validateDistribution(allocs, baseAtoms, quoteAtoms);
if (!valid) throw new Error(errors.join(", "));Account Fetching
Standalone read functions, no class instance needed.
import {
fetchPool,
fetchAllPools,
fetchBinArraysForRange,
fetchAllBinArrays,
fetchPositions,
fetchAllUserPositions,
createReadOnlyProgram,
} from "orbit-dlmm";
// Single pool
const pool = await fetchPool(connection, poolAddress);
// All pools deployed under the program
const pools = await fetchAllPools(connection);
// Bin arrays for a range
const bins = await fetchBinArraysForRange(connection, poolAddress, -1100, -900);
// All bin arrays for a pool
const allBins = await fetchAllBinArrays(connection, poolAddress);
// Positions for a user on a specific pool
const positions = await fetchPositions(connection, walletPubkey, poolAddress);
// All positions across all pools
const allPositions = await fetchAllUserPositions(connection, walletPubkey);
// Anchor program (read-only, for raw account access)
const program = createReadOnlyProgram(connection);
const poolRaw = await program.account.pool.fetch(poolAddress);PDA Derivation
All account addresses used by the program can be derived deterministically.
Pool & bin arrays
import {
derivePoolPda,
deriveBinArrayPda,
getBinArrayLowerIndex,
getBinOffsetInArray,
groupBinsByArray,
getUniqueBinArrayPdas,
derivePoolAuthorityPda,
canonicalBinIndexU64,
decodeBinIndexSigned,
} from "orbit-dlmm";
// Derive pool address from token pair + fee config
const [poolPda] = derivePoolPda(baseMint, quoteMint, binStepBps, baseFeeBps);
// Derive pool authority (signer PDA for token vaults)
const [authority] = derivePoolAuthorityPda(baseMint, quoteMint, binStepBps, baseFeeBps);
// Find the bin array that contains a given bin
const lowerIndex = getBinArrayLowerIndex(-993); // => -1024
const [binArrayPda] = deriveBinArrayPda(poolPda, lowerIndex);
// Offset within the bin array (0–63)
const offset = getBinOffsetInArray(-993); // => 31
// Group a list of bin IDs by their parent bin array
const grouped = groupBinsByArray([-1000, -999, -998, -950]);
// Get unique bin array PDAs needed for a set of bin IDs
const pdas = getUniqueBinArrayPdas(poolPda, [-1000, -999, -950]);
// Convert signed bin ID to on-chain u64 representation and back
const asU64 = canonicalBinIndexU64(-993);
const signed = decodeBinIndexSigned(asU64);Positions
import {
derivePositionPda,
derivePositionBinPda,
deriveLiquidityLockPda,
} from "orbit-dlmm";
const [positionPda] = derivePositionPda(poolPda, walletPubkey, positionNonce);
const [posBinPda] = derivePositionBinPda(positionPda, binIndex);
const [lockPda] = deriveLiquidityLockPda(positionPda);Reward states
import {
deriveHolderGlobalStatePda,
deriveUserHolderStatePda,
deriveNftGlobalStatePda,
deriveUserNftStatePda,
} from "orbit-dlmm";
// CIPHER staker reward accounts
const [holderGlobal] = deriveHolderGlobalStatePda();
const [userHolder] = deriveUserHolderStatePda(walletPubkey);
// NFT staker reward accounts
const [nftGlobal] = deriveNftGlobalStatePda();
const [userNft] = deriveUserNftStatePda(walletPubkey);Price Math
import {
calculatePriceQ64_64,
q64_64ToDecimal,
calculateRelativePrice,
binIdToPrice,
priceToBinId,
estimatePriceRange,
} from "orbit-dlmm";
// Decimal ↔ Q64.64 (on-chain format)
const q = calculatePriceQ64_64(0.00439, 9, 6); // CIPHER price
const price = q64_64ToDecimal(q, 9, 6); // => 0.00439
// Relative price between two Q64.64 values
const rel = calculateRelativePrice(priceA, priceB);
// Bin ID ↔ price
const mult = binIdToPrice(-993, 125); // bin -993 at 125bps step
const binId = priceToBinId(0.00439, 125); // nearest bin for price
// Estimate bin range for a price range
const { minBinId, maxBinId } = estimatePriceRange(
0.0040, // lower price
0.0048, // upper price
125 // bin step
);Instruction Builders
Low-level builders return raw TransactionInstruction[]. Use these when building custom transactions or batching instructions manually.
import {
buildAddLiquidityIxs,
buildRemoveLiquidityIx,
buildSwapIxs,
buildInitPositionIx,
buildClosePositionIx,
buildInitHolderStateIx,
buildClaimHolderRewardsIxs,
buildSyncHolderStakeIx,
} from "orbit-dlmm";Sync stake checkpoint
Call this after any Streamflow stake or unstake. Records a time-weighted checkpoint used for pro-rata reward distribution.
const syncIx = buildSyncHolderStakeIx(
walletPubkey,
stakeEntryNonce, // your Streamflow stake entry nonce
stakePoolAddress, // defaults to CIPHER staking pool
);Claim holder rewards (manual)
Returns [ensureATA, syncRewardIndexes, claimHolderRewards], submit as a single transaction:
const ixs = await buildClaimHolderRewardsIxs(connection, walletPubkey, poolAddress);
const { transaction } = await buildTransaction(connection, {
payer: walletPubkey,
instructions: ixs,
includeHeapFrame: true,
});Transaction builder
All SDK methods use this internally. Available for custom batching:
import { buildTransaction } from "orbit-dlmm";
const { transaction } = await buildTransaction(connection, {
payer: walletPubkey,
instructions: [ix1, ix2, ix3],
computeUnitPrice: 100_000, // micro-lamports per CU
computeUnitLimit: 400_000,
includeHeapFrame: true, // 256KB, required by the program
});Error Handling
import { parseProgramError, CipherDlmmError, CIPHER_DLMM_ERRORS } from "orbit-dlmm";
try {
await connection.sendRawTransaction(signed.serialize());
} catch (err) {
// Parse a numeric error code from a failed transaction
const parsed = parseProgramError(6006);
if (parsed) {
console.log(parsed.errorName); // "SlippageExceeded"
console.log(parsed.message); // "Swap did not meet minimum output"
}
}
// Browse all error codes
console.log(CIPHER_DLMM_ERRORS);
// { 6000: { errorName: "InvalidBinStep", ... }, 6001: ..., ... }
// CipherDlmmError is thrown by SDK methods for validation failures
// before any RPC call is made, e.g. "All bins rounded to zero"Types
Key types exported from the SDK:
import type {
// Pool
PoolState,
FeeConfig,
CompactBin,
BinArrayState,
ActiveBin,
// Positions
PositionState,
PositionBinState,
UserPosition,
// Instructions
AddLiquidityByStrategyParams,
AddLiquidityParams,
RemoveLiquidityParams,
SwapParams,
SwapQuoteResult,
InitPositionParams,
ClosePositionParams,
ClaimRewardsParams,
ClaimNftRewardsParams, // { user, nftMints: PublicKey[] }
// Misc
DistributionStrategy,
BinAllocation,
ValidationResult,
CipherDlmmConfig,
PriorityLevel,
BuildTransactionOptions,
PriceQ64_64,
} from "orbit-dlmm";Constants
import {
DEFAULT_PROGRAM_ID, // Fn3fA3fjsmpULNL7E9U79jKTe1KHxPtQeWdURCbJXCnM
BIN_ARRAY_SIZE, // 64
MAX_BINS_PER_TX, // 32
REQUIRED_HEAP_BYTES, // 262144 (256KB)
KNOWN_MINTS, // { CIPHER, USDC, SOL }
VALID_BIN_STEPS, // [1, 2, 4, 5, 8, 10, 15, 16, 20, 25, 30, 50, 75, ...]
} from "orbit-dlmm";
// CIPHER/USDC pool (mainnet)
const CIPHER_USDC_POOL = "EoLGqHKvtK9NcxjjnvSxTYYuFMYDeWTFFyKYj1DcJyPB";
// binStep: 125bps · fee: 200bps (2%)Fee Architecture
Every swap through an Orbit Finance pool generates protocol fees. Here’s how they flow:
CIPHER stakers earn from both fee streams. The more volume routed through Orbit pools, the higher the yield.
Protocol fee (CIPHER/USDC)
2.00%
per swap, all pools
CIPHER staker share
37.5%
of CIPHER/USDC pool fees
Distribution cadence
Weekly
paid in USDC
Building something on top of Orbit Finance?
Stake $CIPHER, earn from every swap.
Every integration that routes swap volume through Orbit pools contributes to the fee flywheel. CIPHER stakers earn 37.5% of all CIPHER/USDC pool fees, paid weekly in USDC.
Learn about $CIPHER staking →