| name | cdpm-calculation-skill |
| description | CDPM calculation utilities using Cetus DLMM SDK plus the Scallop and Kai SAV lending math used by scallop_supply / scallop_redeem / kai_supply / kai_redeem. Provides liquidity calculation, bin price math, position management, fee calculations, and yield-fee accounting for both lending integrations. Use when performing mathematical operations for CDPM positions. |
CDPM Calculation Guide
Overview
This skill provides calculation utilities for CDPM (Cetus DLMM Position Manager) using the Cetus DLMM SDK, plus off-chain twins of both lending integrations' upstream math — Scallop (scallop_*) and Kai SAV (kai_*). All Cetus calculations should use the SDK for accuracy and to handle edge cases properly; the Scallop and Kai formulas mirror the upstream protocol::mint / protocol::redeem and kai_vault::deposit / kai_vault::withdraw math that cdpm composes into single-call entries. The two integrations share pm.lending: Bag, the same fee_house.fee_rate knob, and the same principal-amortization shape — only the predictors differ (Scallop reads balance_sheet, Kai reads total_available_balance + total_yt_supply).
Installation
npm install @cetusprotocol/dlmm-sdk
SDK Imports
import { BinUtils, FeeUtils } from '@cetusprotocol/dlmm-sdk/utils'
Topics
Core Calculations
Lending Math (Scallop & Kai SAV)
- Scallop Lending Math —
predictScallopMint / predictScallopRedeem, principal amortization, yield-fee deduction, redemption sizing (inverse formulas + worked example), live supply APY via @scallop-io/sui-scallop-sdk, Scallop-vs-Kai picker.
- Kai SAV Lending Math —
predictKaiDeposit / predictKaiWithdraw for <T, YT> vaults; live APY via @kunalabs-io/kai.
- Cross-Protocol PTB (cdpm + Scallop + Kai) — Mysten-rooted shared-
Transaction pattern, approach comparison table, atomic Scallop ↔ Kai rebalance, dust-prediction patterns when composing redeem → add-liquidity.
Advanced Topics
Reference
Complete Examples
Example 1: Create Position Calculation
import { BinUtils } from '@cetusprotocol/dlmm-sdk/utils'
async function calculatePosition(
poolInfo: { bin_step: number; active_bin_id: number },
tokenADecimals: number,
tokenBDecimals: number,
depositA: string,
depositB: string,
slippagePercent: number
) {
const { bin_step, active_bin_id } = poolInfo
const currentPrice = BinUtils.getPriceFromBinId(
active_bin_id,
bin_step,
tokenADecimals,
tokenBDecimals
)
const minPrice = (parseFloat(currentPrice) * (1 - slippagePercent / 100)).toString()
const maxPrice = (parseFloat(currentPrice) * (1 + slippagePercent / 100)).toString()
const lowerBinId = BinUtils.getBinIdFromPrice(
minPrice, bin_step, true, tokenADecimals, tokenBDecimals
)
const upperBinId = BinUtils.getBinIdFromPrice(
maxPrice, bin_step, false, tokenADecimals, tokenBDecimals
)
const positionCount = BinUtils.getPositionCount(lowerBinId, upperBinId)
const binCount = upperBinId - lowerBinId + 1
const amountAPerBin = (BigInt(depositA) / BigInt(binCount)).toString()
const amountBPerBin = (BigInt(depositB) / BigInt(binCount)).toString()
const activeQPrice = BinUtils.getQPriceFromId(active_bin_id, bin_step)
const totalLiquidity = BinUtils.getLiquidity(depositA, depositB, activeQPrice)
return {
lowerBinId,
upperBinId,
positionCount,
totalLiquidity,
bins: Array.from({ length: binCount }, (_, i) => ({
binId: lowerBinId + i,
amountA: amountAPerBin,
amountB: amountBPerBin
}))
}
}
Example 2: Remove Liquidity Calculation
function calculateRemoval(
positionBins: Array<{
binId: number
amountA: string
amountB: string
liquidity: string
}>,
percentage: number
): Array<{ binId: number; amountA: string; amountB: string }> {
const results = []
for (const bin of positionBins) {
const removeLiquidity = (BigInt(bin.liquidity) * BigInt(percentage) / 100n).toString()
const { amount_a, amount_b } = BinUtils.calculateOutByShare(
{ amount_a: bin.amountA, amount_b: bin.amountB, liquidity: bin.liquidity },
removeLiquidity
)
results.push({
binId: bin.binId,
amountA: amount_a,
amountB: amount_b
})
}
return results
}
Example 3: Rebalancing Calculation
async function calculateRebalance(
currentBins: Array<{ binId: number; liquidity: string }>,
targetActiveBinId: number,
rangeWidth: number,
binStep: number
) {
const lowerBinId = targetActiveBinId - rangeWidth
const upperBinId = targetActiveBinId + rangeWidth
let totalLiquidity = 0n
for (const bin of currentBins) {
totalLiquidity += BigInt(bin.liquidity)
}
const targetBinCount = rangeWidth * 2 + 1
const liquidityPerBin = (totalLiquidity / BigInt(targetBinCount)).toString()
const targetBins = []
for (let i = 0; i < targetBinCount; i++) {
const binId = lowerBinId + i
const qPrice = BinUtils.getQPriceFromId(binId, binStep)
const amountA = (BigInt(liquidityPerBin) / (2n * BigInt(qPrice) >> 64n)).toString()
const amountB = (BigInt(liquidityPerBin) / 2n).toString()
targetBins.push({ binId, amountA, amountB, liquidity: liquidityPerBin })
}
const positionCount = BinUtils.getPositionCount(lowerBinId, upperBinId)
return { targetBins, positionCount }
}
Best Practices
1. Always Use SDK Utils
import { BinUtils } from '@cetusprotocol/dlmm-sdk/utils'
const liquidity = BinUtils.getLiquidity(amountA, amountB, qPrice)
const liquidity = (BigInt(price) * BigInt(amountA)) + (BigInt(amountB) << 64n)
2. Pass Amounts as Strings
const liquidity = BinUtils.getLiquidity('1000000', '1200000', qPrice)
const liquidity = BinUtils.getLiquidity(1000000, 1200000, qPrice)
3. Cache QPrice
const qPriceCache = new Map()
function getCachedQPrice(binId: number, binStep: number) {
const key = `${binId}-${binStep}`
if (!qPriceCache.has(key)) {
qPriceCache.set(key, BinUtils.getQPriceFromId(binId, binStep))
}
return qPriceCache.get(key)
}
4. Validate Inputs
function validateBinRange(lowerBinId: number, upperBinId: number) {
if (lowerBinId >= upperBinId) {
throw new Error('Invalid range: lower must be less than upper')
}
if (upperBinId - lowerBinId > 1000) {
throw new Error('Range too large: max 1000 bins')
}
}
5. Handle Errors
try {
const binId = BinUtils.getBinIdFromPrice(price, binStep, true, decimalsA, decimalsB)
} catch (error) {
console.error('Failed to calculate bin ID:', error)
}
Related Skills
cdpm-user-sdk - User operations guide
cdpm-agent-sdk - Agent automation strategies
cdpm-protocol-sdk - Protocol integration guide
cetus-dlmm-sdk-skill - Full Cetus DLMM SDK documentation