|
|
@@ -1,3 +1,4 @@
|
|
|
+import { dummyLogger, Logger } from "ts-log";
|
|
|
import { Wallet } from "@coral-xyz/anchor";
|
|
|
import {
|
|
|
PublicKey,
|
|
|
@@ -42,9 +43,27 @@ export async function sendTransactionsJito(
|
|
|
tx: VersionedTransaction;
|
|
|
signers?: Signer[] | undefined;
|
|
|
}[],
|
|
|
- searcherClient: SearcherClient,
|
|
|
+ searcherClients: SearcherClient | SearcherClient[],
|
|
|
wallet: Wallet,
|
|
|
+ options: {
|
|
|
+ maxRetryTimeMs?: number; // Max time to retry sending transactions
|
|
|
+ delayBetweenCyclesMs?: number; // Delay between cycles of sending transactions to all searcher clients
|
|
|
+ } = {},
|
|
|
+ logger: Logger = dummyLogger, // Optional logger to track progress of retries
|
|
|
): Promise<string> {
|
|
|
+ const clients = Array.isArray(searcherClients)
|
|
|
+ ? searcherClients
|
|
|
+ : [searcherClients];
|
|
|
+
|
|
|
+ if (clients.length === 0) {
|
|
|
+ throw new Error("No searcher clients provided");
|
|
|
+ }
|
|
|
+
|
|
|
+ const maxRetryTimeMs = options.maxRetryTimeMs || 60000; // Default to 60 seconds
|
|
|
+ const delayBetweenCyclesMs = options.delayBetweenCyclesMs || 1000; // Default to 1 second
|
|
|
+
|
|
|
+ const startTime = Date.now();
|
|
|
+
|
|
|
const signedTransactions = [];
|
|
|
|
|
|
for (const transaction of transactions) {
|
|
|
@@ -64,7 +83,56 @@ export async function sendTransactionsJito(
|
|
|
);
|
|
|
|
|
|
const bundle = new Bundle(signedTransactions, 2);
|
|
|
- await searcherClient.sendBundle(bundle);
|
|
|
|
|
|
- return firstTransactionSignature;
|
|
|
+ let lastError: Error | null = null;
|
|
|
+ let totalAttempts = 0;
|
|
|
+
|
|
|
+ while (Date.now() - startTime < maxRetryTimeMs) {
|
|
|
+ // Try all clients in this cycle
|
|
|
+ for (let i = 0; i < clients.length; i++) {
|
|
|
+ const currentClient = clients[i];
|
|
|
+ totalAttempts++;
|
|
|
+
|
|
|
+ try {
|
|
|
+ await currentClient.sendBundle(bundle);
|
|
|
+ logger.info(
|
|
|
+ { clientIndex: i, totalAttempts },
|
|
|
+ `Successfully sent bundle to Jito client after ${totalAttempts} attempts`,
|
|
|
+ );
|
|
|
+ return firstTransactionSignature;
|
|
|
+ } catch (err: any) {
|
|
|
+ lastError = err;
|
|
|
+ logger.error(
|
|
|
+ { clientIndex: i, totalAttempts, err: err.message },
|
|
|
+ `Attempt ${totalAttempts}: Error sending bundle to Jito client ${i}`,
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check if we've run out of time
|
|
|
+ if (Date.now() - startTime >= maxRetryTimeMs) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // If we've tried all clients and still have time, wait before next cycle
|
|
|
+ const timeRemaining = maxRetryTimeMs - (Date.now() - startTime);
|
|
|
+ if (timeRemaining > delayBetweenCyclesMs) {
|
|
|
+ await new Promise((resolve) => setTimeout(resolve, delayBetweenCyclesMs));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const totalTimeMs = Date.now() - startTime;
|
|
|
+ const errorMsg = `Failed to send transactions via JITO after ${totalAttempts} attempts over ${totalTimeMs}ms (max: ${maxRetryTimeMs}ms)`;
|
|
|
+
|
|
|
+ logger.error(
|
|
|
+ {
|
|
|
+ totalAttempts,
|
|
|
+ totalTimeMs,
|
|
|
+ maxRetryTimeMs,
|
|
|
+ lastError: lastError?.message,
|
|
|
+ },
|
|
|
+ errorMsg,
|
|
|
+ );
|
|
|
+
|
|
|
+ throw lastError || new Error(errorMsg);
|
|
|
}
|