diff --git a/web3.js/module.flow.js b/web3.js/module.flow.js index 5bb0cecead..631337fbbd 100644 --- a/web3.js/module.flow.js +++ b/web3.js/module.flow.js @@ -41,7 +41,11 @@ declare module '@solana/web3.js' { userdata: Buffer, } - declare export type SignatureStatus = 'Confirmed' | 'SignatureNotFound' | 'ProgramRuntimeError' | 'GenericFailure'; + declare export type SignatureStatus = 'Confirmed' + | 'AccountInUse' + | 'SignatureNotFound' + | 'ProgramRuntimeError' + | 'GenericFailure'; declare export class Connection { constructor(endpoint: string): Connection; diff --git a/web3.js/src/connection.js b/web3.js/src/connection.js index 6a06bd5bbe..b3b50eaf7d 100644 --- a/web3.js/src/connection.js +++ b/web3.js/src/connection.js @@ -101,10 +101,11 @@ const ConfirmTransactionRpcResult = jsonRpcResult('boolean'); * Expected JSON RPC response for the "getSignatureStatus" message */ const GetSignatureStatusRpcResult = jsonRpcResult(struct.enum([ + 'AccountInUse', 'Confirmed', - 'SignatureNotFound', - 'ProgramRuntimeError', 'GenericFailure', + 'ProgramRuntimeError', + 'SignatureNotFound', ])); /** @@ -152,7 +153,11 @@ type AccountInfo = { * * @typedef {string} SignatureStatus */ -export type SignatureStatus = 'Confirmed' | 'SignatureNotFound' | 'ProgramRuntimeError' | 'GenericFailure'; +export type SignatureStatus = 'Confirmed' + | 'AccountInUse' + | 'SignatureNotFound' + | 'ProgramRuntimeError' + | 'GenericFailure'; /** * A connection to a fullnode JSON RPC endpoint diff --git a/web3.js/src/util/send-and-confirm-transaction.js b/web3.js/src/util/send-and-confirm-transaction.js index c54d873f65..998a81b8bb 100644 --- a/web3.js/src/util/send-and-confirm-transaction.js +++ b/web3.js/src/util/send-and-confirm-transaction.js @@ -15,30 +15,35 @@ export async function sendAndConfirmTransaction( transaction: Transaction, runtimeErrorOk: boolean = false ): Promise { - const start = Date.now(); - const signature = await connection.sendTransaction(from, transaction); - // Wait up to a couple seconds for a confirmation - let i = 4; + let sendRetries = 3; for (;;) { - const status = await connection.getSignatureStatus(signature); - switch (status) { - case 'Confirmed': - return; - case 'ProgramRuntimeError': - if (runtimeErrorOk) return; - //fall through - case 'GenericError': - default: - throw new Error(`Transaction ${signature} failed (${status})`); - case 'SignatureNotFound': - break; + const start = Date.now(); + const signature = await connection.sendTransaction(from, transaction); + + // Wait up to a couple seconds for a confirmation + let status = 'SignatureNotFound'; + let statusRetries = 4; + for (;;) { + status = await connection.getSignatureStatus(signature); + if (status !== 'SignatureNotFound') { + break; + } + + await sleep(500); + if (--statusRetries <= 0) { + const duration = (Date.now() - start) / 1000; + throw new Error(`Transaction '${signature}' was not confirmed in ${duration.toFixed(2)} seconds (${status})`); + } } - await sleep(500); - if (--i < 0) { - const duration = (Date.now() - start) / 1000; - throw new Error(`Transaction '${signature}' was not confirmed in ${duration.toFixed(2)} seconds (${status})`); + if ( (status === 'Confirmed') || + (status === 'ProgramRuntimeError' && runtimeErrorOk) ) { + return; + } + + if (status !== 'AccountInUse' || --sendRetries <= 0) { + throw new Error(`Transaction ${signature} failed (${status})`); } } }