This tutorial shows you how to calculate APY for extra farm rewards that are earned when using specific collateral and debt combinations.
Extra farms provide additional rewards to users who deposit specific collateral and borrow specific assets.
Calculate APY for extra farm rewards paid in stablecoins like CASH that are pegged to USD.
Import Dependencies
Import the required packages for Solana RPC communication, Kamino SDK operations, and decimal calculations.import { createSolanaRpc, address } from '@solana/kit';
import { FarmState, calculateCurrentRewardPerToken } from '@kamino-finance/farms-sdk';
import { KaminoMarket, PROGRAM_ID, lamportsToNumberDecimal } from '@kamino-finance/klend-sdk';
import Decimal from 'decimal.js';
type ExtraFarm = { market: string; farm: string; collMint: string; debtMint: string };
type Resources = { 'mainnet-beta': { extraFarms: ExtraFarm[] } };
Initialize RPC and Load Market
Set up the Solana RPC connection and load the Kamino market.const rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');
const mainMarketAddress = address('7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF');
// Load market and get CASH and SOL mints from reserves
const market = await KaminoMarket.load(rpc, mainMarketAddress, 400, PROGRAM_ID);
let cashReserve, solReserve;
for (const reserve of market!.reserves.values()) {
if (reserve.stats.symbol === 'CASH') cashReserve = reserve;
if (reserve.stats.symbol === 'SOL') solReserve = reserve;
}
if (!cashReserve || !solReserve) {
throw new Error('CASH or SOL reserve not found');
}
const cashMint = cashReserve.getLiquidityMint().toString();
const solMint = solReserve.getLiquidityMint().toString();
This example targets the SOL/CASH extra farm pair where users deposit SOL collateral and borrow CASH to earn rewards.
Get and Filter Extra Farms
Fetch the list of extra farms from the CDN and filter by SOL collateral and CASH debt.// Get extraFarms from CDN API and filter by SOL/CASH pair
const cdnRes = await fetch('https://cdn.kamino.finance/resources.json');
const allResources = (await cdnRes.json()) as Resources;
const kaminoResources = allResources['mainnet-beta'];
const extraFarmsForMarket = kaminoResources.extraFarms.filter((f) => f.market === mainMarketAddress.toString());
// Filter by collateral (SOL) and debt (CASH) mints
const solCashFarms = extraFarmsForMarket.filter((f) => f.collMint === solMint && f.debtMint === cashMint);
The CDN resources endpoint provides a list of all extra farms. We filter for farms that specifically reward SOL collateral deposits paired with CASH borrows.
Fetch All Farm States in Batch
Fetch all farm states in a single batch request for better performance.// Fetch all farm states in batch
const farmAddresses = solCashFarms.map((f) => address(f.farm));
const farmStates = await FarmState.fetchMultiple(rpc, farmAddresses);
const farms = solCashFarms.flatMap((extraFarm, index) => {
const farmState = farmStates[index];
return farmState ? [{ farm: extraFarm.farm, farmState }] : [];
});
Using FarmState.fetchMultiple fetches all farm states in a single batch request.
Calculate APY for SOL/CASH Farms
Loop through the fetched farms and calculate APY for CASH rewards.for (const { farm, farmState } of farms) {
const totalStaked = lamportsToNumberDecimal(
farmState.totalStakedAmount.toString(),
farmState.token.decimals.toNumber() || 6
);
const cashReward = farmState.rewardInfos.find(
(r) => r.token.mint.toString() === cashMint && r.rewardsAvailable.gtn(0)
);
if (cashReward && !totalStaked.isZero()) {
const currentTime = new Decimal(Date.now() / 1000);
// Calculate APY from farm rewards
const rewardPerTokenPerSecond = calculateCurrentRewardPerToken(cashReward, currentTime); // returns the raw value
const divisor = new Decimal(10)
.pow(cashReward.rewardsPerSecondDecimals.toString())
.mul(new Decimal(10).pow(cashReward.token.decimals.toString()));
const adjustedRewardPerToken = new Decimal(rewardPerTokenPerSecond).div(divisor);
const dailyRewards = adjustedRewardPerToken.mul(86400);
const apy = Decimal.pow(dailyRewards.div(totalStaked).plus(1), 365).minus(1);
console.log(`ExtraFarm: ${farm}`);
console.log(` CASH APY: ${(apy.toNumber() * 100).toFixed(2)}%\n`);
}
}
The script outputs the APY for SOL/CASH extra farms paying CASH rewards. The calculateCurrentRewardPerToken helper from farms-sdk handles the reward schedule logic. Since CASH is pegged to USD, no price lookup is required.