diff --git a/signers/signer-solana/readme.md b/signers/signer-solana/readme.md index 5051a6b0f..f7d01135a 100644 --- a/signers/signer-solana/readme.md +++ b/signers/signer-solana/readme.md @@ -47,13 +47,9 @@ Currecntly all Rango Solana transactions are Versioned (and serialized), only So 5. Send and confirm the transaction (similar to [jupiter suggested code](https://github.com/jup-ag/jupiter-quote-api-node/blob/main/example/utils/transactionSender.ts)) - ````ts + ```ts const { txId, txResponse } = await transactionSenderAndConfirmationWaiter({ - connection, - serializedTransaction, - blockhashWithExpiryBlockHeight: { - blockhash: latestBlock.blockhash, - lastValidBlockHeight: latestBlock.lastValidBlockHeight, - }, - }); ``` - ```` + connection, + serializedTransaction, + }); + ``` diff --git a/signers/signer-solana/src/utils/main.ts b/signers/signer-solana/src/utils/main.ts index ac3e979b0..14e893a1b 100644 --- a/signers/signer-solana/src/utils/main.ts +++ b/signers/signer-solana/src/utils/main.ts @@ -20,19 +20,14 @@ export const generalSolanaTransactionExecutor = async ( const latestBlock = await connection.getLatestBlockhash('confirmed'); const finalTx = prepareTransaction(tx, latestBlock.blockhash); - const raw = await DefaultSolanaSigner(finalTx); + const serializedTransaction = await DefaultSolanaSigner(finalTx); // We first simulate whether the transaction would be successful await simulateTransaction(finalTx, tx.txType); - const serializedTransaction = Buffer.from(raw); const { txId, txResponse } = await transactionSenderAndConfirmationWaiter({ connection, serializedTransaction, - blockhashWithExpiryBlockHeight: { - blockhash: latestBlock.blockhash, - lastValidBlockHeight: latestBlock.lastValidBlockHeight, - }, }); if (!txId || !txResponse) { throw new SignerError( diff --git a/signers/signer-solana/src/utils/send.ts b/signers/signer-solana/src/utils/send.ts index a6f3a92e3..ba7244dcf 100644 --- a/signers/signer-solana/src/utils/send.ts +++ b/signers/signer-solana/src/utils/send.ts @@ -5,6 +5,7 @@ import type { import { TransactionExpiredBlockheightExceededError } from '@solana/web3.js'; import promiseRetry from 'promise-retry'; +import { SignerError, SignerErrorCode } from 'rango-types'; import { wait } from './helpers.js'; @@ -12,12 +13,12 @@ const SEND_OPTIONS = { skipPreflight: true, }; const TIME_OUT = 2_000; +const CONFIRMATION_TIME_OUT = 30_000; // https://github.com/jup-ag/jupiter-quote-api-node/blob/main/example/utils/transactionSender.ts export async function transactionSenderAndConfirmationWaiter({ connection, serializedTransaction, - blockhashWithExpiryBlockHeight, }: TransactionSenderAndConfirmationWaiterArgs): Promise { const txId = await connection.sendRawTransaction( serializedTransaction, @@ -46,32 +47,53 @@ export async function transactionSenderAndConfirmationWaiter({ }; try { - const BLOCK_HEIGHT_DIFF = 150; void abortableResender(); - const lastValidBlockHeight = - blockhashWithExpiryBlockHeight.lastValidBlockHeight - BLOCK_HEIGHT_DIFF; // this would throw TransactionExpiredBlockheightExceededError await Promise.race([ - connection.confirmTransaction( - { - ...blockhashWithExpiryBlockHeight, - lastValidBlockHeight, - signature: txId, - abortSignal, - }, - 'confirmed' + new Promise((_, reject) => + setTimeout(() => { + if (!abortSignal.aborted) { + reject( + new SignerError( + SignerErrorCode.SEND_TX_ERROR, + undefined, + `Error confirming transaction (timeout)` + ) + ); + } + }, CONFIRMATION_TIME_OUT) ), // eslint-disable-next-line no-async-promise-executor - new Promise(async (resolve) => { + new Promise(async (resolve, reject) => { // in case ws socket died while (!abortSignal.aborted) { await wait(TIME_OUT); - const tx = await connection.getSignatureStatus(txId, { - searchTransactionHistory: false, - }); - if (tx?.value?.confirmationStatus === 'confirmed') { - resolve(tx); + const { value: statuses } = await connection.getSignatureStatuses( + [txId], + { + searchTransactionHistory: false, + } + ); + if (statuses?.length > 0) { + const status = statuses[0]; + if (status) { + if (status.err) { + reject( + new SignerError( + SignerErrorCode.SEND_TX_ERROR, + undefined, + `Transaction failed: ${JSON.stringify(status.err)}` + ) + ); + } + if ( + status.confirmationStatus && + ['confirmed', 'finalized'].includes(status.confirmationStatus) + ) { + resolve(true); + } + } } } }), @@ -81,7 +103,6 @@ export async function transactionSenderAndConfirmationWaiter({ // we consume this error and getTransaction would return null return { txId, txResponse: null }; } - // invalid state from web3.js throw e; } finally { controller.abort(); diff --git a/signers/signer-solana/src/utils/types.ts b/signers/signer-solana/src/utils/types.ts index 097a01a63..1cc9b4adb 100644 --- a/signers/signer-solana/src/utils/types.ts +++ b/signers/signer-solana/src/utils/types.ts @@ -1,5 +1,4 @@ import type { - BlockhashWithExpiryBlockHeight, Connection, PublicKey, SendOptions, @@ -39,8 +38,7 @@ export interface SolanaExternalProvider { export type TransactionSenderAndConfirmationWaiterArgs = { connection: Connection; - serializedTransaction: Buffer; - blockhashWithExpiryBlockHeight: BlockhashWithExpiryBlockHeight; + serializedTransaction: SerializedTransaction; }; export type TransactionSenderAndConfirmationWaiterResponse = {