@kamino-finance/klend-sdk
Version:
Typescript SDK for interacting with the Kamino Lending (klend) protocol
165 lines (143 loc) • 5.71 kB
text/typescript
import { BN } from '@coral-xyz/anchor';
import {
Farms,
FarmState,
getUserStatePDA,
UserState,
FarmConfigOption,
lamportsToCollDecimal,
scaleDownWads,
WAD,
} from '@kamino-finance/farms-sdk';
import { Connection, Keypair, PublicKey, TransactionInstruction } from '@solana/web3.js';
import Decimal from 'decimal.js/decimal';
export const FARMS_GLOBAL_CONFIG_MAINNET: PublicKey = new PublicKey('6UodrBjL2ZreDy7QdR4YV1oxqMBjVYSEyrFpctqqwGwL');
export async function getFarmStakeIxs(
connection: Connection,
user: PublicKey,
lamportsToStake: Decimal,
farmAddress: PublicKey,
fetchedFarmState?: FarmState
): Promise<TransactionInstruction[]> {
const farmState = fetchedFarmState ? fetchedFarmState : await FarmState.fetch(connection, farmAddress);
if (!farmState) {
throw new Error(`Farm state not found for ${farmAddress}`);
}
const farmClient = new Farms(connection);
const scopePricesArg = farmState.scopePrices.equals(PublicKey.default)
? farmClient.getProgramID()
: farmState!.scopePrices;
const stakeIxs: TransactionInstruction[] = [];
const userState = getUserStatePDA(farmClient.getProgramID(), farmAddress, user);
const userStateExists = await connection.getAccountInfo(userState);
if (!userStateExists) {
const createUserIx = farmClient.createNewUserIx(user, farmAddress);
stakeIxs.push(createUserIx);
}
const stakeIx = farmClient.stakeIx(user, farmAddress, lamportsToStake, farmState.token.mint, scopePricesArg);
stakeIxs.push(stakeIx);
return stakeIxs;
}
export async function getFarmUserStatePDA(
connection: Connection,
user: PublicKey,
farm: PublicKey
): Promise<PublicKey> {
const farmClient = new Farms(connection);
return getUserStatePDA(farmClient.getProgramID(), farm, user);
}
export async function getFarmUnstakeIx(
connection: Connection,
user: PublicKey,
lamportsToUnstake: Decimal,
farmAddress: PublicKey,
fetchedFarmState?: FarmState
): Promise<TransactionInstruction> {
const farmState = fetchedFarmState ? fetchedFarmState : await FarmState.fetch(connection, farmAddress);
if (!farmState) {
throw new Error(`Farm state not found for ${farmAddress}`);
}
const farmClient = new Farms(connection);
const scopePricesArg = farmState.scopePrices.equals(PublicKey.default)
? farmClient.getProgramID()
: farmState!.scopePrices;
const userState = getUserStatePDA(farmClient.getProgramID(), farmAddress, user);
if (!userState) {
throw new Error(`User state not found for ${user}`);
}
const scaledLamportsToUnstake = new BN(lamportsToUnstake.floor().toString()).mul(new BN(WAD.toString()));
return farmClient.unstakeIx(user, farmAddress, scaledLamportsToUnstake.toString(), scopePricesArg);
}
// withdrawing from a farm is a 2 step operation: first we unstake the tokens from the farm, then we withdraw them
export async function getFarmWithdrawUnstakedDepositIx(
connection: Connection,
user: PublicKey,
farm: PublicKey,
stakeTokenMint: PublicKey
): Promise<TransactionInstruction> {
const farmClient = new Farms(connection);
const userState = getUserStatePDA(farmClient.getProgramID(), farm, user);
return farmClient.withdrawUnstakedDepositIx(user, userState, farm, stakeTokenMint);
}
export async function getFarmUnstakeAndWithdrawIxs(
connection: Connection,
user: PublicKey,
lamportsToUnstake: Decimal,
farmAddress: PublicKey,
fetchedFarmState?: FarmState
): Promise<UnstakeAndWithdrawFromFarmIxs> {
const farmState = fetchedFarmState ? fetchedFarmState : await 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 };
}
export async function getSetupFarmIxsWithFarm(
connection: Connection,
farmAdmin: Keypair,
farmTokenMint: PublicKey
): Promise<SetupFarmIxsWithFarm> {
const farmClient = new Farms(connection);
const farm = new Keypair();
const ixs = await farmClient.createFarmIx(farmAdmin.publicKey, farm, FARMS_GLOBAL_CONFIG_MAINNET, farmTokenMint);
return { farm, setupFarmIxs: ixs };
}
// returns tokens, not lamports
export async function getUserSharesInFarm(
connection: Connection,
user: PublicKey,
farm: PublicKey,
farmTokenDecimals: number
): Promise<Decimal> {
const farmClient = new Farms(connection);
const userStatePDA = getUserStatePDA(farmClient.getProgramID(), farm, user);
// if the user state does not exist, return 0
const userState = await connection.getAccountInfo(userStatePDA);
if (!userState) {
return new Decimal(0);
}
// if the user state exists, return the user shares
return farmClient.getUserTokensInUndelegatedFarm(user, farm, farmTokenDecimals);
}
export async function setVaultIdForFarmIx(
connection: Connection,
farmAdmin: PublicKey,
farm: PublicKey,
vault: PublicKey
): Promise<TransactionInstruction> {
const farmClient = new Farms(connection);
return farmClient.updateFarmConfigIx(farmAdmin, farm, PublicKey.default, new FarmConfigOption.UpdateVaultId(), vault);
}
export function getSharesInFarmUserPosition(userState: UserState, tokenDecimals: number): Decimal {
return lamportsToCollDecimal(new Decimal(scaleDownWads(userState.activeStakeScaled)), tokenDecimals);
}
export type SetupFarmIxsWithFarm = {
farm: Keypair;
setupFarmIxs: TransactionInstruction[];
};
export type UnstakeAndWithdrawFromFarmIxs = {
unstakeIx: TransactionInstruction;
withdrawIx: TransactionInstruction;
};