Skip to content

Commit 1aec7bd

Browse files
committed
Combine prepareCompute and Retries
- Added a new helper to make sending transactions only one line: sendTransactionWithRetryAndPriorityFees
1 parent 22a8361 commit 1aec7bd

File tree

2 files changed

+131
-0
lines changed

2 files changed

+131
-0
lines changed

src/lib/transaction.ts

+81
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,18 @@ export async function sendTransactionWithRetry(
207207
transaction.sign(...signers);
208208
}
209209

210+
if (transaction.recentBlockhash === undefined) {
211+
console.log("No blockhash provided. Setting recent blockhash");
212+
const { blockhash } = await connection.getLatestBlockhash(commitment);
213+
transaction.recentBlockhash = blockhash;
214+
}
215+
if (transaction.feePayer === undefined) {
216+
if (signers.length === 0) {
217+
throw new Error("No signers or fee payer provided");
218+
}
219+
transaction.feePayer = signers[0].publicKey;
220+
}
221+
210222
onStatusUpdate?.({ status: "signed" });
211223

212224
let signature: string | null = null;
@@ -624,3 +636,72 @@ function formatData(data: any): any {
624636
}
625637
return data;
626638
}
639+
640+
/**
641+
* Sends a transaction with compute unit optimization and automatic retries
642+
*
643+
* @param connection - The Solana connection object
644+
* @param transaction - The transaction to send
645+
* @param signers - Array of signers needed for the transaction
646+
* @param priorityFee - Priority fee in microLamports
647+
* @param options - Optional configuration for retry mechanism and compute units
648+
* @returns Promise that resolves to the transaction signature
649+
*
650+
* @example
651+
* ```typescript
652+
* const signature = await sendTransactionWithRetryAndPriorityFees(
653+
* connection,
654+
* transaction,
655+
* [payer],
656+
* 1000,
657+
* {
658+
* computeUnitBuffer: { multiplier: 1.1 },
659+
* onStatusUpdate: (status) => console.log(status),
660+
* }
661+
* );
662+
* ```
663+
*/
664+
export async function sendTransactionWithRetryAndPriorityFees(
665+
connection: Connection,
666+
transaction: Transaction,
667+
signers: Keypair[],
668+
priorityFee: number = 10000,
669+
options: SendTransactionOptions & {
670+
computeUnitBuffer?: ComputeUnitBuffer;
671+
} = {},
672+
): Promise<string> {
673+
const {
674+
computeUnitBuffer: userComputeBuffer, // Rename to make clear it's user provided
675+
commitment = "confirmed",
676+
...sendOptions
677+
} = options;
678+
679+
// Use user provided buffer or default to 1.1 multiplier
680+
const computeUnitBuffer = userComputeBuffer ?? { multiplier: 1.1 };
681+
682+
if (transaction.recentBlockhash === undefined) {
683+
console.log("No blockhash provided. Setting recent blockhash");
684+
const { blockhash } = await connection.getLatestBlockhash(commitment);
685+
transaction.recentBlockhash = blockhash;
686+
}
687+
if (transaction.feePayer === undefined) {
688+
if (signers.length === 0) {
689+
throw new Error("No signers or fee payer provided");
690+
}
691+
transaction.feePayer = signers[0].publicKey;
692+
}
693+
694+
await prepareTransactionWithCompute(
695+
connection,
696+
transaction,
697+
transaction.feePayer,
698+
priorityFee,
699+
computeUnitBuffer,
700+
commitment,
701+
);
702+
703+
return sendTransactionWithRetry(connection, transaction, signers, {
704+
commitment,
705+
...sendOptions,
706+
});
707+
}

tests/src/transaction.test.ts

+50
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ import {
77
SystemProgram,
88
TransactionInstruction,
99
PublicKey,
10+
ComputeBudgetProgram,
1011
} from "@solana/web3.js";
1112
import {
1213
airdropIfRequired,
1314
confirmTransaction,
1415
getSimulationComputeUnits,
1516
prepareTransactionWithCompute,
1617
sendTransactionWithRetry,
18+
sendTransactionWithRetryAndPriorityFees,
1719
} from "../../src";
1820
import { sendAndConfirmTransaction } from "@solana/web3.js";
1921
import assert from "node:assert";
@@ -192,4 +194,52 @@ describe("Transaction utilities", () => {
192194
);
193195
});
194196
});
197+
198+
test("sendTransactionWithRetryAndPriorityFees should prepare and send a transaction", async () => {
199+
const connection = new Connection(LOCALHOST);
200+
const sender = Keypair.generate();
201+
await airdropIfRequired(
202+
connection,
203+
sender.publicKey,
204+
2 * LAMPORTS_PER_SOL,
205+
1 * LAMPORTS_PER_SOL,
206+
);
207+
const recipient = Keypair.generate();
208+
209+
const transaction = new Transaction().add(
210+
SystemProgram.transfer({
211+
fromPubkey: sender.publicKey,
212+
toPubkey: recipient.publicKey,
213+
lamports: LAMPORTS_PER_SOL * 0.1,
214+
}),
215+
);
216+
217+
const statusUpdates: any[] = [];
218+
const signature = await sendTransactionWithRetryAndPriorityFees(
219+
connection,
220+
transaction,
221+
[sender],
222+
1000,
223+
{
224+
computeUnitBuffer: { multiplier: 1.1 },
225+
onStatusUpdate: (status) => {
226+
statusUpdates.push(status);
227+
console.log("status", status);
228+
},
229+
},
230+
);
231+
232+
assert.ok(signature);
233+
assert.deepEqual(
234+
statusUpdates.map((s) => s.status),
235+
["created", "signed", "sent", "confirmed"],
236+
);
237+
238+
// Verify compute budget instructions were added
239+
assert.equal(transaction.instructions.length, 3); // transfer + 2 compute budget instructions
240+
const computeInstructions = transaction.instructions.filter((ix) =>
241+
ix.programId.equals(ComputeBudgetProgram.programId),
242+
);
243+
assert.equal(computeInstructions.length, 2);
244+
});
195245
});

0 commit comments

Comments
 (0)