UNPKG

@kamino-finance/klend-sdk

Version:

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

121 lines (101 loc) 4.72 kB
import { Address } from '@solana/kit'; import { getAssociatedTokenAddress } from './ata'; import { userMetadataPda, referrerTokenStatePda, obligationFarmStatePda } from './seeds'; import { DEFAULT_PUBLIC_KEY, isNotNullPubkey } from './pubkey'; import { VanillaObligation, MultiplyObligation, LeverageObligation, LendingObligation } from './ObligationType'; export type DeriveReserveInfo = { address: Address; mint: Address; cTokenMint: Address; farmCollateral: Address; farmDebt: Address; }; type MintPair = { coll: Address; debt: Address; obligationPda?: Address }; export type DeriveUserAccountsParams = { wallet: Address; /** KLend-specific context. Omit if tx doesn't touch KLend. */ klend?: { market: Address; programId: Address; reservesInfo: DeriveReserveInfo[]; multiplyMints?: MintPair[]; leverageMints?: MintPair[]; referrer?: Address; }; /** * Extra mints to derive ATAs for — strategy shares, vault shares, swap tokens, * or any other token the user holds. These generate user ATAs automatically. */ additionalMints?: Address[]; }; /** * Deterministically derives all user-specific accounts for a Kamino transaction. * Pass the result as `userAccounts` to POST /luts/find-minimal. * Over-deriving is safe — the API only uses userAccounts to filter uncovered addresses. */ export async function deriveUserAccounts(params: DeriveUserAccountsParams): Promise<Address[]> { const { wallet, klend, additionalMints = [] } = params; // Run additional ATAs and klend derivation in parallel const [additionalAtas, klendAddresses] = await Promise.all([ Promise.all(additionalMints.map((mint) => getAssociatedTokenAddress(mint, wallet))), klend ? deriveKlendUserAccounts(klend, wallet) : Promise.resolve([]), ]); const addresses = new Set<Address>([wallet, ...additionalAtas, ...klendAddresses]); return [...addresses]; } async function deriveKlendUserAccounts( klend: NonNullable<DeriveUserAccountsParams['klend']>, wallet: Address ): Promise<Address[]> { const { market, programId, reservesInfo, multiplyMints = [], leverageMints = [], referrer } = klend; // User metadata + vanilla obligation in parallel const [[userMetadata], vanillaObligationPda] = await Promise.all([ userMetadataPda(wallet, programId), new VanillaObligation(programId).toPda(market, wallet), ]); // Per-reserve: all derivations in a single Promise.all const reservePromises = reservesInfo.map(async (reserve) => { const promises: Promise<Address>[] = [ getAssociatedTokenAddress(reserve.mint, wallet), getAssociatedTokenAddress(reserve.cTokenMint, wallet), new LendingObligation(reserve.mint, programId).toPda(market, wallet), ]; if (isNotNullPubkey(reserve.farmCollateral)) { promises.push(obligationFarmStatePda(reserve.farmCollateral, vanillaObligationPda)); } if (isNotNullPubkey(reserve.farmDebt)) { promises.push(obligationFarmStatePda(reserve.farmDebt, vanillaObligationPda)); } if (referrer && referrer !== DEFAULT_PUBLIC_KEY) { promises.push(referrerTokenStatePda(referrer, reserve.address, programId)); } return Promise.all(promises); }); // Shared helper for multiply/leverage — same structure, different obligation type const deriveObligationWithFarms = async ( ObligationType: typeof MultiplyObligation | typeof LeverageObligation, coll: Address, debt: Address, precomputedPda?: Address ): Promise<Address[]> => { const obligationPda = precomputedPda ?? (await new ObligationType(coll, debt, programId).toPda(market, wallet)); const collReserve = reservesInfo.find((r) => r.mint === coll); const debtReserve = reservesInfo.find((r) => r.mint === debt); const farmPromises: Promise<Address>[] = []; if (collReserve && isNotNullPubkey(collReserve.farmCollateral)) { farmPromises.push(obligationFarmStatePda(collReserve.farmCollateral, obligationPda)); } if (debtReserve && isNotNullPubkey(debtReserve.farmDebt)) { farmPromises.push(obligationFarmStatePda(debtReserve.farmDebt, obligationPda)); } return [obligationPda, ...(await Promise.all(farmPromises))]; }; const multiplyPromises = multiplyMints.map(({ coll, debt, obligationPda }) => deriveObligationWithFarms(MultiplyObligation, coll, debt, obligationPda) ); const leveragePromises = leverageMints.map(({ coll, debt, obligationPda }) => deriveObligationWithFarms(LeverageObligation, coll, debt, obligationPda) ); const allResults = await Promise.all([...reservePromises, ...multiplyPromises, ...leveragePromises]); return [userMetadata, vanillaObligationPda, ...allResults.flat()]; }