@hubbleprotocol/farms-sdk
Version:
242 lines (217 loc) • 5.89 kB
text/typescript
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;
}