UNPKG

@hubbleprotocol/farms-sdk

Version:
242 lines (217 loc) 5.89 kB
import { KaminoAction, KaminoMarket, KaminoObligation, getObligationTypeFromObligation, sleep, } from "@kamino-finance/klend-sdk"; import { AddressLookupTableAccount, BlockhashWithExpiryBlockHeight, Connection, Keypair, PublicKey, Signer, TransactionInstruction, TransactionMessage, TransactionSignature, VersionedTransaction, } from "@solana/web3.js"; import { initializeClient } from "./utils"; import { Env, getFarmsProgramId } from "../utils"; export const LENDING_LUT = new PublicKey( "284iwGtA9X9aLy3KsyV8uT2pXLARhYbiSi5SiM2g47M2", ); export async function refreshAllKlendObligationFarmsFromFileCommand( market: string, file: string, ) { const admin = process.env.ADMIN; const rpc = process.env.RPC; const klendProgramId = new PublicKey(process.env.KLEND_PROGRAM_ID || ""); const env = initializeClient(rpc!, admin!, getFarmsProgramId(rpc!), false); const c = env.provider.connection; const lendingMarket = new PublicKey(market); const kaminoMarket = await KaminoMarket.load( c, lendingMarket, 450, klendProgramId, ); if (!kaminoMarket) { throw new Error("Kamino market not found"); } const fs = require("fs"); const rawData = fs.readFileSync(file, "utf8"); const obligationAddresses: string[] = JSON.parse(rawData); let startAt = 0; let stopAt = obligationAddresses.length; let maxConcurrent = 30; let promises: any = []; let lut = (await c.getAddressLookupTable(LENDING_LUT)).value!; for (let i = startAt; i < stopAt; i++) { const obligationAddress = new PublicKey(obligationAddresses[i]); // console.log("Obligation", obligationAddress.toString()); const obligation = await kaminoMarket.getObligationByAddress(obligationAddress); const kaminoAction = buildRefreshObligationTxns( kaminoMarket, env.initialOwner.publicKey, obligationAddress, ); promises.push( executeWithoutAwait( env, obligationAddress, i, kaminoAction, lut, obligationAddresses.length, ), ); if ( promises.length >= maxConcurrent || i === obligationAddresses.length - 1 || i === stopAt - 1 ) { await Promise.all(promises); promises = []; } } await sleep(10); } const executeWithoutAwait = async ( env: Env, obligation: PublicKey, i: number, kaminoAction: Promise<KaminoAction>, lut: AddressLookupTableAccount, len: number, ): Promise<string | null> => { let numRetries = 5; let retry = 0; while (retry < numRetries) { try { const action = await kaminoAction; const sig = await sendTransactionFromAction( env.provider.connection, action, env.initialOwner, [lut], "Refreshed Obligation " + i + " " + obligation.toString() + " out of " + len, ); return sig; } catch (e) { console.log("Obligation error", i, obligation.toString(), e); retry += 1; await sleep(1000); } } return null; }; export const sendTransactionFromAction = async ( c: Connection, kaminoAction: KaminoAction, liquidator: Keypair, lookupTables: AddressLookupTableAccount[], withDescription: string = "", ): Promise<TransactionSignature> => { const ixs = [ ...kaminoAction.setupIxs, ...kaminoAction.lendingIxs, ...kaminoAction.cleanupIxs, ]; return sendAndConfirmTransactionV0( c, liquidator, ixs, lookupTables, [], withDescription, (str) => console.log(str), ); }; export async function sendAndConfirmTransactionV0( c: Connection, payer: Keypair, instructions: TransactionInstruction[], lookupTables: AddressLookupTableAccount[], signers: Signer[], withDescription: string = "", logger: (str: string) => void = console.log, ): Promise<TransactionSignature> { const blockhash = await c.getLatestBlockhash("confirmed"); const messageV0 = new TransactionMessage({ payerKey: payer.publicKey, recentBlockhash: blockhash.blockhash, instructions, }).compileToV0Message(lookupTables); const tx = new VersionedTransaction(messageV0); tx.sign([payer, ...signers]); let sig: string; try { sig = await c.sendTransaction(tx, { preflightCommitment: "processed", skipPreflight: true, }); logger(`Transaction Hash: ${withDescription} ${sig}`); } catch (e: any) { logger(e); } const status = await confirmTx(c, sig!, blockhash); return sig!; } export async function confirmTx( connection: Connection, txHash: string, txnBlockhash: BlockhashWithExpiryBlockHeight, ) { return connection.confirmTransaction( { blockhash: txnBlockhash.blockhash, lastValidBlockHeight: txnBlockhash.lastValidBlockHeight, signature: txHash, }, "confirmed", ); } async function buildRefreshObligationTxns( kaminoMarket: KaminoMarket, payer: PublicKey, obligation: PublicKey, ) { const kaminoObligation = await KaminoObligation.load( kaminoMarket, obligation, ); if (!kaminoObligation) { throw new Error(`Obligation ${obligation.toBase58()} not found`); } const firstReserve = kaminoObligation.state.deposits.find( (d) => !d.depositReserve.equals(PublicKey.default), )!.depositReserve; const firstKaminoReserve = kaminoMarket.getReserveByAddress(firstReserve); if (!firstKaminoReserve) { throw new Error(`Reserve ${firstReserve.toBase58()} not found`); } const obligationType = getObligationTypeFromObligation( kaminoMarket, kaminoObligation, ); const axn = await KaminoAction.initialize( "refreshObligation", "0", firstKaminoReserve?.getLiquidityMint(), kaminoObligation.state.owner, kaminoMarket, obligationType, kaminoMarket.programId, ); axn.addRefreshObligation(payer); return axn; }