UNPKG

@kamino-finance/klend-sdk

Version:

Typescript SDK for interacting with the Kamino Lending (klend) protocol

507 lines (489 loc) 23.3 kB
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 LendingMarketFields { /** Version of lending market */ version: BN /** Bump seed for derived authority address */ bumpSeed: BN /** Owner authority which can add new reserves */ lendingMarketOwner: PublicKey /** Temporary cache of the lending market owner, used in update_lending_market_owner */ lendingMarketOwnerCached: PublicKey /** * Currency market prices are quoted in * e.g. "USD" null padded (`*b"USD\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"`) or a SPL token mint pubkey */ quoteCurrency: Array<number> /** Referral fee for the lending market, as bps out of the total protocol fee */ referralFeeBps: number emergencyMode: number /** * Whether the obligations on this market should be subject to auto-deleveraging after deposit * or borrow limit is crossed. * Besides this flag, the particular reserve's flag also needs to be enabled (logical `AND`). * **NOTE:** this also affects the individual "target LTV" deleveraging. */ autodeleverageEnabled: number borrowDisabled: number /** * Refresh price from oracle only if it's older than this percentage of the price max age. * e.g. if the max age is set to 100s and this is set to 80%, the price will be refreshed if it's older than 80s. * Price is always refreshed if this set to 0. */ priceRefreshTriggerToMaxAgePct: number /** Percentage of the total borrowed value in an obligation available for liquidation */ liquidationMaxDebtCloseFactorPct: number /** Minimum acceptable unhealthy LTV before max_debt_close_factor_pct becomes 100% */ insolvencyRiskUnhealthyLtvPct: number /** Minimum liquidation value threshold triggering full liquidation for an obligation */ minFullLiquidationValueThreshold: BN /** Max allowed liquidation value in one ix call */ maxLiquidatableDebtMarketValueAtOnce: BN /** [DEPRECATED] Global maximum unhealthy borrow value allowed for any obligation */ reserved0: Array<number> /** Global maximum allowed borrow value allowed for any obligation */ globalAllowedBorrowValue: BN /** The address of the risk council, in charge of making parameter and risk decisions on behalf of the protocol */ riskCouncil: PublicKey /** [DEPRECATED] Reward points multiplier per obligation type */ reserved1: Array<number> /** Elevation groups are used to group together reserves that have the same risk parameters and can bump the ltv and liquidation threshold */ elevationGroups: Array<types.ElevationGroupFields> elevationGroupPadding: Array<BN> /** Min net value accepted to be found in a position after any lending action in an obligation (scaled by quote currency decimals) */ minNetValueInObligationSf: BN /** Minimum value to enforce smallest ltv priority checks on the collateral reserves on liquidation */ minValueSkipLiquidationLtvChecks: BN /** Market name, zero-padded. */ name: Array<number> /** Minimum value to enforce highest borrow factor priority checks on the debt reserves on liquidation */ minValueSkipLiquidationBfChecks: BN /** * Time (in seconds) that must pass before liquidation is allowed on an obligation that has * been individually marked for auto-deleveraging (by the risk council). */ individualAutodeleverageMarginCallPeriodSecs: BN /** * Minimum amount of deposit at creation of a reserve to prevent artificial inflation * Note: this amount cannot be recovered, the ctoken associated are never minted */ minInitialDepositAmount: BN /** Whether the obligation orders should be evaluated during liquidations. */ obligationOrderExecutionEnabled: number /** Whether the lending market is set as immutable. */ immutable: number /** * Whether new obligation orders can be created. * Note: updating or cancelling existing orders is *not* affected by this flag. */ obligationOrderCreationEnabled: number padding2: Array<number> padding1: Array<BN> } export interface LendingMarketJSON { /** Version of lending market */ version: string /** Bump seed for derived authority address */ bumpSeed: string /** Owner authority which can add new reserves */ lendingMarketOwner: string /** Temporary cache of the lending market owner, used in update_lending_market_owner */ lendingMarketOwnerCached: string /** * Currency market prices are quoted in * e.g. "USD" null padded (`*b"USD\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"`) or a SPL token mint pubkey */ quoteCurrency: Array<number> /** Referral fee for the lending market, as bps out of the total protocol fee */ referralFeeBps: number emergencyMode: number /** * Whether the obligations on this market should be subject to auto-deleveraging after deposit * or borrow limit is crossed. * Besides this flag, the particular reserve's flag also needs to be enabled (logical `AND`). * **NOTE:** this also affects the individual "target LTV" deleveraging. */ autodeleverageEnabled: number borrowDisabled: number /** * Refresh price from oracle only if it's older than this percentage of the price max age. * e.g. if the max age is set to 100s and this is set to 80%, the price will be refreshed if it's older than 80s. * Price is always refreshed if this set to 0. */ priceRefreshTriggerToMaxAgePct: number /** Percentage of the total borrowed value in an obligation available for liquidation */ liquidationMaxDebtCloseFactorPct: number /** Minimum acceptable unhealthy LTV before max_debt_close_factor_pct becomes 100% */ insolvencyRiskUnhealthyLtvPct: number /** Minimum liquidation value threshold triggering full liquidation for an obligation */ minFullLiquidationValueThreshold: string /** Max allowed liquidation value in one ix call */ maxLiquidatableDebtMarketValueAtOnce: string /** [DEPRECATED] Global maximum unhealthy borrow value allowed for any obligation */ reserved0: Array<number> /** Global maximum allowed borrow value allowed for any obligation */ globalAllowedBorrowValue: string /** The address of the risk council, in charge of making parameter and risk decisions on behalf of the protocol */ riskCouncil: string /** [DEPRECATED] Reward points multiplier per obligation type */ reserved1: Array<number> /** Elevation groups are used to group together reserves that have the same risk parameters and can bump the ltv and liquidation threshold */ elevationGroups: Array<types.ElevationGroupJSON> elevationGroupPadding: Array<string> /** Min net value accepted to be found in a position after any lending action in an obligation (scaled by quote currency decimals) */ minNetValueInObligationSf: string /** Minimum value to enforce smallest ltv priority checks on the collateral reserves on liquidation */ minValueSkipLiquidationLtvChecks: string /** Market name, zero-padded. */ name: Array<number> /** Minimum value to enforce highest borrow factor priority checks on the debt reserves on liquidation */ minValueSkipLiquidationBfChecks: string /** * Time (in seconds) that must pass before liquidation is allowed on an obligation that has * been individually marked for auto-deleveraging (by the risk council). */ individualAutodeleverageMarginCallPeriodSecs: string /** * Minimum amount of deposit at creation of a reserve to prevent artificial inflation * Note: this amount cannot be recovered, the ctoken associated are never minted */ minInitialDepositAmount: string /** Whether the obligation orders should be evaluated during liquidations. */ obligationOrderExecutionEnabled: number /** Whether the lending market is set as immutable. */ immutable: number /** * Whether new obligation orders can be created. * Note: updating or cancelling existing orders is *not* affected by this flag. */ obligationOrderCreationEnabled: number padding2: Array<number> padding1: Array<string> } export class LendingMarket { /** Version of lending market */ readonly version: BN /** Bump seed for derived authority address */ readonly bumpSeed: BN /** Owner authority which can add new reserves */ readonly lendingMarketOwner: PublicKey /** Temporary cache of the lending market owner, used in update_lending_market_owner */ readonly lendingMarketOwnerCached: PublicKey /** * Currency market prices are quoted in * e.g. "USD" null padded (`*b"USD\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"`) or a SPL token mint pubkey */ readonly quoteCurrency: Array<number> /** Referral fee for the lending market, as bps out of the total protocol fee */ readonly referralFeeBps: number readonly emergencyMode: number /** * Whether the obligations on this market should be subject to auto-deleveraging after deposit * or borrow limit is crossed. * Besides this flag, the particular reserve's flag also needs to be enabled (logical `AND`). * **NOTE:** this also affects the individual "target LTV" deleveraging. */ readonly autodeleverageEnabled: number readonly borrowDisabled: number /** * Refresh price from oracle only if it's older than this percentage of the price max age. * e.g. if the max age is set to 100s and this is set to 80%, the price will be refreshed if it's older than 80s. * Price is always refreshed if this set to 0. */ readonly priceRefreshTriggerToMaxAgePct: number /** Percentage of the total borrowed value in an obligation available for liquidation */ readonly liquidationMaxDebtCloseFactorPct: number /** Minimum acceptable unhealthy LTV before max_debt_close_factor_pct becomes 100% */ readonly insolvencyRiskUnhealthyLtvPct: number /** Minimum liquidation value threshold triggering full liquidation for an obligation */ readonly minFullLiquidationValueThreshold: BN /** Max allowed liquidation value in one ix call */ readonly maxLiquidatableDebtMarketValueAtOnce: BN /** [DEPRECATED] Global maximum unhealthy borrow value allowed for any obligation */ readonly reserved0: Array<number> /** Global maximum allowed borrow value allowed for any obligation */ readonly globalAllowedBorrowValue: BN /** The address of the risk council, in charge of making parameter and risk decisions on behalf of the protocol */ readonly riskCouncil: PublicKey /** [DEPRECATED] Reward points multiplier per obligation type */ readonly reserved1: Array<number> /** Elevation groups are used to group together reserves that have the same risk parameters and can bump the ltv and liquidation threshold */ readonly elevationGroups: Array<types.ElevationGroup> readonly elevationGroupPadding: Array<BN> /** Min net value accepted to be found in a position after any lending action in an obligation (scaled by quote currency decimals) */ readonly minNetValueInObligationSf: BN /** Minimum value to enforce smallest ltv priority checks on the collateral reserves on liquidation */ readonly minValueSkipLiquidationLtvChecks: BN /** Market name, zero-padded. */ readonly name: Array<number> /** Minimum value to enforce highest borrow factor priority checks on the debt reserves on liquidation */ readonly minValueSkipLiquidationBfChecks: BN /** * Time (in seconds) that must pass before liquidation is allowed on an obligation that has * been individually marked for auto-deleveraging (by the risk council). */ readonly individualAutodeleverageMarginCallPeriodSecs: BN /** * Minimum amount of deposit at creation of a reserve to prevent artificial inflation * Note: this amount cannot be recovered, the ctoken associated are never minted */ readonly minInitialDepositAmount: BN /** Whether the obligation orders should be evaluated during liquidations. */ readonly obligationOrderExecutionEnabled: number /** Whether the lending market is set as immutable. */ readonly immutable: number /** * Whether new obligation orders can be created. * Note: updating or cancelling existing orders is *not* affected by this flag. */ readonly obligationOrderCreationEnabled: number readonly padding2: Array<number> readonly padding1: Array<BN> static readonly discriminator = Buffer.from([ 246, 114, 50, 98, 72, 157, 28, 120, ]) static readonly layout = borsh.struct([ borsh.u64("version"), borsh.u64("bumpSeed"), borsh.publicKey("lendingMarketOwner"), borsh.publicKey("lendingMarketOwnerCached"), borsh.array(borsh.u8(), 32, "quoteCurrency"), borsh.u16("referralFeeBps"), borsh.u8("emergencyMode"), borsh.u8("autodeleverageEnabled"), borsh.u8("borrowDisabled"), borsh.u8("priceRefreshTriggerToMaxAgePct"), borsh.u8("liquidationMaxDebtCloseFactorPct"), borsh.u8("insolvencyRiskUnhealthyLtvPct"), borsh.u64("minFullLiquidationValueThreshold"), borsh.u64("maxLiquidatableDebtMarketValueAtOnce"), borsh.array(borsh.u8(), 8, "reserved0"), borsh.u64("globalAllowedBorrowValue"), borsh.publicKey("riskCouncil"), borsh.array(borsh.u8(), 8, "reserved1"), borsh.array(types.ElevationGroup.layout(), 32, "elevationGroups"), borsh.array(borsh.u64(), 90, "elevationGroupPadding"), borsh.u128("minNetValueInObligationSf"), borsh.u64("minValueSkipLiquidationLtvChecks"), borsh.array(borsh.u8(), 32, "name"), borsh.u64("minValueSkipLiquidationBfChecks"), borsh.u64("individualAutodeleverageMarginCallPeriodSecs"), borsh.u64("minInitialDepositAmount"), borsh.u8("obligationOrderExecutionEnabled"), borsh.u8("immutable"), borsh.u8("obligationOrderCreationEnabled"), borsh.array(borsh.u8(), 5, "padding2"), borsh.array(borsh.u64(), 169, "padding1"), ]) constructor(fields: LendingMarketFields) { this.version = fields.version this.bumpSeed = fields.bumpSeed this.lendingMarketOwner = fields.lendingMarketOwner this.lendingMarketOwnerCached = fields.lendingMarketOwnerCached this.quoteCurrency = fields.quoteCurrency this.referralFeeBps = fields.referralFeeBps this.emergencyMode = fields.emergencyMode this.autodeleverageEnabled = fields.autodeleverageEnabled this.borrowDisabled = fields.borrowDisabled this.priceRefreshTriggerToMaxAgePct = fields.priceRefreshTriggerToMaxAgePct this.liquidationMaxDebtCloseFactorPct = fields.liquidationMaxDebtCloseFactorPct this.insolvencyRiskUnhealthyLtvPct = fields.insolvencyRiskUnhealthyLtvPct this.minFullLiquidationValueThreshold = fields.minFullLiquidationValueThreshold this.maxLiquidatableDebtMarketValueAtOnce = fields.maxLiquidatableDebtMarketValueAtOnce this.reserved0 = fields.reserved0 this.globalAllowedBorrowValue = fields.globalAllowedBorrowValue this.riskCouncil = fields.riskCouncil this.reserved1 = fields.reserved1 this.elevationGroups = fields.elevationGroups.map( (item) => new types.ElevationGroup({ ...item }) ) this.elevationGroupPadding = fields.elevationGroupPadding this.minNetValueInObligationSf = fields.minNetValueInObligationSf this.minValueSkipLiquidationLtvChecks = fields.minValueSkipLiquidationLtvChecks this.name = fields.name this.minValueSkipLiquidationBfChecks = fields.minValueSkipLiquidationBfChecks this.individualAutodeleverageMarginCallPeriodSecs = fields.individualAutodeleverageMarginCallPeriodSecs this.minInitialDepositAmount = fields.minInitialDepositAmount this.obligationOrderExecutionEnabled = fields.obligationOrderExecutionEnabled this.immutable = fields.immutable this.obligationOrderCreationEnabled = fields.obligationOrderCreationEnabled this.padding2 = fields.padding2 this.padding1 = fields.padding1 } static async fetch( c: Connection, address: PublicKey, programId: PublicKey = PROGRAM_ID ): Promise<LendingMarket | 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<LendingMarket | 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): LendingMarket { if (!data.slice(0, 8).equals(LendingMarket.discriminator)) { throw new Error("invalid account discriminator") } const dec = LendingMarket.layout.decode(data.slice(8)) return new LendingMarket({ version: dec.version, bumpSeed: dec.bumpSeed, lendingMarketOwner: dec.lendingMarketOwner, lendingMarketOwnerCached: dec.lendingMarketOwnerCached, quoteCurrency: dec.quoteCurrency, referralFeeBps: dec.referralFeeBps, emergencyMode: dec.emergencyMode, autodeleverageEnabled: dec.autodeleverageEnabled, borrowDisabled: dec.borrowDisabled, priceRefreshTriggerToMaxAgePct: dec.priceRefreshTriggerToMaxAgePct, liquidationMaxDebtCloseFactorPct: dec.liquidationMaxDebtCloseFactorPct, insolvencyRiskUnhealthyLtvPct: dec.insolvencyRiskUnhealthyLtvPct, minFullLiquidationValueThreshold: dec.minFullLiquidationValueThreshold, maxLiquidatableDebtMarketValueAtOnce: dec.maxLiquidatableDebtMarketValueAtOnce, reserved0: dec.reserved0, globalAllowedBorrowValue: dec.globalAllowedBorrowValue, riskCouncil: dec.riskCouncil, reserved1: dec.reserved1, elevationGroups: dec.elevationGroups.map( ( item: any /* eslint-disable-line @typescript-eslint/no-explicit-any */ ) => types.ElevationGroup.fromDecoded(item) ), elevationGroupPadding: dec.elevationGroupPadding, minNetValueInObligationSf: dec.minNetValueInObligationSf, minValueSkipLiquidationLtvChecks: dec.minValueSkipLiquidationLtvChecks, name: dec.name, minValueSkipLiquidationBfChecks: dec.minValueSkipLiquidationBfChecks, individualAutodeleverageMarginCallPeriodSecs: dec.individualAutodeleverageMarginCallPeriodSecs, minInitialDepositAmount: dec.minInitialDepositAmount, obligationOrderExecutionEnabled: dec.obligationOrderExecutionEnabled, immutable: dec.immutable, obligationOrderCreationEnabled: dec.obligationOrderCreationEnabled, padding2: dec.padding2, padding1: dec.padding1, }) } toJSON(): LendingMarketJSON { return { version: this.version.toString(), bumpSeed: this.bumpSeed.toString(), lendingMarketOwner: this.lendingMarketOwner.toString(), lendingMarketOwnerCached: this.lendingMarketOwnerCached.toString(), quoteCurrency: this.quoteCurrency, referralFeeBps: this.referralFeeBps, emergencyMode: this.emergencyMode, autodeleverageEnabled: this.autodeleverageEnabled, borrowDisabled: this.borrowDisabled, priceRefreshTriggerToMaxAgePct: this.priceRefreshTriggerToMaxAgePct, liquidationMaxDebtCloseFactorPct: this.liquidationMaxDebtCloseFactorPct, insolvencyRiskUnhealthyLtvPct: this.insolvencyRiskUnhealthyLtvPct, minFullLiquidationValueThreshold: this.minFullLiquidationValueThreshold.toString(), maxLiquidatableDebtMarketValueAtOnce: this.maxLiquidatableDebtMarketValueAtOnce.toString(), reserved0: this.reserved0, globalAllowedBorrowValue: this.globalAllowedBorrowValue.toString(), riskCouncil: this.riskCouncil.toString(), reserved1: this.reserved1, elevationGroups: this.elevationGroups.map((item) => item.toJSON()), elevationGroupPadding: this.elevationGroupPadding.map((item) => item.toString() ), minNetValueInObligationSf: this.minNetValueInObligationSf.toString(), minValueSkipLiquidationLtvChecks: this.minValueSkipLiquidationLtvChecks.toString(), name: this.name, minValueSkipLiquidationBfChecks: this.minValueSkipLiquidationBfChecks.toString(), individualAutodeleverageMarginCallPeriodSecs: this.individualAutodeleverageMarginCallPeriodSecs.toString(), minInitialDepositAmount: this.minInitialDepositAmount.toString(), obligationOrderExecutionEnabled: this.obligationOrderExecutionEnabled, immutable: this.immutable, obligationOrderCreationEnabled: this.obligationOrderCreationEnabled, padding2: this.padding2, padding1: this.padding1.map((item) => item.toString()), } } static fromJSON(obj: LendingMarketJSON): LendingMarket { return new LendingMarket({ version: new BN(obj.version), bumpSeed: new BN(obj.bumpSeed), lendingMarketOwner: new PublicKey(obj.lendingMarketOwner), lendingMarketOwnerCached: new PublicKey(obj.lendingMarketOwnerCached), quoteCurrency: obj.quoteCurrency, referralFeeBps: obj.referralFeeBps, emergencyMode: obj.emergencyMode, autodeleverageEnabled: obj.autodeleverageEnabled, borrowDisabled: obj.borrowDisabled, priceRefreshTriggerToMaxAgePct: obj.priceRefreshTriggerToMaxAgePct, liquidationMaxDebtCloseFactorPct: obj.liquidationMaxDebtCloseFactorPct, insolvencyRiskUnhealthyLtvPct: obj.insolvencyRiskUnhealthyLtvPct, minFullLiquidationValueThreshold: new BN( obj.minFullLiquidationValueThreshold ), maxLiquidatableDebtMarketValueAtOnce: new BN( obj.maxLiquidatableDebtMarketValueAtOnce ), reserved0: obj.reserved0, globalAllowedBorrowValue: new BN(obj.globalAllowedBorrowValue), riskCouncil: new PublicKey(obj.riskCouncil), reserved1: obj.reserved1, elevationGroups: obj.elevationGroups.map((item) => types.ElevationGroup.fromJSON(item) ), elevationGroupPadding: obj.elevationGroupPadding.map( (item) => new BN(item) ), minNetValueInObligationSf: new BN(obj.minNetValueInObligationSf), minValueSkipLiquidationLtvChecks: new BN( obj.minValueSkipLiquidationLtvChecks ), name: obj.name, minValueSkipLiquidationBfChecks: new BN( obj.minValueSkipLiquidationBfChecks ), individualAutodeleverageMarginCallPeriodSecs: new BN( obj.individualAutodeleverageMarginCallPeriodSecs ), minInitialDepositAmount: new BN(obj.minInitialDepositAmount), obligationOrderExecutionEnabled: obj.obligationOrderExecutionEnabled, immutable: obj.immutable, obligationOrderCreationEnabled: obj.obligationOrderCreationEnabled, padding2: obj.padding2, padding1: obj.padding1.map((item) => new BN(item)), }) } }