Files
solana-agent-kit/src/utils/send_tx.ts
2024-12-30 04:53:28 +01:00

122 lines
3.9 KiB
TypeScript

import { SolanaAgentKit } from "../agent";
import { Keypair, Signer, TransactionInstruction, TransactionMessage, VersionedTransaction } from "@solana/web3.js";
import { ComputeBudgetProgram, } from "@solana/web3.js";
const feeTiers = {
min: 0.01,
mid: 0.5,
max: 0.95
}
/**
* Get priority fees for the current block
* @param connection - Solana RPC connection
* @returns Priority fees statistics and instructions for different fee levels
*/
export async function getComputeBudgetInstructions(agent: SolanaAgentKit, instructions: TransactionInstruction[], feeTier: keyof typeof feeTiers): Promise<{
blockhash: string;
computeBudgetLimitInstruction: TransactionInstruction;
computeBudgetPriorityFeeInstructions: TransactionInstruction;
} > {
try {
const blockhash = (await agent.connection.getLatestBlockhash()).blockhash;
const messageV0 = new TransactionMessage({
payerKey: agent.wallet_address,
recentBlockhash: blockhash,
instructions: instructions,
}).compileToV0Message();
const transaction = new VersionedTransaction(messageV0);
const simulatedTx = agent.connection.simulateTransaction(transaction);
const estimatedComputeUnits = (await simulatedTx).value.unitsConsumed;
const safeComputeUnits = Math.ceil(
estimatedComputeUnits ?
Math.max(estimatedComputeUnits + 100000, estimatedComputeUnits * 1.2)
: 200000
);
const computeBudgetLimitInstruction = ComputeBudgetProgram.setComputeUnitLimit({
units: safeComputeUnits,
});
const priorityFee = await agent.connection.getRecentPrioritizationFees()
.then(fees =>
fees.sort((a, b) => a.prioritizationFee - b.prioritizationFee)
[Math.floor(fees.length * feeTiers[feeTier])].prioritizationFee
);
const computeBudgetPriorityFeeInstructions = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: priorityFee,
});
return {
blockhash,
computeBudgetLimitInstruction,
computeBudgetPriorityFeeInstructions
};
} catch (error) {
throw error;
}
}
/**
* Send a transaction with priority fees
* @param agent - SolanaAgentKit instance
* @param tx - Transaction to send
* @returns Transaction ID
*/
export async function sendTx(
agent: SolanaAgentKit,
instructions: TransactionInstruction[],
otherKeypairs?: Keypair[]
) {
try {
const ixComputeBudget = await getComputeBudgetInstructions(agent, instructions, "mid");
const allInstructions = [
ixComputeBudget.computeBudgetLimitInstruction,
ixComputeBudget.computeBudgetPriorityFeeInstructions,
...instructions];
const messageV0 = new TransactionMessage({
payerKey: agent.wallet_address,
recentBlockhash: ixComputeBudget.blockhash,
instructions: allInstructions,
}).compileToV0Message();
const transaction = new VersionedTransaction(messageV0);
transaction.sign([agent.wallet, ...(otherKeypairs ?? [])] as Signer[]);
const timeoutMs = 90000;
const startTime = Date.now();
try {
while (Date.now() - startTime < timeoutMs) {
const transactionStartTime = Date.now();
const signature = await agent.connection.sendTransaction(
transaction,
{
maxRetries: 0,
skipPreflight: false,
});
const statuses = await agent.connection.getSignatureStatuses([signature]);
if (statuses.value[0]) {
if (!statuses.value[0].err) {
return signature;
} else {
throw new Error(`Transaction failed: ${statuses.value[0].err.toString()}`);
}
}
const elapsedTime = Date.now() - transactionStartTime;
const remainingTime = Math.max(0, 1000 - elapsedTime);
if (remainingTime > 0) {
await new Promise(resolve => setTimeout(resolve, remainingTime));
}
}
throw new Error("Transaction timeout");
} catch (error) {
throw error;
}
} catch (error) {
throw error;
}
}