diff --git a/web3.js/module.d.ts b/web3.js/module.d.ts index 6b881ab769..dd4e043a38 100644 --- a/web3.js/module.d.ts +++ b/web3.js/module.d.ts @@ -48,6 +48,11 @@ declare module '@solana/web3.js' { export type SignatureStatusResult = SignatureSuccess | TransactionError; + export type SignatureStatus = { + slot: number; + status: SignatureSuccess | TransactionError; + }; + export type BlockhashAndFeeCalculator = { blockhash: Blockhash; feeCalculator: FeeCalculator; @@ -183,7 +188,11 @@ declare module '@solana/web3.js' { getSignatureStatus( signature: TransactionSignature, commitment?: Commitment, - ): Promise; + ): Promise; + getSignatureStatusBatch( + signatures: Array, + commitment?: Commitment, + ): Promise>; getTransactionCount(commitment?: Commitment): Promise; getTotalSupply(commitment?: Commitment): Promise; getVersion(): Promise; diff --git a/web3.js/module.flow.js b/web3.js/module.flow.js index 6ac0d7d863..74c0e9712f 100644 --- a/web3.js/module.flow.js +++ b/web3.js/module.flow.js @@ -63,6 +63,11 @@ declare module '@solana/web3.js' { | SignatureSuccess | TransactionError; + declare export type SignatureStatus = { + slot: number, + status: SignatureSuccess | TransactionError, + }; + declare export type BlockhashAndFeeCalculator = { blockhash: Blockhash, feeCalculator: FeeCalculator, @@ -198,7 +203,11 @@ declare module '@solana/web3.js' { getSignatureStatus( signature: TransactionSignature, commitment: ?Commitment, - ): Promise; + ): Promise; + getSignatureStatusBatch( + signatures: Array, + commitment: ?Commitment, + ): Promise>; getTransactionCount(commitment: ?Commitment): Promise; getTotalSupply(commitment: ?Commitment): Promise; getVersion(): Promise; diff --git a/web3.js/src/connection.js b/web3.js/src/connection.js index 38f31d1b8c..e28f8762ff 100644 --- a/web3.js/src/connection.js +++ b/web3.js/src/connection.js @@ -452,7 +452,15 @@ const GetVoteAccounts = jsonRpcResult( * Expected JSON RPC response for the "getSignatureStatus" message */ const GetSignatureStatusRpcResult = jsonRpcResult( - struct.union(['null', SignatureStatusResult]), + struct.array([ + struct.union([ + 'null', + struct({ + slot: 'number', + status: SignatureStatusResult, + }), + ]), + ]), ); /** @@ -660,6 +668,18 @@ export type TransactionError = {| Err: Object, |}; +/** + * Signature status + * + * @typedef {Object} SignatureStatus + * @property {number} slot when the transaction was processed + * @property {SignatureStatus | TransactionError} status + */ +export type SignatureStatus = { + slot: number, + status: SignatureSuccess | TransactionError, +}; + /** * A connection to a fullnode JSON RPC endpoint */ @@ -944,8 +964,20 @@ export class Connection { async getSignatureStatus( signature: TransactionSignature, commitment: ?Commitment, - ): Promise { - const args = this._argsWithCommitment([signature], commitment); + ): Promise { + const res = await this.getSignatureStatusBatch([signature], commitment); + assert(res.length === 1); + return res[0]; + } + + /** + * Fetch the current status of a signature + */ + async getSignatureStatusBatch( + signatures: Array, + commitment: ?Commitment, + ): Promise> { + const args = this._argsWithCommitment([signatures], commitment); const unsafeRes = await this._rpcRequest('getSignatureStatus', args); const res = GetSignatureStatusRpcResult(unsafeRes); if (res.error) { diff --git a/web3.js/src/util/send-and-confirm-raw-transaction.js b/web3.js/src/util/send-and-confirm-raw-transaction.js index e1c0c6df1a..d4c05b7dba 100644 --- a/web3.js/src/util/send-and-confirm-raw-transaction.js +++ b/web3.js/src/util/send-and-confirm-raw-transaction.js @@ -39,7 +39,7 @@ export async function sendAndConfirmRawTransaction( } } - if (status && 'Ok' in status) { + if (status && status.status && 'Ok' in status.status) { return signature; } diff --git a/web3.js/src/util/send-and-confirm-transaction.js b/web3.js/src/util/send-and-confirm-transaction.js index f3ed5a3122..4c82a1e0b2 100644 --- a/web3.js/src/util/send-and-confirm-transaction.js +++ b/web3.js/src/util/send-and-confirm-transaction.js @@ -65,7 +65,7 @@ async function _sendAndConfirmTransaction( await sleep((500 * DEFAULT_TICKS_PER_SLOT) / NUM_TICKS_PER_SECOND); } - if (status && 'Ok' in status) { + if (status && 'Ok' in status.status) { break; } if (--sendRetries <= 0) { @@ -77,7 +77,7 @@ async function _sendAndConfirmTransaction( ); } - if (status && status.Err && !('AccountInUse' in status.Err)) { + if (status && status.status.Err && !('AccountInUse' in status.status.Err)) { throw new Error( `Transaction ${signature} failed (${JSON.stringify(status)})`, ); diff --git a/web3.js/test/connection.test.js b/web3.js/test/connection.test.js index 1d46c4c55b..85d1b5cf8b 100644 --- a/web3.js/test/connection.test.js +++ b/web3.js/test/connection.test.js @@ -100,13 +100,20 @@ test('get program accounts', async () => { { method: 'getSignatureStatus', params: [ - '3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk', + [ + '3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk', + ], {commitment: 'recent'}, ], }, { error: null, - result: {Ok: null}, + result: [ + { + slot: 0, + status: {Ok: null}, + }, + ], }, ]); let transaction = SystemProgram.assign({ @@ -131,13 +138,20 @@ test('get program accounts', async () => { { method: 'getSignatureStatus', params: [ - '3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk', + [ + '3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk', + ], {commitment: 'recent'}, ], }, { error: null, - result: {Ok: null}, + result: [ + { + slot: 0, + status: {Ok: null}, + }, + ], }, ]); transaction = SystemProgram.assign({ @@ -471,7 +485,7 @@ test('confirm transaction - error', async () => { url, { method: 'getSignatureStatus', - params: [badTransactionSignature], + params: [[badTransactionSignature]], }, errorResponse, ]); @@ -1011,18 +1025,64 @@ test('transaction', async () => { { method: 'getSignatureStatus', params: [ - '3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk', + [ + '3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk', + ], {commitment: 'recent'}, ], }, { error: null, - result: {Ok: null}, + result: [ + { + slot: 0, + status: {Ok: null}, + }, + ], }, ]); - await expect(connection.getSignatureStatus(signature)).resolves.toEqual({ - Ok: null, - }); + + const response = await connection.getSignatureStatus(signature); + if (response !== null) { + expect(typeof response.slot).toEqual('number'); + expect(response.status).toEqual({Ok: null}); + } else { + expect(response).not.toBeNull(); + } + + const unprocessedSignature = + '8WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk'; + mockRpc.push([ + url, + { + method: 'getSignatureStatus', + params: [ + [ + '3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk', + unprocessedSignature, + ], + {commitment: 'recent'}, + ], + }, + { + error: null, + result: [ + { + slot: 0, + status: {Ok: null}, + }, + null, + ], + }, + ]); + + const responses = await connection.getSignatureStatusBatch([ + signature, + unprocessedSignature, + ]); + expect(responses.length).toEqual(2); + expect(responses[0]).toEqual(response); + expect(responses[1]).toBeNull(); mockRpc.push([ url, @@ -1120,9 +1180,14 @@ test('multi-instruction transaction', async () => { expect(++i).toBeLessThan(10); await sleep(500); } - await expect(connection.getSignatureStatus(signature)).resolves.toEqual({ - Ok: null, - }); + + const response = await connection.getSignatureStatus(signature); + if (response !== null) { + expect(typeof response.slot).toEqual('number'); + expect(response.status).toEqual({Ok: null}); + } else { + expect(response).not.toBeNull(); + } // accountFrom may have less than LAMPORTS_PER_SOL due to transaction fees expect(await connection.getBalance(accountFrom.publicKey)).toBeGreaterThan(0); diff --git a/web3.js/test/transaction-payer.test.js b/web3.js/test/transaction-payer.test.js index 5a383e6518..71ee5a4f13 100644 --- a/web3.js/test/transaction-payer.test.js +++ b/web3.js/test/transaction-payer.test.js @@ -148,18 +148,29 @@ test('transaction-payer', async () => { { method: 'getSignatureStatus', params: [ - '3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk', + [ + '3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk', + ], {commitment: 'recent'}, ], }, { error: null, - result: {Ok: null}, + result: [ + { + slot: 0, + status: {Ok: null}, + }, + ], }, ]); - await expect(connection.getSignatureStatus(signature)).resolves.toEqual({ - Ok: null, - }); + const response = await connection.getSignatureStatus(signature); + if (response !== null) { + expect(typeof response.slot).toEqual('number'); + expect(response.status).toEqual({Ok: null}); + } else { + expect(response).not.toBeNull(); + } mockRpc.push([ url,