Skip to main content
The interest rate curve shows how borrow and supply APY change as pool utilization increases from 0% to 100%, where utilization represents the share of supplied liquidity that is borrowed.

Calculate Interest Rate Curve

Generate curve points showing APY at different utilization levels.
1

Import Dependencies

Import the required packages for Solana RPC communication and Kamino SDK operations.
import { createSolanaRpc, address } from '@solana/kit';
import { KaminoMarket, DEFAULT_RECENT_SLOT_DURATION_MS, SLOTS_PER_SECOND, PROGRAM_ID } from '@kamino-finance/klend-sdk';
import { getBorrowRate, calculateAPYFromAPR } from '@kamino-finance/klend-sdk/dist/classes/utils';
2

Configure Market and Reserve

Set up the lending market, reserve address, and calculation parameters.
const LENDING_MARKET_ID = address('7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF');
const RESERVE_ADDRESS = address('ESCkPWKHmgNE7Msf77n9yzqJd5kQVWWGy3o5Mgxhvavp');
const STEP = 0.05;
The STEP value determines curve granularity: 0.05 = 21 points, 0.01 = 101 points. Smaller steps provide smoother curves but increase computation.
3

Load Market and Reserve

Initialize RPC connection and load the market and reserve data.
const rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');
const market = await KaminoMarket.load(rpc, LENDING_MARKET_ID, DEFAULT_RECENT_SLOT_DURATION_MS, PROGRAM_ID);
const reserve = market!.getReserveByAddress(RESERVE_ADDRESS);

if (!reserve) throw new Error(`Reserve ${RESERVE_ADDRESS} not found`);

const currentSlot = await rpc.getSlot().send();
The getReserveByAddress method retrieves reserve configuration including the borrow rate curve points and protocol parameters needed for APY calculations.
4

Extract Borrow Curve Points

Convert the reserve’s borrow rate curve from basis points to decimal values.
const ONE_HUNDRED_PCT_IN_BPS = 10_000;
const borrowCurve: [number, number][] = [];

for (const { utilizationRateBps, borrowRateBps } of reserve.state.config.borrowRateCurve.points) {
  borrowCurve.push([utilizationRateBps / ONE_HUNDRED_PCT_IN_BPS, borrowRateBps / ONE_HUNDRED_PCT_IN_BPS]);
  if (utilizationRateBps === ONE_HUNDRED_PCT_IN_BPS) break;
}
The borrow curve defines interest rates at specific utilization thresholds and interpolates between them. Rates rise sharply after 80% utilization to discourage the pool from becoming fully utilized.
5

Calculate Adjustment Parameters

Get parameters needed to convert per-slot rates to annual APY.
const slotDuration = DEFAULT_RECENT_SLOT_DURATION_MS;
const slotAdjustmentFactor = 1000 / SLOTS_PER_SECOND / slotDuration;
const protocolTakeRatePct = 1 - (reserve.state.config.protocolTakeRatePct / 100);
const fixedHostInterestRate = reserve.getFixedHostInterestRate();
Key Parameters Explained
  • Slot Adjustment Factor: Converts per-slot rates to annual rates. Solana has ~2 slots per second with average 400ms duration.
  • Protocol Take Rate: The percentage of borrow interest paid to suppliers. If protocol takes 10%, suppliers receive 90% of interest paid by borrowers.
  • Fixed Host Interest Rate: A small base rate added to all borrows, ensuring borrowers pay at least a minimum rate even at 0% utilization.
6

Generate Curve Points

Calculate borrow and supply APY at each utilization level.
const curvePoints = [];
const numSteps = Math.round(1.0 / STEP);

for (let i = 0; i <= numSteps; i++) {
  const utilization = i / numSteps;
  const borrowAPR = getBorrowRate(utilization, borrowCurve) * slotAdjustmentFactor;
  const borrowRate = calculateAPYFromAPR(
    fixedHostInterestRate.mul(slotAdjustmentFactor).add(borrowAPR).toNumber()
  );
  const supplyRate = calculateAPYFromAPR(utilization * borrowAPR * protocolTakeRatePct);

  curvePoints.push({
    utilization: utilization,
    borrowApy: borrowRate * 100,
    supplyApy: supplyRate * 100
  });
}
How the Calculation Works
  1. getBorrowRate interpolates the borrow rate from the curve at the given utilization
  2. calculateAPYFromAPR converts APR to APY: APY = (1 + rate/periods)^periods - 1, accounting for compound interest per slot
  3. Supply APY formula: supplyAPY = utilization × borrowAPR × (1 - protocolFee) - suppliers earn interest only on borrowed funds, minus the protocol fee
7

Build Result with Current Metrics

Combine the curve data with current reserve metrics.
const result = {
  symbol: reserve.symbol,
  reserveAddress: RESERVE_ADDRESS.toString(),
  currentUtilization: reserve.calculateUtilizationRatio(),
  currentBorrowApy: reserve.totalBorrowAPY(currentSlot) * 100,
  currentSupplyApy: reserve.totalSupplyAPY(currentSlot) * 100,
  curvePoints
};

console.log(JSON.stringify(result, null, 2));
The result includes the reserve symbol, current utilization and APY values, plus an array of curve points. Each curve point contains the utilization level, borrow APY, and supply APY at that utilization. This data can be used to visualize the interest rate curve with charting libraries.

Understanding the Results

The curve shows how rates change with utilization:
  • Low utilization (0-50%): Borrow rates are low to encourage borrowing, supply rates are minimal
  • Medium utilization (50-80%): Rates increase moderately to balance supply and demand - this is the target range
  • High utilization (80-100%): Rates increase sharply to incentivize repayment and new deposits

Example Output

{
  "symbol": "USDG",
  "reserveAddress": "ESCkPWKHmgNE7Msf77n9yzqJd5kQVWWGy3o5Mgxhvavp",
  "currentUtilization": 0.7888,
  "currentBorrowApy": 8.2,
  "currentSupplyApy": 5.8,
  "curvePoints": [
    { "utilization": 0.0, "borrowApy": 2.1, "supplyApy": 0.0 },
    { "utilization": 0.25, "borrowApy": 3.5, "supplyApy": 0.8 },
    { "utilization": 0.5, "borrowApy": 5.2, "supplyApy": 2.3 },
    { "utilization": 0.75, "borrowApy": 8.9, "supplyApy": 5.9 },
    { "utilization": 1.0, "borrowApy": 45.2, "supplyApy": 40.1 }
  ]
}