@kamino-finance/klend-sdk
Version:
Typescript SDK for interacting with the Kamino Lending (klend) protocol
168 lines • 9.28 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FARMS_ADMIN_MAINNET = exports.FARMS_GLOBAL_CONFIG_MAINNET = void 0;
exports.getFarmStakeIxs = getFarmStakeIxs;
exports.getFarmUserStatePDA = getFarmUserStatePDA;
exports.getFarmUnstakeIx = getFarmUnstakeIx;
exports.getFarmWithdrawUnstakedDepositIx = getFarmWithdrawUnstakedDepositIx;
exports.getFarmUnstakeAndWithdrawIxs = getFarmUnstakeAndWithdrawIxs;
exports.getSetupFarmIxsWithFarm = getSetupFarmIxsWithFarm;
exports.getUserSharesInTokensStakedInFarm = getUserSharesInTokensStakedInFarm;
exports.setVaultIdForFarmIx = setVaultIdForFarmIx;
exports.getSharesInFarmUserPosition = getSharesInFarmUserPosition;
exports.getRewardPerTimeUnitSecond = getRewardPerTimeUnitSecond;
exports.getUserPendingRewardsInFarm = getUserPendingRewardsInFarm;
const farms_sdk_1 = require("@kamino-finance/farms-sdk");
const kit_1 = require("@solana/kit");
const decimal_1 = __importDefault(require("decimal.js/decimal"));
const utils_1 = require("../utils");
const option_1 = require("@kamino-finance/farms-sdk/dist/utils/option");
exports.FARMS_GLOBAL_CONFIG_MAINNET = (0, kit_1.address)('6UodrBjL2ZreDy7QdR4YV1oxqMBjVYSEyrFpctqqwGwL');
exports.FARMS_ADMIN_MAINNET = (0, kit_1.address)('BbM3mbcLsa3QcYEVx8iovwfKaA1iZ6DK5fEbbtHwS3N8');
async function getFarmStakeIxs(rpc, user, lamportsToStake, farmAddress, fetchedFarmState) {
const farmState = fetchedFarmState ? fetchedFarmState : await farms_sdk_1.FarmState.fetch(rpc, farmAddress);
if (!farmState) {
throw new Error(`Farm state not found for ${farmAddress}`);
}
const farmClient = new farms_sdk_1.Farms(rpc);
const scopePricesArg = (0, option_1.getScopePricesFromFarm)(farmState);
const stakeIxs = [];
const userState = await (0, farms_sdk_1.getUserStatePDA)(farmClient.getProgramID(), farmAddress, user.address);
const userStateExists = await (0, kit_1.fetchEncodedAccount)(rpc, userState);
if (!userStateExists.exists) {
const createUserIx = await farmClient.createNewUserIx(user, farmAddress);
stakeIxs.push(createUserIx);
}
const stakeIx = await farmClient.stakeIx(user, farmAddress, lamportsToStake, farmState.token.mint, scopePricesArg);
stakeIxs.push(stakeIx);
return stakeIxs;
}
async function getFarmUserStatePDA(rpc, user, farm) {
const farmClient = new farms_sdk_1.Farms(rpc);
return (0, farms_sdk_1.getUserStatePDA)(farmClient.getProgramID(), farm, user);
}
async function getFarmUnstakeIx(rpc, user, lamportsToUnstake, farmAddress, fetchedFarmState) {
const farmState = fetchedFarmState ? fetchedFarmState : await farms_sdk_1.FarmState.fetch(rpc, farmAddress);
if (!farmState) {
throw new Error(`Farm state not found for ${farmAddress}`);
}
const farmClient = new farms_sdk_1.Farms(rpc);
const scopePricesArg = (0, option_1.getScopePricesFromFarm)(farmState);
const scaledLamportsToUnstake = lamportsToUnstake.floor().mul(farms_sdk_1.WAD);
return farmClient.unstakeIx(user, farmAddress, scaledLamportsToUnstake, scopePricesArg);
}
// withdrawing from a farm is a 2 step operation: first we unstake the tokens from the farm, then we withdraw them
async function getFarmWithdrawUnstakedDepositIx(rpc, user, farm, stakeTokenMint) {
const farmClient = new farms_sdk_1.Farms(rpc);
const userState = await (0, farms_sdk_1.getUserStatePDA)(farmClient.getProgramID(), farm, user.address);
return farmClient.withdrawUnstakedDepositIx(user, userState, farm, stakeTokenMint);
}
async function getFarmUnstakeAndWithdrawIxs(connection, user, lamportsToUnstake, farmAddress, fetchedFarmState) {
const farmState = fetchedFarmState ? fetchedFarmState : await farms_sdk_1.FarmState.fetch(connection, farmAddress);
if (!farmState) {
throw new Error(`Farm state not found for ${farmAddress}`);
}
const unstakeIx = await getFarmUnstakeIx(connection, user, lamportsToUnstake, farmAddress, farmState);
const withdrawIx = await getFarmWithdrawUnstakedDepositIx(connection, user, farmAddress, farmState.token.mint);
return { unstakeIx, withdrawIx };
}
async function getSetupFarmIxsWithFarm(connection, farmAdmin, farmTokenMint) {
const farmClient = new farms_sdk_1.Farms(connection);
const farm = await (0, kit_1.generateKeyPairSigner)();
const ixs = await farmClient.createFarmIxs(farmAdmin, farm, exports.FARMS_GLOBAL_CONFIG_MAINNET, farmTokenMint);
return { farm, setupFarmIxs: ixs };
}
/**
* Returns the number of tokens the user has staked in the farm
* @param connection - the connection to the cluster
* @param user - the user's public key
* @param farm - the farm's public key
* @param farmTokenDecimals - the decimals of the farm token
* @returns the number of tokens the user has staked in the farm
*/
async function getUserSharesInTokensStakedInFarm(rpc, user, farm, farmTokenDecimals) {
const farmClient = new farms_sdk_1.Farms(rpc);
const userStatePDA = await (0, farms_sdk_1.getUserStatePDA)(farmClient.getProgramID(), farm, user);
// if the user state does not exist, return 0
const userState = await (0, kit_1.fetchEncodedAccount)(rpc, userStatePDA);
if (!userState.exists) {
return new decimal_1.default(0);
}
// if the user state exists, return the user shares
return farmClient.getUserTokensInUndelegatedFarm(user, farm, farmTokenDecimals);
}
async function setVaultIdForFarmIx(rpc, farmAdmin, farm, vault) {
const farmClient = new farms_sdk_1.Farms(rpc);
return farmClient.updateFarmConfigIx(farmAdmin, farm, utils_1.DEFAULT_PUBLIC_KEY, new farms_sdk_1.FarmConfigOption.UpdateVaultId(), vault);
}
function getSharesInFarmUserPosition(userState, tokenDecimals) {
return (0, farms_sdk_1.lamportsToCollDecimal)(new decimal_1.default((0, farms_sdk_1.scaleDownWads)(userState.activeStakeScaled)), tokenDecimals);
}
function getRewardPerTimeUnitSecond(reward) {
const now = new decimal_1.default(new Date().getTime()).div(1000);
let rewardPerTimeUnitSecond = new decimal_1.default(0);
for (let i = 0; i < reward.rewardScheduleCurve.points.length - 1; i++) {
const { tsStart: tsStartThisPoint, rewardPerTimeUnit } = reward.rewardScheduleCurve.points[i];
const { tsStart: tsStartNextPoint } = reward.rewardScheduleCurve.points[i + 1];
const thisPeriodStart = new decimal_1.default(tsStartThisPoint.toString());
const thisPeriodEnd = new decimal_1.default(tsStartNextPoint.toString());
const rps = new decimal_1.default(rewardPerTimeUnit.toString());
if (thisPeriodStart <= now && thisPeriodEnd >= now) {
rewardPerTimeUnitSecond = rps;
break;
}
else if (thisPeriodStart > now && thisPeriodEnd > now) {
rewardPerTimeUnitSecond = rps;
break;
}
}
const rewardTokenDecimals = reward.token.decimals.toNumber();
const rewardAmountPerUnitDecimals = new decimal_1.default(10).pow(reward.rewardsPerSecondDecimals.toString());
const rewardAmountPerUnitLamports = new decimal_1.default(10).pow(rewardTokenDecimals.toString());
const rpsAdjusted = new decimal_1.default(rewardPerTimeUnitSecond.toString())
.div(rewardAmountPerUnitDecimals)
.div(rewardAmountPerUnitLamports);
return rewardPerTimeUnitSecond ? rpsAdjusted : new decimal_1.default(0);
}
/**
* reads the pending rewards for a user in a vault farm
* @param rpc - the rpc connection
* @param userStateAddress - the address of the user state (computed differently depending on farm type)
* @param farm - the address of the farm
* @returns a map of the pending rewards per token
*/
async function getUserPendingRewardsInFarm(rpc, userStateAddress, farm) {
const pendingRewardsPerToken = new Map();
const farmClient = new farms_sdk_1.Farms(rpc);
// if the user state does not exist, return 0
const userStateAccountInfo = await (0, kit_1.fetchEncodedAccount)(rpc, userStateAddress);
if (!userStateAccountInfo.exists) {
return pendingRewardsPerToken;
}
const userState = farms_sdk_1.UserState.decode(Buffer.from(userStateAccountInfo.data));
const farmState = await farms_sdk_1.FarmState.fetch(rpc, farm);
if (!farmState) {
throw new Error(`Farm state not found for ${farm}`);
}
const currentTimestamp = new decimal_1.default(new Date().getTime() / 1000);
const rawRewards = farmClient.getUserPendingRewards(userState, farmState, currentTimestamp, null);
if (!rawRewards.hasReward) {
return pendingRewardsPerToken;
}
for (let i = 0; i < rawRewards.userPendingRewardAmounts.length; i++) {
const reward = rawRewards.userPendingRewardAmounts[i];
const rewardToken = farmState.rewardInfos[i].token.mint;
const existingReward = pendingRewardsPerToken.get(rewardToken);
if (existingReward) {
pendingRewardsPerToken.set(rewardToken, existingReward.add(reward));
}
else {
pendingRewardsPerToken.set(rewardToken, reward);
}
}
return pendingRewardsPerToken;
}
//# sourceMappingURL=farm_utils.js.map