@kamino-finance/klend-sdk
Version:
Typescript SDK for interacting with the Kamino Lending (klend) protocol
341 lines (323 loc) • 14.1 kB
text/typescript
import { PublicKey, Connection } from "@solana/web3.js"
import BN from "bn.js" // eslint-disable-line @typescript-eslint/no-unused-vars
import * as borsh from "@coral-xyz/borsh" // eslint-disable-line @typescript-eslint/no-unused-vars
import * as types from "../types" // eslint-disable-line @typescript-eslint/no-unused-vars
import { PROGRAM_ID } from "../programId"
export interface ObligationFields {
/** Version of the struct */
tag: BN
/** Last update to collateral, liquidity, or their market values */
lastUpdate: types.LastUpdateFields
/** Lending market address */
lendingMarket: PublicKey
/** Owner authority which can borrow liquidity */
owner: PublicKey
/** Deposited collateral for the obligation, unique by deposit reserve address */
deposits: Array<types.ObligationCollateralFields>
/** Worst LTV for the collaterals backing the loan, represented as a percentage */
lowestReserveDepositLiquidationLtv: BN
/** Market value of deposits (scaled fraction) */
depositedValueSf: BN
/** Borrowed liquidity for the obligation, unique by borrow reserve address */
borrows: Array<types.ObligationLiquidityFields>
/** Risk adjusted market value of borrows/debt (sum of price * borrowed_amount * borrow_factor) (scaled fraction) */
borrowFactorAdjustedDebtValueSf: BN
/** Market value of borrows - used for max_liquidatable_borrowed_amount (scaled fraction) */
borrowedAssetsMarketValueSf: BN
/** The maximum borrow value at the weighted average loan to value ratio (scaled fraction) */
allowedBorrowValueSf: BN
/** The dangerous borrow value at the weighted average liquidation threshold (scaled fraction) */
unhealthyBorrowValueSf: BN
/** The asset tier of the deposits */
depositsAssetTiers: Array<number>
/** The asset tier of the borrows */
borrowsAssetTiers: Array<number>
/** The elevation group id the obligation opted into. */
elevationGroup: number
/** The number of deprecated reserves the obligation has a deposit */
numOfObsoleteReserves: number
/** Marked = 1 if borrows array is not empty, 0 = borrows empty */
hasDebt: number
/** Wallet address of the referrer */
referrer: PublicKey
/** Marked = 1 if borrowing disabled, 0 = borrowing enabled */
borrowingDisabled: number
reserved: Array<number>
highestBorrowFactorPct: BN
padding3: Array<BN>
}
export interface ObligationJSON {
/** Version of the struct */
tag: string
/** Last update to collateral, liquidity, or their market values */
lastUpdate: types.LastUpdateJSON
/** Lending market address */
lendingMarket: string
/** Owner authority which can borrow liquidity */
owner: string
/** Deposited collateral for the obligation, unique by deposit reserve address */
deposits: Array<types.ObligationCollateralJSON>
/** Worst LTV for the collaterals backing the loan, represented as a percentage */
lowestReserveDepositLiquidationLtv: string
/** Market value of deposits (scaled fraction) */
depositedValueSf: string
/** Borrowed liquidity for the obligation, unique by borrow reserve address */
borrows: Array<types.ObligationLiquidityJSON>
/** Risk adjusted market value of borrows/debt (sum of price * borrowed_amount * borrow_factor) (scaled fraction) */
borrowFactorAdjustedDebtValueSf: string
/** Market value of borrows - used for max_liquidatable_borrowed_amount (scaled fraction) */
borrowedAssetsMarketValueSf: string
/** The maximum borrow value at the weighted average loan to value ratio (scaled fraction) */
allowedBorrowValueSf: string
/** The dangerous borrow value at the weighted average liquidation threshold (scaled fraction) */
unhealthyBorrowValueSf: string
/** The asset tier of the deposits */
depositsAssetTiers: Array<number>
/** The asset tier of the borrows */
borrowsAssetTiers: Array<number>
/** The elevation group id the obligation opted into. */
elevationGroup: number
/** The number of deprecated reserves the obligation has a deposit */
numOfObsoleteReserves: number
/** Marked = 1 if borrows array is not empty, 0 = borrows empty */
hasDebt: number
/** Wallet address of the referrer */
referrer: string
/** Marked = 1 if borrowing disabled, 0 = borrowing enabled */
borrowingDisabled: number
reserved: Array<number>
highestBorrowFactorPct: string
padding3: Array<string>
}
/** Lending market obligation state */
export class Obligation {
/** Version of the struct */
readonly tag: BN
/** Last update to collateral, liquidity, or their market values */
readonly lastUpdate: types.LastUpdate
/** Lending market address */
readonly lendingMarket: PublicKey
/** Owner authority which can borrow liquidity */
readonly owner: PublicKey
/** Deposited collateral for the obligation, unique by deposit reserve address */
readonly deposits: Array<types.ObligationCollateral>
/** Worst LTV for the collaterals backing the loan, represented as a percentage */
readonly lowestReserveDepositLiquidationLtv: BN
/** Market value of deposits (scaled fraction) */
readonly depositedValueSf: BN
/** Borrowed liquidity for the obligation, unique by borrow reserve address */
readonly borrows: Array<types.ObligationLiquidity>
/** Risk adjusted market value of borrows/debt (sum of price * borrowed_amount * borrow_factor) (scaled fraction) */
readonly borrowFactorAdjustedDebtValueSf: BN
/** Market value of borrows - used for max_liquidatable_borrowed_amount (scaled fraction) */
readonly borrowedAssetsMarketValueSf: BN
/** The maximum borrow value at the weighted average loan to value ratio (scaled fraction) */
readonly allowedBorrowValueSf: BN
/** The dangerous borrow value at the weighted average liquidation threshold (scaled fraction) */
readonly unhealthyBorrowValueSf: BN
/** The asset tier of the deposits */
readonly depositsAssetTiers: Array<number>
/** The asset tier of the borrows */
readonly borrowsAssetTiers: Array<number>
/** The elevation group id the obligation opted into. */
readonly elevationGroup: number
/** The number of deprecated reserves the obligation has a deposit */
readonly numOfObsoleteReserves: number
/** Marked = 1 if borrows array is not empty, 0 = borrows empty */
readonly hasDebt: number
/** Wallet address of the referrer */
readonly referrer: PublicKey
/** Marked = 1 if borrowing disabled, 0 = borrowing enabled */
readonly borrowingDisabled: number
readonly reserved: Array<number>
readonly highestBorrowFactorPct: BN
readonly padding3: Array<BN>
static readonly discriminator = Buffer.from([
168, 206, 141, 106, 88, 76, 172, 167,
])
static readonly layout = borsh.struct([
borsh.u64("tag"),
types.LastUpdate.layout("lastUpdate"),
borsh.publicKey("lendingMarket"),
borsh.publicKey("owner"),
borsh.array(types.ObligationCollateral.layout(), 8, "deposits"),
borsh.u64("lowestReserveDepositLiquidationLtv"),
borsh.u128("depositedValueSf"),
borsh.array(types.ObligationLiquidity.layout(), 5, "borrows"),
borsh.u128("borrowFactorAdjustedDebtValueSf"),
borsh.u128("borrowedAssetsMarketValueSf"),
borsh.u128("allowedBorrowValueSf"),
borsh.u128("unhealthyBorrowValueSf"),
borsh.array(borsh.u8(), 8, "depositsAssetTiers"),
borsh.array(borsh.u8(), 5, "borrowsAssetTiers"),
borsh.u8("elevationGroup"),
borsh.u8("numOfObsoleteReserves"),
borsh.u8("hasDebt"),
borsh.publicKey("referrer"),
borsh.u8("borrowingDisabled"),
borsh.array(borsh.u8(), 7, "reserved"),
borsh.u64("highestBorrowFactorPct"),
borsh.array(borsh.u64(), 126, "padding3"),
])
constructor(fields: ObligationFields) {
this.tag = fields.tag
this.lastUpdate = new types.LastUpdate({ ...fields.lastUpdate })
this.lendingMarket = fields.lendingMarket
this.owner = fields.owner
this.deposits = fields.deposits.map(
(item) => new types.ObligationCollateral({ ...item })
)
this.lowestReserveDepositLiquidationLtv =
fields.lowestReserveDepositLiquidationLtv
this.depositedValueSf = fields.depositedValueSf
this.borrows = fields.borrows.map(
(item) => new types.ObligationLiquidity({ ...item })
)
this.borrowFactorAdjustedDebtValueSf =
fields.borrowFactorAdjustedDebtValueSf
this.borrowedAssetsMarketValueSf = fields.borrowedAssetsMarketValueSf
this.allowedBorrowValueSf = fields.allowedBorrowValueSf
this.unhealthyBorrowValueSf = fields.unhealthyBorrowValueSf
this.depositsAssetTiers = fields.depositsAssetTiers
this.borrowsAssetTiers = fields.borrowsAssetTiers
this.elevationGroup = fields.elevationGroup
this.numOfObsoleteReserves = fields.numOfObsoleteReserves
this.hasDebt = fields.hasDebt
this.referrer = fields.referrer
this.borrowingDisabled = fields.borrowingDisabled
this.reserved = fields.reserved
this.highestBorrowFactorPct = fields.highestBorrowFactorPct
this.padding3 = fields.padding3
}
static async fetch(
c: Connection,
address: PublicKey,
programId: PublicKey = PROGRAM_ID
): Promise<Obligation | null> {
const info = await c.getAccountInfo(address)
if (info === null) {
return null
}
if (!info.owner.equals(programId)) {
throw new Error("account doesn't belong to this program")
}
return this.decode(info.data)
}
static async fetchMultiple(
c: Connection,
addresses: PublicKey[],
programId: PublicKey = PROGRAM_ID
): Promise<Array<Obligation | null>> {
const infos = await c.getMultipleAccountsInfo(addresses)
return infos.map((info) => {
if (info === null) {
return null
}
if (!info.owner.equals(programId)) {
throw new Error("account doesn't belong to this program")
}
return this.decode(info.data)
})
}
static decode(data: Buffer): Obligation {
if (!data.slice(0, 8).equals(Obligation.discriminator)) {
throw new Error("invalid account discriminator")
}
const dec = Obligation.layout.decode(data.slice(8))
return new Obligation({
tag: dec.tag,
lastUpdate: types.LastUpdate.fromDecoded(dec.lastUpdate),
lendingMarket: dec.lendingMarket,
owner: dec.owner,
deposits: dec.deposits.map(
(
item: any /* eslint-disable-line @typescript-eslint/no-explicit-any */
) => types.ObligationCollateral.fromDecoded(item)
),
lowestReserveDepositLiquidationLtv:
dec.lowestReserveDepositLiquidationLtv,
depositedValueSf: dec.depositedValueSf,
borrows: dec.borrows.map(
(
item: any /* eslint-disable-line @typescript-eslint/no-explicit-any */
) => types.ObligationLiquidity.fromDecoded(item)
),
borrowFactorAdjustedDebtValueSf: dec.borrowFactorAdjustedDebtValueSf,
borrowedAssetsMarketValueSf: dec.borrowedAssetsMarketValueSf,
allowedBorrowValueSf: dec.allowedBorrowValueSf,
unhealthyBorrowValueSf: dec.unhealthyBorrowValueSf,
depositsAssetTiers: dec.depositsAssetTiers,
borrowsAssetTiers: dec.borrowsAssetTiers,
elevationGroup: dec.elevationGroup,
numOfObsoleteReserves: dec.numOfObsoleteReserves,
hasDebt: dec.hasDebt,
referrer: dec.referrer,
borrowingDisabled: dec.borrowingDisabled,
reserved: dec.reserved,
highestBorrowFactorPct: dec.highestBorrowFactorPct,
padding3: dec.padding3,
})
}
toJSON(): ObligationJSON {
return {
tag: this.tag.toString(),
lastUpdate: this.lastUpdate.toJSON(),
lendingMarket: this.lendingMarket.toString(),
owner: this.owner.toString(),
deposits: this.deposits.map((item) => item.toJSON()),
lowestReserveDepositLiquidationLtv:
this.lowestReserveDepositLiquidationLtv.toString(),
depositedValueSf: this.depositedValueSf.toString(),
borrows: this.borrows.map((item) => item.toJSON()),
borrowFactorAdjustedDebtValueSf:
this.borrowFactorAdjustedDebtValueSf.toString(),
borrowedAssetsMarketValueSf: this.borrowedAssetsMarketValueSf.toString(),
allowedBorrowValueSf: this.allowedBorrowValueSf.toString(),
unhealthyBorrowValueSf: this.unhealthyBorrowValueSf.toString(),
depositsAssetTiers: this.depositsAssetTiers,
borrowsAssetTiers: this.borrowsAssetTiers,
elevationGroup: this.elevationGroup,
numOfObsoleteReserves: this.numOfObsoleteReserves,
hasDebt: this.hasDebt,
referrer: this.referrer.toString(),
borrowingDisabled: this.borrowingDisabled,
reserved: this.reserved,
highestBorrowFactorPct: this.highestBorrowFactorPct.toString(),
padding3: this.padding3.map((item) => item.toString()),
}
}
static fromJSON(obj: ObligationJSON): Obligation {
return new Obligation({
tag: new BN(obj.tag),
lastUpdate: types.LastUpdate.fromJSON(obj.lastUpdate),
lendingMarket: new PublicKey(obj.lendingMarket),
owner: new PublicKey(obj.owner),
deposits: obj.deposits.map((item) =>
types.ObligationCollateral.fromJSON(item)
),
lowestReserveDepositLiquidationLtv: new BN(
obj.lowestReserveDepositLiquidationLtv
),
depositedValueSf: new BN(obj.depositedValueSf),
borrows: obj.borrows.map((item) =>
types.ObligationLiquidity.fromJSON(item)
),
borrowFactorAdjustedDebtValueSf: new BN(
obj.borrowFactorAdjustedDebtValueSf
),
borrowedAssetsMarketValueSf: new BN(obj.borrowedAssetsMarketValueSf),
allowedBorrowValueSf: new BN(obj.allowedBorrowValueSf),
unhealthyBorrowValueSf: new BN(obj.unhealthyBorrowValueSf),
depositsAssetTiers: obj.depositsAssetTiers,
borrowsAssetTiers: obj.borrowsAssetTiers,
elevationGroup: obj.elevationGroup,
numOfObsoleteReserves: obj.numOfObsoleteReserves,
hasDebt: obj.hasDebt,
referrer: new PublicKey(obj.referrer),
borrowingDisabled: obj.borrowingDisabled,
reserved: obj.reserved,
highestBorrowFactorPct: new BN(obj.highestBorrowFactorPct),
padding3: obj.padding3.map((item) => new BN(item)),
})
}
}