UNPKG

@kamino-finance/klend-sdk

Version:

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

170 lines (156 loc) 6.04 kB
import { Address, GetAccountInfoApi, GetSlotApi, Instruction, Rpc, Slot, TransactionSigner } from '@solana/kit'; import { fetchAddressLookupTable, findAddressLookupTablePda, getCloseLookupTableInstruction, getCreateLookupTableInstruction, getDeactivateLookupTableInstruction, getExtendLookupTableInstruction, } from '@solana-program/address-lookup-table'; import { DEFAULT_PUBLIC_KEY } from './pubkey'; import { SYSTEM_PROGRAM_ADDRESS } from '@solana-program/system'; export async function printAddressLookupTable(rpc: Rpc<GetAccountInfoApi>, lookupTablePk: Address): Promise<void> { const lookupTableAccount = (await fetchAddressLookupTable(rpc, lookupTablePk)).data; if (!lookupTableAccount) { console.error('Lookup table is not found'); } console.log(`Lookup table account, ${lookupTablePk.toString()}`); lookupTableAccount.addresses.forEach((address: Address, index: number) => { console.log(`Address: ${address.toString()} at index ${index}`); }); } export async function createLookupTableIx( connection: Rpc<GetSlotApi>, authority: TransactionSigner ): Promise<[Instruction, Address]> { const recentSlot = await connection.getSlot({ commitment: 'finalized' }).send(); return initLookupTableIx(authority, recentSlot); } export function extendLookupTableChunkIx( authority: TransactionSigner, lookupTablePk: Address, keys: Address[], payer: TransactionSigner = authority ): Instruction { return getExtendLookupTableInstruction({ authority, payer, address: lookupTablePk, addresses: keys, }); } export const extendLookupTableIxs = ( authority: TransactionSigner, table: Address, keys: Address[], payer: TransactionSigner = authority ): Instruction[] => { const chunkSize = 25; const extendLookupIxs: Instruction[] = []; for (let i = 0; i < keys.length; i += chunkSize) { const chunk = keys.slice(i, i + chunkSize); extendLookupIxs.push(extendLookupTableChunkIx(authority, table, chunk, payer)); } return extendLookupIxs; }; /** * This method returns an instruction that creates a lookup table, alongside the pubkey of the lookup table * @param authority - the owner of the lookup table * @param recentSlot - the current slot * @returns - the instruction to create the lookup table and its address */ export async function initLookupTableIx( authority: TransactionSigner, recentSlot: Slot ): Promise<[Instruction, Address]> { const address = await findAddressLookupTablePda({ authority: authority.address, recentSlot }); const createLookupTableIx = getCreateLookupTableInstruction({ authority, payer: authority, recentSlot, address, }); return [createLookupTableIx, address[0]]; } /** * This method retuns an instruction that deactivates a lookup table, which is needed to close it * @param authority - the owner of the lookup table * @param lookupTable - the lookup table to deactivate * @returns - the instruction to deactivate the lookup table */ export function deactivateLookupTableIx(authority: TransactionSigner, lookupTable: Address): Instruction { return getDeactivateLookupTableInstruction({ authority, address: lookupTable, }); } /** * This method returns an instruction that closes a lookup table. That lookup table needs to be disabled at least 500 blocks before closing it. * @param authority - the owner of the lookup table * @param lookupTable - the lookup table to close * @returns - the instruction to close the lookup table */ /// this require the LUT to be deactivated at least 500 blocks before export function closeLookupTableIx(authority: TransactionSigner, lookupTable: Address): Instruction { return getCloseLookupTableInstruction({ authority, address: lookupTable, recipient: authority.address, }); } /** * Returns the accounts in a lookup table * @param rpc * @param lookupTable - lookup table to get the accounts from * @returns - an array of accounts in the lookup table or an empty array if the lookup table does not exist */ export async function getAccountsInLut(rpc: Rpc<GetAccountInfoApi>, lookupTable: Address): Promise<Address[]> { try { const lutState = await fetchAddressLookupTable(rpc, lookupTable); return lutState.data.addresses; } catch (error) { console.error(`Error fetching accounts in lookup table ${lookupTable.toString()}`, error); return []; } } /** * This method inserts the missing keys from the provided keys into an existent lookup table * @param authority - payer wallet pubkey * @param lookupTable - lookup table to insert the keys into * @param keys - keys to insert into the lookup table * @param [accountsInLut] - the existent accounts in the lookup table. Optional. If provided, the function will not fetch the accounts in the lookup table * @returns - an array of instructions to insert the missing keys into the lookup table */ export async function insertIntoLookupTableIxs( rpc: Rpc<GetAccountInfoApi>, authority: TransactionSigner, lookupTable: Address, keys: Address[], accountsInLut?: Address[] ): Promise<Instruction[]> { let lutContentsList = accountsInLut; if (!accountsInLut) { lutContentsList = await getAccountsInLut(rpc, lookupTable); } else { lutContentsList = accountsInLut; } const lutContents = new Set<Address>(lutContentsList); const missingAccounts = keys.filter((key) => !lutContents.has(key) && key !== DEFAULT_PUBLIC_KEY); // deduplicate missing accounts and remove default accounts and convert it back to an array const missingAccountsList = [...new Set<Address>(missingAccounts)]; const chunkSize = 20; const ixs: Instruction[] = []; for (let i = 0; i < missingAccountsList.length; i += chunkSize) { const chunk = missingAccountsList.slice(i, i + chunkSize); ixs.push( getExtendLookupTableInstruction({ payer: authority, authority, address: lookupTable, systemProgram: SYSTEM_PROGRAM_ADDRESS, addresses: chunk, }) ); } return ixs; }