agentsclimarketplace

Cdpm calculation skill

Skill RandyPen/cdpm/skills/cdpm-calculation-skill

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.From its SKILL.md

Install
npx -y skills add RandyPen/cdpm --skill cdpm-calculation-skill

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

8.7 KB, ~2.3k tokens by cl100k_base, as published. Nobody here has run it

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 MathpredictScallopMint / 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 MathpredictKaiDeposit / 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
  
  // 1. Get current price
  const currentPrice = BinUtils.getPriceFromBinId(
    active_bin_id,
    bin_step,
    tokenADecimals,
    tokenBDecimals
  )
  
  // 2. Calculate price range with slippage
  const minPrice = (parseFloat(currentPrice) * (1 - slippagePercent / 100)).toString()
  const maxPrice = (parseFloat(currentPrice) * (1 + slippagePercent / 100)).toString()
  
  // 3. Get bin IDs
  const lowerBinId = BinUtils.getBinIdFromPrice(
    minPrice, bin_step, true, tokenADecimals, tokenBDecimals
  )
  const upperBinId = BinUtils.getBinIdFromPrice(
    maxPrice, bin_step, false, tokenADecimals, tokenBDecimals
  )
  
  // 4. Calculate position count
  const positionCount = BinUtils.getPositionCount(lowerBinId, upperBinId)
  
  // 5. Distribute liquidity
  const binCount = upperBinId - lowerBinId + 1
  const amountAPerBin = (BigInt(depositA) / BigInt(binCount)).toString()
  const amountBPerBin = (BigInt(depositB) / BigInt(binCount)).toString()
  
  // 6. Calculate total liquidity
  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  // 0-100
): 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
  
  // 1. Calculate total liquidity
  let totalLiquidity = 0n
  for (const bin of currentBins) {
    totalLiquidity += BigInt(bin.liquidity)
  }
  
  // 2. Calculate new distribution
  const targetBinCount = rangeWidth * 2 + 1
  const liquidityPerBin = (totalLiquidity / BigInt(targetBinCount)).toString()
  
  // 3. Get amounts needed for each bin
  const targetBins = []
  for (let i = 0; i < targetBinCount; i++) {
    const binId = lowerBinId + i
    const qPrice = BinUtils.getQPriceFromId(binId, binStep)
    
    // For equal distribution, amounts depend on price
    // amount_a = liquidity / (2 * price), amount_b = liquidity / 2
    const amountA = (BigInt(liquidityPerBin) / (2n * BigInt(qPrice) >> 64n)).toString()
    const amountB = (BigInt(liquidityPerBin) / 2n).toString()
    
    targetBins.push({ binId, amountA, amountB, liquidity: liquidityPerBin })
  }
  
  // 4. Calculate positions needed
  const positionCount = BinUtils.getPositionCount(lowerBinId, upperBinId)
  
  return { targetBins, positionCount }
}

Best Practices

1. Always Use SDK Utils

// Good - Use SDK
import { BinUtils } from '@cetusprotocol/dlmm-sdk/utils'
const liquidity = BinUtils.getLiquidity(amountA, amountB, qPrice)

// Bad - Manual calculation
const liquidity = (BigInt(price) * BigInt(amountA)) + (BigInt(amountB) << 64n)

2. Pass Amounts as Strings

// Good - String format
const liquidity = BinUtils.getLiquidity('1000000', '1200000', qPrice)

// Bad - Number format (precision loss)
const liquidity = BinUtils.getLiquidity(1000000, 1200000, qPrice)

3. Cache QPrice

// Good - 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)
  // Fallback or retry logic
}

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

What ships with it: 13 files

142.8 KB alongside SKILL.md

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.