From ba8d65f4017f6098d5aa86b5e83c9a39a4f40917 Mon Sep 17 00:00:00 2001 From: Steven Luscher Date: Sun, 6 Oct 2024 21:13:24 +0000 Subject: [PATCH] Start exactly _one_ transaction confirmer, and a separate send retry loop on the side --- ping-thing-client.mjs | 60 +++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/ping-thing-client.mjs b/ping-thing-client.mjs index 9e4a107..1752706 100644 --- a/ping-thing-client.mjs +++ b/ping-thing-client.mjs @@ -1,4 +1,3 @@ -import { safeRace } from "@solana/promises"; import { createTransactionMessage, pipe, @@ -12,18 +11,20 @@ import { isSolanaError, getSignatureFromTransaction, sendAndConfirmTransactionFactory, + sendTransactionWithoutConfirmingFactory, // Address, } from "@solana/web3.js"; import dotenv from "dotenv"; import bs58 from "bs58"; import { getSetComputeUnitLimitInstruction } from "@solana-program/compute-budget"; import { getTransferSolInstruction } from "@solana-program/system"; -import { sleep, timeout } from "./utils/misc.mjs"; +import { sleep } from "./utils/misc.mjs"; import { getLatestBlockhash } from "./utils/blockhash.mjs"; import { rpc, rpcSubscriptions } from "./utils/rpc.mjs"; import { getNextSlot } from "./utils/slot.mjs"; import { setMaxListeners } from "events"; import axios from "axios"; +import { createRecentSignatureConfirmationPromiseFactory } from "@solana/transaction-confirmation"; dotenv.config(); @@ -58,10 +59,11 @@ const TX_RETRY_INTERVAL = 2000; setMaxListeners(100); -const mSendAndConfirmTransaction = sendAndConfirmTransactionFactory({ +const mConfirmRecentSignature = createRecentSignatureConfirmationPromiseFactory({ rpc, rpcSubscriptions, }); +const mSendTransactionWithoutConfirming = sendTransactionWithoutConfirmingFactory({ rpc }); async function pingThing() { USER_KEYPAIR = await createKeyPairFromBytes( @@ -116,35 +118,37 @@ async function pingThing() { console.log(`Sending ${signature}`); - while (true) { - try { - slotSent = await getNextSlot(); - - txStart = Date.now(); - - await safeRace([ - mSendAndConfirmTransaction(transactionSignedWithFeePayer, { - commitment: "confirmed", - maxRetries: 0n, - skipPreflight: true, - }), - timeout(TX_RETRY_INTERVAL * txSendAttempts), - ]); - - console.log(`Confirmed tx ${signature}`); - - break; - } catch (e) { - if (e.message === "Timeout") { - console.log( - `Tx not confirmed after ${TX_RETRY_INTERVAL * txSendAttempts++ - }ms, resending` - ); + let sendAbortController; + function sendTransaction() { + sendAbortController = new AbortController() + mSendTransactionWithoutConfirming(transactionSignedWithFeePayer, { + abortSignal: sendAbortController.signal, + commitment: COMMITMENT_LEVEL, + maxRetries: 0n, + skipPreflight: true, + }).catch(e => { + if (e instanceof Error && e.name === 'AbortError') { + return; } else { throw e; } - } + }); } + const sendRetryInterval = setInterval(() => { + sendAbortController.abort(); + console.log(`Tx not confirmed after ${TX_RETRY_INTERVAL * txSendAttempts++}ms, resending`); + sendTransaction(); + }, TX_RETRY_INTERVAL); + slotSent = await getNextSlot(); + txStart = Date.now(); + sendTransaction(); + await mConfirmRecentSignature({ + abortSignal: AbortSignal.any([]), + commitment: COMMITMENT_LEVEL, + signature, + }); + clearInterval(sendRetryInterval); + console.log(`Confirmed tx ${signature}`); } catch (e) { // Log and loop if we get a bad blockhash. if (isSolanaError(e, SOLANA_ERROR__TRANSACTION_ERROR__BLOCKHASH_NOT_FOUND)) {