feat: update transaction confirming apis
This commit is contained in:
		
				
					committed by
					
						 Michael Vines
						Michael Vines
					
				
			
			
				
	
			
			
			
						parent
						
							3b71ec1ff6
						
					
				
				
					commit
					839e93480c
				
			
							
								
								
									
										21
									
								
								web3.js/module.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								web3.js/module.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -210,14 +210,10 @@ declare module '@solana/web3.js' { | ||||
|       endSlot: number, | ||||
|     ): Promise<Array<TransactionSignature>>; | ||||
|     getVoteAccounts(commitment?: Commitment): Promise<VoteAccountStatus>; | ||||
|     confirmTransactionAndContext( | ||||
|       signature: TransactionSignature, | ||||
|       commitment?: Commitment, | ||||
|     ): Promise<RpcResponseAndContext<boolean>>; | ||||
|     confirmTransaction( | ||||
|       signature: TransactionSignature, | ||||
|       commitment?: Commitment, | ||||
|     ): Promise<boolean>; | ||||
|       confirmations?: number, | ||||
|     ): Promise<RpcResponseAndContext<SignatureStatus | null>>; | ||||
|     getSlot(commitment?: Commitment): Promise<number>; | ||||
|     getSlotLeader(commitment?: Commitment): Promise<string>; | ||||
|     getSignatureStatus( | ||||
| @@ -247,7 +243,7 @@ declare module '@solana/web3.js' { | ||||
|     ): Promise<TransactionSignature>; | ||||
|     sendTransaction( | ||||
|       transaction: Transaction, | ||||
|       ...signers: Array<Account> | ||||
|       signers: Array<Account>, | ||||
|     ): Promise<TransactionSignature>; | ||||
|     sendEncodedTransaction( | ||||
|       encodedTransaction: string, | ||||
| @@ -769,20 +765,15 @@ declare module '@solana/web3.js' { | ||||
|   export function sendAndConfirmTransaction( | ||||
|     connection: Connection, | ||||
|     transaction: Transaction, | ||||
|     ...signers: Array<Account> | ||||
|   ): Promise<TransactionSignature>; | ||||
|  | ||||
|   export function sendAndConfirmRecentTransaction( | ||||
|     connection: Connection, | ||||
|     transaction: Transaction, | ||||
|     ...signers: Array<Account> | ||||
|     signers: Array<Account>, | ||||
|     confirmations?: number, | ||||
|   ): Promise<TransactionSignature>; | ||||
|  | ||||
|   // === src/util/send-and-confirm-raw-transaction.js === | ||||
|   export function sendAndConfirmRawTransaction( | ||||
|     connection: Connection, | ||||
|     wireTransaction: Buffer, | ||||
|     commitment?: Commitment, | ||||
|     confirmations?: number, | ||||
|   ): Promise<TransactionSignature>; | ||||
|  | ||||
|   // === src/util/cluster.js === | ||||
|   | ||||
| @@ -223,14 +223,10 @@ declare module '@solana/web3.js' { | ||||
|       endSlot: number, | ||||
|     ): Promise<Array<TransactionSignature>>; | ||||
|     getVoteAccounts(commitment: ?Commitment): Promise<VoteAccountStatus>; | ||||
|     confirmTransactionAndContext( | ||||
|       signature: TransactionSignature, | ||||
|       commitment: ?Commitment, | ||||
|     ): Promise<RpcResponseAndContext<boolean>>; | ||||
|     confirmTransaction( | ||||
|       signature: TransactionSignature, | ||||
|       commitment: ?Commitment, | ||||
|     ): Promise<boolean>; | ||||
|       confirmations: ?number, | ||||
|     ): Promise<RpcResponseAndContext<SignatureStatus | null>>; | ||||
|     getSlot(commitment: ?Commitment): Promise<number>; | ||||
|     getSlotLeader(commitment: ?Commitment): Promise<string>; | ||||
|     getSignatureStatus( | ||||
| @@ -260,7 +256,7 @@ declare module '@solana/web3.js' { | ||||
|     ): Promise<TransactionSignature>; | ||||
|     sendTransaction( | ||||
|       transaction: Transaction, | ||||
|       ...signers: Array<Account> | ||||
|       signers: Array<Account>, | ||||
|     ): Promise<TransactionSignature>; | ||||
|     sendEncodedTransaction( | ||||
|       encodedTransaction: string, | ||||
| @@ -784,20 +780,15 @@ declare module '@solana/web3.js' { | ||||
|   declare export function sendAndConfirmTransaction( | ||||
|     connection: Connection, | ||||
|     transaction: Transaction, | ||||
|     ...signers: Array<Account> | ||||
|   ): Promise<TransactionSignature>; | ||||
|  | ||||
|   declare export function sendAndConfirmRecentTransaction( | ||||
|     connection: Connection, | ||||
|     transaction: Transaction, | ||||
|     ...signers: Array<Account> | ||||
|     signers: Array<Account>, | ||||
|     confirmations: ?number, | ||||
|   ): Promise<TransactionSignature>; | ||||
|  | ||||
|   // === src/util/send-and-confirm-raw-transaction.js === | ||||
|   declare export function sendAndConfirmRawTransaction( | ||||
|     connection: Connection, | ||||
|     wireTransaction: Buffer, | ||||
|     commitment: ?Commitment, | ||||
|     confirmations: ?number, | ||||
|   ): Promise<TransactionSignature>; | ||||
|  | ||||
|   // === src/util/cluster.js === | ||||
|   | ||||
| @@ -469,13 +469,6 @@ const GetProgramAccountsRpcResult = jsonRpcResult( | ||||
|   struct.array([ProgramAccountInfoResult]), | ||||
| ); | ||||
|  | ||||
| /** | ||||
|  * Expected JSON RPC response for the "confirmTransaction" message | ||||
|  */ | ||||
| const ConfirmTransactionAndContextRpcResult = jsonRpcResultAndContext( | ||||
|   'boolean', | ||||
| ); | ||||
|  | ||||
| /** | ||||
|  * Expected JSON RPC response for the "getSlot" message | ||||
|  */ | ||||
| @@ -1063,35 +1056,44 @@ export class Connection { | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Confirm the transaction identified by the specified signature, return with context | ||||
|    */ | ||||
|   async confirmTransactionAndContext( | ||||
|     signature: TransactionSignature, | ||||
|     commitment: ?Commitment, | ||||
|   ): Promise<RpcResponseAndContext<boolean>> { | ||||
|     const args = this._argsWithCommitment([signature], commitment); | ||||
|     const unsafeRes = await this._rpcRequest('confirmTransaction', args); | ||||
|     const res = ConfirmTransactionAndContextRpcResult(unsafeRes); | ||||
|     if (res.error) { | ||||
|       throw new Error('failed to confirm transaction: ' + res.error.message); | ||||
|     } | ||||
|     assert(typeof res.result !== 'undefined'); | ||||
|     return res.result; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Confirm the transaction identified by the specified signature | ||||
|    */ | ||||
|   async confirmTransaction( | ||||
|     signature: TransactionSignature, | ||||
|     commitment: ?Commitment, | ||||
|   ): Promise<boolean> { | ||||
|     return await this.confirmTransactionAndContext(signature, commitment) | ||||
|       .then(x => x.value) | ||||
|       .catch(e => { | ||||
|         throw new Error('failed to confirm transaction: ' + e); | ||||
|       }); | ||||
|     confirmations: ?number, | ||||
|   ): Promise<RpcResponseAndContext<SignatureStatus | null>> { | ||||
|     const NUM_STATUS_RETRIES = 10; | ||||
|  | ||||
|     const MS_PER_SECOND = 1000; | ||||
|     const MS_PER_SLOT = | ||||
|       (DEFAULT_TICKS_PER_SLOT / NUM_TICKS_PER_SECOND) * MS_PER_SECOND; | ||||
|  | ||||
|     let statusRetries = NUM_STATUS_RETRIES; | ||||
|     let statusResponse = await this.getSignatureStatus(signature); | ||||
|     for (;;) { | ||||
|       const status = statusResponse.value; | ||||
|       if (status) { | ||||
|         // Received a status, if not an error wait for confirmation | ||||
|         statusRetries = NUM_STATUS_RETRIES; | ||||
|         if ( | ||||
|           status.err || | ||||
|           status.confirmations === null || | ||||
|           (typeof confirmations === 'number' && | ||||
|             status.confirmations >= confirmations) | ||||
|         ) { | ||||
|           break; | ||||
|         } | ||||
|       } else if (--statusRetries <= 0) { | ||||
|         break; | ||||
|       } | ||||
|  | ||||
|       // Sleep for approximately half a slot | ||||
|       await sleep(MS_PER_SLOT / 2); | ||||
|       statusResponse = await this.getSignatureStatus(signature); | ||||
|     } | ||||
|  | ||||
|     return statusResponse; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
| @@ -1474,7 +1476,7 @@ export class Connection { | ||||
|    */ | ||||
|   async sendTransaction( | ||||
|     transaction: Transaction, | ||||
|     ...signers: Array<Account> | ||||
|     signers: Array<Account>, | ||||
|   ): Promise<TransactionSignature> { | ||||
|     if (transaction.nonceInfo) { | ||||
|       transaction.sign(...signers); | ||||
|   | ||||
| @@ -30,10 +30,7 @@ export { | ||||
|   SYSVAR_REWARDS_PUBKEY, | ||||
|   SYSVAR_STAKE_HISTORY_PUBKEY, | ||||
| } from './sysvar'; | ||||
| export { | ||||
|   sendAndConfirmTransaction, | ||||
|   sendAndConfirmRecentTransaction, | ||||
| } from './util/send-and-confirm-transaction'; | ||||
| export {sendAndConfirmTransaction} from './util/send-and-confirm-transaction'; | ||||
| export {sendAndConfirmRawTransaction} from './util/send-and-confirm-raw-transaction'; | ||||
| export {clusterApiUrl} from './util/cluster'; | ||||
|  | ||||
|   | ||||
| @@ -65,7 +65,12 @@ export class Loader { | ||||
|         space: data.length, | ||||
|         programId, | ||||
|       }); | ||||
|       await sendAndConfirmTransaction(connection, transaction, payer, program); | ||||
|       await sendAndConfirmTransaction( | ||||
|         connection, | ||||
|         transaction, | ||||
|         [payer, program], | ||||
|         1, | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     const dataLayout = BufferLayout.struct([ | ||||
| @@ -102,7 +107,7 @@ export class Loader { | ||||
|         data, | ||||
|       }); | ||||
|       transactions.push( | ||||
|         sendAndConfirmTransaction(connection, transaction, payer, program), | ||||
|         sendAndConfirmTransaction(connection, transaction, [payer, program], 1), | ||||
|       ); | ||||
|  | ||||
|       // Delay ~1 tick between write transactions in an attempt to reduce AccountInUse errors | ||||
| @@ -143,7 +148,12 @@ export class Loader { | ||||
|         programId, | ||||
|         data, | ||||
|       }); | ||||
|       await sendAndConfirmTransaction(connection, transaction, payer, program); | ||||
|       await sendAndConfirmTransaction( | ||||
|         connection, | ||||
|         transaction, | ||||
|         [payer, program], | ||||
|         1, | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,54 +1,34 @@ | ||||
| // @flow | ||||
|  | ||||
| import {Connection} from '../connection'; | ||||
| import type {Commitment} from '../connection'; | ||||
| import {sleep} from './sleep'; | ||||
| import type {TransactionSignature} from '../transaction'; | ||||
| import {DEFAULT_TICKS_PER_SLOT, NUM_TICKS_PER_SECOND} from '../timing'; | ||||
|  | ||||
| /** | ||||
|  * Sign, send and confirm a raw transaction | ||||
|  * Send and confirm a raw transaction | ||||
|  */ | ||||
| export async function sendAndConfirmRawTransaction( | ||||
|   connection: Connection, | ||||
|   rawTransaction: Buffer, | ||||
|   commitment: ?Commitment, | ||||
|   confirmations: ?number, | ||||
| ): Promise<TransactionSignature> { | ||||
|   const start = Date.now(); | ||||
|   const statusCommitment = commitment || connection.commitment || 'max'; | ||||
|   let signature = await connection.sendRawTransaction(rawTransaction); | ||||
|   const signature = await connection.sendRawTransaction(rawTransaction); | ||||
|   const status = (await connection.confirmTransaction(signature, confirmations)) | ||||
|     .value; | ||||
|  | ||||
|   // Wait up to a couple slots for a confirmation | ||||
|   let status = null; | ||||
|   let statusRetries = 6; | ||||
|   for (;;) { | ||||
|     status = (await connection.getSignatureStatus(signature)).value; | ||||
|   if (status) { | ||||
|       if (statusCommitment === 'max' && status.confirmations === null) { | ||||
|         break; | ||||
|       } else if (statusCommitment === 'recent') { | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // Sleep for approximately half a slot | ||||
|     await sleep((500 * DEFAULT_TICKS_PER_SLOT) / NUM_TICKS_PER_SECOND); | ||||
|  | ||||
|     if (--statusRetries <= 0) { | ||||
|       const duration = (Date.now() - start) / 1000; | ||||
|       throw new Error( | ||||
|         `Raw Transaction '${signature}' was not confirmed in ${duration.toFixed( | ||||
|           2, | ||||
|         )} seconds (${JSON.stringify(status)})`, | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (status && !status.err) { | ||||
|     return signature; | ||||
|   } | ||||
|  | ||||
|     if (status.err) { | ||||
|       throw new Error( | ||||
|         `Raw transaction ${signature} failed (${JSON.stringify(status)})`, | ||||
|       ); | ||||
|     } | ||||
|     return signature; | ||||
|   } | ||||
|  | ||||
|   const duration = (Date.now() - start) / 1000; | ||||
|   throw new Error( | ||||
|     `Raw transaction '${signature}' was not confirmed in ${duration.toFixed( | ||||
|       2, | ||||
|     )} seconds`, | ||||
|   ); | ||||
| } | ||||
|   | ||||
| @@ -1,111 +1,52 @@ | ||||
| // @flow | ||||
|  | ||||
| import invariant from 'assert'; | ||||
|  | ||||
| import {Connection} from '../connection'; | ||||
| import type {Commitment} from '../connection'; | ||||
| import {Transaction} from '../transaction'; | ||||
| import {sleep} from './sleep'; | ||||
| import type {Account} from '../account'; | ||||
| import type {TransactionSignature} from '../transaction'; | ||||
| import {DEFAULT_TICKS_PER_SLOT, NUM_TICKS_PER_SECOND} from '../timing'; | ||||
|  | ||||
| const MS_PER_SECOND = 1000; | ||||
| const MS_PER_SLOT = | ||||
|   (DEFAULT_TICKS_PER_SLOT / NUM_TICKS_PER_SECOND) * MS_PER_SECOND; | ||||
|  | ||||
| const NUM_SEND_RETRIES = 10; | ||||
| const NUM_STATUS_RETRIES = 10; | ||||
|  | ||||
| /** | ||||
|  * Sign, send and confirm a transaction with recent commitment level | ||||
|  */ | ||||
| export async function sendAndConfirmRecentTransaction( | ||||
|   connection: Connection, | ||||
|   transaction: Transaction, | ||||
|   ...signers: Array<Account> | ||||
| ): Promise<TransactionSignature> { | ||||
|   return await _sendAndConfirmTransaction( | ||||
|     connection, | ||||
|     transaction, | ||||
|     signers, | ||||
|     'recent', | ||||
|   ); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Sign, send and confirm a transaction | ||||
|  * Sign, send and confirm a transaction. | ||||
|  * | ||||
|  * If `confirmations` count is not specified, wait for transaction to be finalized. | ||||
|  */ | ||||
| export async function sendAndConfirmTransaction( | ||||
|   connection: Connection, | ||||
|   transaction: Transaction, | ||||
|   ...signers: Array<Account> | ||||
| ): Promise<TransactionSignature> { | ||||
|   return await _sendAndConfirmTransaction(connection, transaction, signers); | ||||
| } | ||||
|  | ||||
| async function _sendAndConfirmTransaction( | ||||
|   connection: Connection, | ||||
|   transaction: Transaction, | ||||
|   signers: Array<Account>, | ||||
|   commitment: ?Commitment, | ||||
|   confirmations: ?number, | ||||
| ): Promise<TransactionSignature> { | ||||
|   const statusCommitment = commitment || connection.commitment || 'max'; | ||||
|  | ||||
|   let sendRetries = NUM_SEND_RETRIES; | ||||
|   let signature; | ||||
|  | ||||
|   for (;;) { | ||||
|   const start = Date.now(); | ||||
|     signature = await connection.sendTransaction(transaction, ...signers); | ||||
|   let sendRetries = NUM_SEND_RETRIES; | ||||
|  | ||||
|     // Wait up to a couple slots for a confirmation | ||||
|     let status = null; | ||||
|     let statusRetries = NUM_STATUS_RETRIES; | ||||
|   for (;;) { | ||||
|       status = (await connection.getSignatureStatus(signature)).value; | ||||
|       if (status) { | ||||
|         // Recieved a status, if not an error wait for confirmation | ||||
|         statusRetries = NUM_STATUS_RETRIES; | ||||
|         if ( | ||||
|           status.err || | ||||
|           status.confirmations === null || | ||||
|           (statusCommitment === 'recent' && status.confirmations >= 1) | ||||
|         ) { | ||||
|           break; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       if (--statusRetries <= 0) { | ||||
|         break; | ||||
|       } | ||||
|       // Sleep for approximately half a slot | ||||
|       await sleep(MS_PER_SLOT / 2); | ||||
|     } | ||||
|     const signature = await connection.sendTransaction(transaction, signers); | ||||
|     const status = ( | ||||
|       await connection.confirmTransaction(signature, confirmations) | ||||
|     ).value; | ||||
|  | ||||
|     if (status) { | ||||
|       if (!status.err) { | ||||
|         break; | ||||
|       } else if (!('AccountInUse' in status.err)) { | ||||
|       if (status.err) { | ||||
|         throw new Error( | ||||
|           `Transaction ${signature} failed (${JSON.stringify(status)})`, | ||||
|         ); | ||||
|       } | ||||
|       return signature; | ||||
|     } | ||||
|  | ||||
|     if (--sendRetries <= 0) { | ||||
|       const duration = (Date.now() - start) / 1000; | ||||
|       throw new Error( | ||||
|         `Transaction '${signature}' was not confirmed in ${duration.toFixed( | ||||
|           2, | ||||
|         )} seconds (${JSON.stringify(status)})`, | ||||
|       ); | ||||
|     } | ||||
|     if (--sendRetries <= 0) break; | ||||
|  | ||||
|     // Retry in 0..100ms to try to avoid another AccountInUse collision | ||||
|     await sleep(Math.random() * 100); | ||||
|   } | ||||
|  | ||||
|   invariant(signature !== undefined); | ||||
|   return signature; | ||||
|   const duration = (Date.now() - start) / 1000; | ||||
|   throw new Error( | ||||
|     `Transaction was not confirmed in ${duration.toFixed( | ||||
|       2, | ||||
|     )} seconds (${JSON.stringify(status)})`, | ||||
|   ); | ||||
| } | ||||
|   | ||||
| @@ -44,7 +44,7 @@ test('load BPF C program', async () => { | ||||
|     keys: [{pubkey: from.publicKey, isSigner: true, isWritable: true}], | ||||
|     programId: program.publicKey, | ||||
|   }); | ||||
|   await sendAndConfirmTransaction(connection, transaction, from); | ||||
|   await sendAndConfirmTransaction(connection, transaction, [from], 1); | ||||
| }); | ||||
|  | ||||
| test('load BPF Rust program', async () => { | ||||
| @@ -73,5 +73,5 @@ test('load BPF Rust program', async () => { | ||||
|     keys: [{pubkey: from.publicKey, isSigner: true, isWritable: true}], | ||||
|     programId: program.publicKey, | ||||
|   }); | ||||
|   await sendAndConfirmTransaction(connection, transaction, from); | ||||
|   await sendAndConfirmTransaction(connection, transaction, [from], 1); | ||||
| }); | ||||
|   | ||||
| @@ -14,6 +14,7 @@ import {mockRpc, mockRpcEnabled} from './__mocks__/node-fetch'; | ||||
| import {mockGetRecentBlockhash} from './mockrpc/get-recent-blockhash'; | ||||
| import {url} from './url'; | ||||
| import {sleep} from '../src/util/sleep'; | ||||
| import type {SignatureStatus, TransactionError} from '../src/connection'; | ||||
|  | ||||
| if (!mockRpcEnabled) { | ||||
|   // Testing max commitment level takes around 20s to complete | ||||
| @@ -28,6 +29,29 @@ const errorResponse = { | ||||
|   result: undefined, | ||||
| }; | ||||
|  | ||||
| const verifySignatureStatus = ( | ||||
|   status: SignatureStatus | null, | ||||
|   err?: TransactionError, | ||||
| ): SignatureStatus => { | ||||
|   if (status === null) { | ||||
|     expect(status).not.toBeNull(); | ||||
|     throw new Error(); // unreachable | ||||
|   } | ||||
|  | ||||
|   const expectedErr = err || null; | ||||
|   expect(status.err).toEqual(expectedErr); | ||||
|   expect(status.slot).toBeGreaterThanOrEqual(0); | ||||
|   if (expectedErr !== null) return status; | ||||
|  | ||||
|   const confirmations = status.confirmations; | ||||
|   if (typeof confirmations === 'number') { | ||||
|     expect(confirmations).toBeGreaterThan(0); | ||||
|   } else { | ||||
|     expect(confirmations).toBeNull(); | ||||
|   } | ||||
|   return status; | ||||
| }; | ||||
|  | ||||
| test('get account info - not found', async () => { | ||||
|   const account = new Account(); | ||||
|   const connection = new Connection(url); | ||||
| @@ -135,7 +159,7 @@ test('get program accounts', async () => { | ||||
|     accountPubkey: account0.publicKey, | ||||
|     programId: programId.publicKey, | ||||
|   }); | ||||
|   await sendAndConfirmTransaction(connection, transaction, account0); | ||||
|   await sendAndConfirmTransaction(connection, transaction, [account0], 1); | ||||
|  | ||||
|   mockRpc.push([ | ||||
|     url, | ||||
| @@ -180,7 +204,7 @@ test('get program accounts', async () => { | ||||
|     programId: programId.publicKey, | ||||
|   }); | ||||
|  | ||||
|   await sendAndConfirmTransaction(connection, transaction, account1); | ||||
|   await sendAndConfirmTransaction(connection, transaction, [account1], 1); | ||||
|  | ||||
|   mockGetRecentBlockhash('recent'); | ||||
|   const {feeCalculator} = await connection.getRecentBlockhash(); | ||||
| @@ -493,8 +517,8 @@ test('confirm transaction - error', async () => { | ||||
|   mockRpc.push([ | ||||
|     url, | ||||
|     { | ||||
|       method: 'confirmTransaction', | ||||
|       params: [badTransactionSignature], | ||||
|       method: 'getSignatureStatuses', | ||||
|       params: [[badTransactionSignature]], | ||||
|     }, | ||||
|     errorResponse, | ||||
|   ]); | ||||
| @@ -1367,15 +1391,17 @@ test('transaction failure', async () => { | ||||
|     toPubkey: account.publicKey, | ||||
|     lamports: 10, | ||||
|   }); | ||||
|   const signature = await connection.sendTransaction(transaction, account); | ||||
|   const signature = await connection.sendTransaction(transaction, [account]); | ||||
|  | ||||
|   const expectedErr = {InstructionError: [0, 'AccountBorrowFailed']}; | ||||
|   mockRpc.push([ | ||||
|     url, | ||||
|     { | ||||
|       method: 'confirmTransaction', | ||||
|       method: 'getSignatureStatuses', | ||||
|       params: [ | ||||
|         [ | ||||
|           '3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk', | ||||
|         {commitment: 'recent'}, | ||||
|         ], | ||||
|       ], | ||||
|     }, | ||||
|     { | ||||
| @@ -1384,16 +1410,23 @@ test('transaction failure', async () => { | ||||
|         context: { | ||||
|           slot: 11, | ||||
|         }, | ||||
|         value: false, | ||||
|         value: [ | ||||
|           { | ||||
|             slot: 0, | ||||
|             confirmations: 1, | ||||
|             status: {Err: expectedErr}, | ||||
|             err: expectedErr, | ||||
|           }, | ||||
|         ], | ||||
|       }, | ||||
|     }, | ||||
|   ]); | ||||
|  | ||||
|   // Wait for one confirmation | ||||
|   await sleep(1000); | ||||
|   expect(await connection.confirmTransaction(signature)).toEqual(false); | ||||
|   const confirmResult = (await connection.confirmTransaction(signature, 1)) | ||||
|     .value; | ||||
|   verifySignatureStatus(confirmResult, expectedErr); | ||||
|  | ||||
|   const expectedErr = {InstructionError: [0, 'AccountBorrowFailed']}; | ||||
|   mockRpc.push([ | ||||
|     url, | ||||
|     { | ||||
| @@ -1423,19 +1456,7 @@ test('transaction failure', async () => { | ||||
|   ]); | ||||
|  | ||||
|   const response = (await connection.getSignatureStatus(signature)).value; | ||||
|   if (response === null) { | ||||
|     expect(response).not.toBeNull(); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   expect(response.err).toEqual(expectedErr); | ||||
|   expect(response.slot).toBeGreaterThanOrEqual(0); | ||||
|   const responseConfirmations = response.confirmations; | ||||
|   if (typeof responseConfirmations === 'number') { | ||||
|     expect(responseConfirmations).toBeGreaterThan(0); | ||||
|   } else { | ||||
|     expect(responseConfirmations).toBeNull(); | ||||
|   } | ||||
|   verifySignatureStatus(response, expectedErr); | ||||
| }); | ||||
|  | ||||
| test('transaction', async () => { | ||||
| @@ -1555,15 +1576,18 @@ test('transaction', async () => { | ||||
|     toPubkey: accountTo.publicKey, | ||||
|     lamports: 10, | ||||
|   }); | ||||
|   const signature = await connection.sendTransaction(transaction, accountFrom); | ||||
|   const signature = await connection.sendTransaction(transaction, [ | ||||
|     accountFrom, | ||||
|   ]); | ||||
|  | ||||
|   mockRpc.push([ | ||||
|     url, | ||||
|     { | ||||
|       method: 'confirmTransaction', | ||||
|       method: 'getSignatureStatuses', | ||||
|       params: [ | ||||
|         [ | ||||
|           '3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk', | ||||
|         {commitment: 'recent'}, | ||||
|         ], | ||||
|       ], | ||||
|     }, | ||||
|     { | ||||
| @@ -1572,24 +1596,22 @@ test('transaction', async () => { | ||||
|         context: { | ||||
|           slot: 11, | ||||
|         }, | ||||
|         value: true, | ||||
|         value: [ | ||||
|           { | ||||
|             slot: 0, | ||||
|             confirmations: 1, | ||||
|             status: {Ok: null}, | ||||
|             err: null, | ||||
|           }, | ||||
|         ], | ||||
|       }, | ||||
|     }, | ||||
|   ]); | ||||
|  | ||||
|   // Wait for one confirmation | ||||
|   await sleep(1000); | ||||
|  | ||||
|   let i = 0; | ||||
|   for (;;) { | ||||
|     if (await connection.confirmTransaction(signature)) { | ||||
|       break; | ||||
|     } | ||||
|     console.log('not confirmed', signature); | ||||
|     expect(mockRpcEnabled).toBe(false); | ||||
|     expect(++i).toBeLessThan(10); | ||||
|     await sleep(500); | ||||
|   } | ||||
|   const confirmResult = (await connection.confirmTransaction(signature, 1)) | ||||
|     .value; | ||||
|   verifySignatureStatus(confirmResult); | ||||
|  | ||||
|   mockRpc.push([ | ||||
|     url, | ||||
| @@ -1619,20 +1641,9 @@ test('transaction', async () => { | ||||
|     }, | ||||
|   ]); | ||||
|  | ||||
|   const response = (await connection.getSignatureStatus(signature)).value; | ||||
|   if (response === null) { | ||||
|     expect(response).not.toBeNull(); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   expect(response.err).toBeNull(); | ||||
|   expect(response.slot).toBeGreaterThanOrEqual(0); | ||||
|   const responseConfirmations = response.confirmations; | ||||
|   if (typeof responseConfirmations === 'number') { | ||||
|     expect(responseConfirmations).toBeGreaterThan(0); | ||||
|   } else { | ||||
|     expect(responseConfirmations).toBeNull(); | ||||
|   } | ||||
|   const response = verifySignatureStatus( | ||||
|     (await connection.getSignatureStatus(signature)).value, | ||||
|   ); | ||||
|  | ||||
|   const unprocessedSignature = | ||||
|     '8WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk'; | ||||
| @@ -1670,28 +1681,22 @@ test('transaction', async () => { | ||||
|     await connection.getSignatureStatuses([signature, unprocessedSignature]) | ||||
|   ).value; | ||||
|   expect(responses.length).toEqual(2); | ||||
|  | ||||
|   const firstResponse = responses[0]; | ||||
|   expect(responses[1]).toBeNull(); | ||||
|  | ||||
|   if (firstResponse === null) { | ||||
|     expect(firstResponse).not.toBeNull(); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   const firstResponse = verifySignatureStatus(responses[0]); | ||||
|   expect(firstResponse.slot).toBeGreaterThanOrEqual(response.slot); | ||||
|   expect(firstResponse.err).toEqual(response.err); | ||||
|  | ||||
|   const firstResponseConfirmations = firstResponse.confirmations; | ||||
|   const responseConfirmations = response.confirmations; | ||||
|   if ( | ||||
|     typeof responseConfirmations === 'number' && | ||||
|     typeof firstResponseConfirmations === 'number' | ||||
|     typeof firstResponse.confirmations === 'number' | ||||
|   ) { | ||||
|     expect(firstResponseConfirmations).toBeGreaterThanOrEqual( | ||||
|     expect(firstResponse.confirmations).toBeGreaterThanOrEqual( | ||||
|       responseConfirmations, | ||||
|     ); | ||||
|   } else { | ||||
|     expect(firstResponseConfirmations).toBeNull(); | ||||
|     expect(firstResponse.confirmations).toBeNull(); | ||||
|   } | ||||
|  | ||||
|   mockRpc.push([ | ||||
| @@ -1775,21 +1780,12 @@ test('multi-instruction transaction', async () => { | ||||
|       lamports: 100, | ||||
|     }), | ||||
|   ); | ||||
|   const signature = await connection.sendTransaction( | ||||
|     transaction, | ||||
|   const signature = await connection.sendTransaction(transaction, [ | ||||
|     accountFrom, | ||||
|     accountTo, | ||||
|   ); | ||||
|   let i = 0; | ||||
|   for (;;) { | ||||
|     if (await connection.confirmTransaction(signature)) { | ||||
|       break; | ||||
|     } | ||||
|   ]); | ||||
|  | ||||
|     expect(mockRpcEnabled).toBe(false); | ||||
|     expect(++i).toBeLessThan(10); | ||||
|     await sleep(500); | ||||
|   } | ||||
|   await connection.confirmTransaction(signature, 1); | ||||
|  | ||||
|   const response = (await connection.getSignatureStatus(signature)).value; | ||||
|   if (response !== null) { | ||||
| @@ -1839,7 +1835,7 @@ test('account change notification', async () => { | ||||
|       toPubkey: programAccount.publicKey, | ||||
|       lamports: balanceNeeded, | ||||
|     }); | ||||
|     await sendAndConfirmTransaction(connection, transaction, owner); | ||||
|     await sendAndConfirmTransaction(connection, transaction, [owner], 1); | ||||
|   } catch (err) { | ||||
|     await connection.removeAccountChangeListener(subscriptionId); | ||||
|     throw err; | ||||
| @@ -1903,7 +1899,7 @@ test('program account change notification', async () => { | ||||
|       toPubkey: programAccount.publicKey, | ||||
|       lamports: balanceNeeded, | ||||
|     }); | ||||
|     await sendAndConfirmTransaction(connection, transaction, owner); | ||||
|     await sendAndConfirmTransaction(connection, transaction, [owner], 1); | ||||
|   } catch (err) { | ||||
|     await connection.removeProgramAccountChangeListener(subscriptionId); | ||||
|     throw err; | ||||
|   | ||||
| @@ -103,7 +103,7 @@ test('create and query nonce account', async () => { | ||||
|     authorizedPubkey: from.publicKey, | ||||
|     lamports: minimumAmount, | ||||
|   }); | ||||
|   await connection.sendTransaction(transaction, from, nonceAccount); | ||||
|   await connection.sendTransaction(transaction, [from, nonceAccount]); | ||||
|  | ||||
|   mockRpc.push([ | ||||
|     url, | ||||
| @@ -222,7 +222,7 @@ test('create and query nonce account with seed', async () => { | ||||
|     authorizedPubkey: from.publicKey, | ||||
|     lamports: minimumAmount, | ||||
|   }); | ||||
|   await connection.sendTransaction(transaction, from); | ||||
|   await connection.sendTransaction(transaction, [from]); | ||||
|  | ||||
|   mockRpc.push([ | ||||
|     url, | ||||
|   | ||||
| @@ -6,7 +6,7 @@ import { | ||||
|   Connection, | ||||
|   Lockup, | ||||
|   PublicKey, | ||||
|   sendAndConfirmRecentTransaction, | ||||
|   sendAndConfirmTransaction, | ||||
|   LAMPORTS_PER_SOL, | ||||
|   StakeAuthorizationLayout, | ||||
|   StakeInstruction, | ||||
| @@ -268,11 +268,11 @@ test('live staking actions', async () => { | ||||
|       lamports: minimumAmount + 42, | ||||
|     }); | ||||
|  | ||||
|     await sendAndConfirmRecentTransaction( | ||||
|     await sendAndConfirmTransaction( | ||||
|       connection, | ||||
|       createAndInitialize, | ||||
|       from, | ||||
|       newStakeAccount, | ||||
|       [from, newStakeAccount], | ||||
|       0, | ||||
|     ); | ||||
|     expect(await connection.getBalance(newStakeAccount.publicKey)).toEqual( | ||||
|       minimumAmount + 42, | ||||
| @@ -283,7 +283,7 @@ test('live staking actions', async () => { | ||||
|       authorizedPubkey: authorized.publicKey, | ||||
|       votePubkey, | ||||
|     }); | ||||
|     await sendAndConfirmRecentTransaction(connection, delegation, authorized); | ||||
|     await sendAndConfirmTransaction(connection, delegation, [authorized], 0); | ||||
|   } | ||||
|  | ||||
|   // Create Stake account with seed | ||||
| @@ -304,10 +304,11 @@ test('live staking actions', async () => { | ||||
|     lamports: 3 * minimumAmount + 42, | ||||
|   }); | ||||
|  | ||||
|   await sendAndConfirmRecentTransaction( | ||||
|   await sendAndConfirmTransaction( | ||||
|     connection, | ||||
|     createAndInitializeWithSeed, | ||||
|     from, | ||||
|     [from], | ||||
|     0, | ||||
|   ); | ||||
|   let originalStakeBalance = await connection.getBalance(newAccountPubkey); | ||||
|   expect(originalStakeBalance).toEqual(3 * minimumAmount + 42); | ||||
| @@ -317,7 +318,7 @@ test('live staking actions', async () => { | ||||
|     authorizedPubkey: authorized.publicKey, | ||||
|     votePubkey, | ||||
|   }); | ||||
|   await sendAndConfirmRecentTransaction(connection, delegation, authorized); | ||||
|   await sendAndConfirmTransaction(connection, delegation, [authorized], 0); | ||||
|  | ||||
|   // Test that withdraw fails before deactivation | ||||
|   const recipient = new Account(); | ||||
| @@ -328,7 +329,7 @@ test('live staking actions', async () => { | ||||
|     lamports: 1000, | ||||
|   }); | ||||
|   await expect( | ||||
|     sendAndConfirmRecentTransaction(connection, withdraw, authorized), | ||||
|     sendAndConfirmTransaction(connection, withdraw, [authorized], 0), | ||||
|   ).rejects.toThrow(); | ||||
|  | ||||
|   // Split stake | ||||
| @@ -339,12 +340,7 @@ test('live staking actions', async () => { | ||||
|     splitStakePubkey: newStake.publicKey, | ||||
|     lamports: minimumAmount + 20, | ||||
|   }); | ||||
|   await sendAndConfirmRecentTransaction( | ||||
|     connection, | ||||
|     split, | ||||
|     authorized, | ||||
|     newStake, | ||||
|   ); | ||||
|   await sendAndConfirmTransaction(connection, split, [authorized, newStake], 0); | ||||
|  | ||||
|   // Authorize to new account | ||||
|   const newAuthorized = new Account(); | ||||
| @@ -356,14 +352,14 @@ test('live staking actions', async () => { | ||||
|     newAuthorizedPubkey: newAuthorized.publicKey, | ||||
|     stakeAuthorizationType: StakeAuthorizationLayout.Withdrawer, | ||||
|   }); | ||||
|   await sendAndConfirmRecentTransaction(connection, authorize, authorized); | ||||
|   await sendAndConfirmTransaction(connection, authorize, [authorized], 0); | ||||
|   authorize = StakeProgram.authorize({ | ||||
|     stakePubkey: newAccountPubkey, | ||||
|     authorizedPubkey: authorized.publicKey, | ||||
|     newAuthorizedPubkey: newAuthorized.publicKey, | ||||
|     stakeAuthorizationType: StakeAuthorizationLayout.Staker, | ||||
|   }); | ||||
|   await sendAndConfirmRecentTransaction(connection, authorize, authorized); | ||||
|   await sendAndConfirmTransaction(connection, authorize, [authorized], 0); | ||||
|  | ||||
|   // Test old authorized can't deactivate | ||||
|   let deactivateNotAuthorized = StakeProgram.deactivate({ | ||||
| @@ -371,10 +367,11 @@ test('live staking actions', async () => { | ||||
|     authorizedPubkey: authorized.publicKey, | ||||
|   }); | ||||
|   await expect( | ||||
|     sendAndConfirmRecentTransaction( | ||||
|     sendAndConfirmTransaction( | ||||
|       connection, | ||||
|       deactivateNotAuthorized, | ||||
|       authorized, | ||||
|       [authorized], | ||||
|       0, | ||||
|     ), | ||||
|   ).rejects.toThrow(); | ||||
|  | ||||
| @@ -383,7 +380,7 @@ test('live staking actions', async () => { | ||||
|     stakePubkey: newAccountPubkey, | ||||
|     authorizedPubkey: newAuthorized.publicKey, | ||||
|   }); | ||||
|   await sendAndConfirmRecentTransaction(connection, deactivate, newAuthorized); | ||||
|   await sendAndConfirmTransaction(connection, deactivate, [newAuthorized], 0); | ||||
|  | ||||
|   // Test that withdraw succeeds after deactivation | ||||
|   withdraw = StakeProgram.withdraw({ | ||||
| @@ -392,7 +389,7 @@ test('live staking actions', async () => { | ||||
|     toPubkey: recipient.publicKey, | ||||
|     lamports: minimumAmount + 20, | ||||
|   }); | ||||
|   await sendAndConfirmRecentTransaction(connection, withdraw, newAuthorized); | ||||
|   await sendAndConfirmTransaction(connection, withdraw, [newAuthorized], 0); | ||||
|   const balance = await connection.getBalance(newAccountPubkey); | ||||
|   expect(balance).toEqual(minimumAmount + 2); | ||||
|   const recipientBalance = await connection.getBalance(recipient.publicKey); | ||||
|   | ||||
| @@ -8,7 +8,7 @@ import { | ||||
|   SystemProgram, | ||||
|   Transaction, | ||||
|   TransactionInstruction, | ||||
|   sendAndConfirmRecentTransaction, | ||||
|   sendAndConfirmTransaction, | ||||
|   LAMPORTS_PER_SOL, | ||||
| } from '../src'; | ||||
| import {NONCE_ACCOUNT_LENGTH} from '../src/nonce-account'; | ||||
| @@ -293,11 +293,11 @@ test('live Nonce actions', async () => { | ||||
|     authorizedPubkey: from.publicKey, | ||||
|     lamports: minimumAmount, | ||||
|   }); | ||||
|   await sendAndConfirmRecentTransaction( | ||||
|   await sendAndConfirmTransaction( | ||||
|     connection, | ||||
|     createNonceAccount, | ||||
|     from, | ||||
|     nonceAccount, | ||||
|     [from, nonceAccount], | ||||
|     0, | ||||
|   ); | ||||
|   const nonceBalance = await connection.getBalance(nonceAccount.publicKey); | ||||
|   expect(nonceBalance).toEqual(minimumAmount); | ||||
| @@ -325,7 +325,7 @@ test('live Nonce actions', async () => { | ||||
|       authorizedPubkey: from.publicKey, | ||||
|     }), | ||||
|   ); | ||||
|   await sendAndConfirmRecentTransaction(connection, advanceNonce, from); | ||||
|   await sendAndConfirmTransaction(connection, advanceNonce, [from], 0); | ||||
|   const nonceQuery3 = await connection.getNonce(nonceAccount.publicKey); | ||||
|   if (nonceQuery3 === null) { | ||||
|     expect(nonceQuery3).not.toBeNull(); | ||||
| @@ -344,7 +344,7 @@ test('live Nonce actions', async () => { | ||||
|       newAuthorizedPubkey: newAuthority.publicKey, | ||||
|     }), | ||||
|   ); | ||||
|   await sendAndConfirmRecentTransaction(connection, authorizeNonce, from); | ||||
|   await sendAndConfirmTransaction(connection, authorizeNonce, [from], 0); | ||||
|  | ||||
|   let transfer = SystemProgram.transfer({ | ||||
|     fromPubkey: from.publicKey, | ||||
| @@ -359,11 +359,11 @@ test('live Nonce actions', async () => { | ||||
|     }), | ||||
|   }; | ||||
|  | ||||
|   await sendAndConfirmRecentTransaction( | ||||
|   await sendAndConfirmTransaction( | ||||
|     connection, | ||||
|     transfer, | ||||
|     from, | ||||
|     newAuthority, | ||||
|     [from, newAuthority], | ||||
|     0, | ||||
|   ); | ||||
|   const toBalance = await connection.getBalance(to.publicKey); | ||||
|   expect(toBalance).toEqual(minimumAmount); | ||||
| @@ -380,11 +380,7 @@ test('live Nonce actions', async () => { | ||||
|       toPubkey: withdrawAccount.publicKey, | ||||
|     }), | ||||
|   ); | ||||
|   await sendAndConfirmRecentTransaction( | ||||
|     connection, | ||||
|     withdrawNonce, | ||||
|     newAuthority, | ||||
|   ); | ||||
|   await sendAndConfirmTransaction(connection, withdrawNonce, [newAuthority], 0); | ||||
|   expect(await connection.getBalance(nonceAccount.publicKey)).toEqual(0); | ||||
|   const withdrawBalance = await connection.getBalance( | ||||
|     withdrawAccount.publicKey, | ||||
|   | ||||
| @@ -3,7 +3,6 @@ import {Account, Connection, SystemProgram, LAMPORTS_PER_SOL} from '../src'; | ||||
| import {mockRpc, mockRpcEnabled} from './__mocks__/node-fetch'; | ||||
| import {mockGetRecentBlockhash} from './mockrpc/get-recent-blockhash'; | ||||
| import {url} from './url'; | ||||
| import {sleep} from '../src/util/sleep'; | ||||
|  | ||||
| if (!mockRpcEnabled) { | ||||
|   // The default of 5 seconds is too slow for live testing sometimes | ||||
| @@ -106,19 +105,19 @@ test('transaction-payer', async () => { | ||||
|     lamports: 10, | ||||
|   }); | ||||
|  | ||||
|   const signature = await connection.sendTransaction( | ||||
|     transaction, | ||||
|   const signature = await connection.sendTransaction(transaction, [ | ||||
|     accountPayer, | ||||
|     accountFrom, | ||||
|   ); | ||||
|   ]); | ||||
|  | ||||
|   mockRpc.push([ | ||||
|     url, | ||||
|     { | ||||
|       method: 'confirmTransaction', | ||||
|       method: 'getSignatureStatuses', | ||||
|       params: [ | ||||
|         [ | ||||
|           '3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk', | ||||
|         {commitment: 'recent'}, | ||||
|         ], | ||||
|       ], | ||||
|     }, | ||||
|     { | ||||
| @@ -127,21 +126,19 @@ test('transaction-payer', async () => { | ||||
|         context: { | ||||
|           slot: 11, | ||||
|         }, | ||||
|         value: true, | ||||
|         value: [ | ||||
|           { | ||||
|             slot: 0, | ||||
|             confirmations: 1, | ||||
|             status: {Ok: null}, | ||||
|             err: null, | ||||
|           }, | ||||
|         ], | ||||
|       }, | ||||
|     }, | ||||
|   ]); | ||||
|  | ||||
|   let i = 0; | ||||
|   for (;;) { | ||||
|     if (await connection.confirmTransaction(signature)) { | ||||
|       break; | ||||
|     } | ||||
|  | ||||
|     expect(mockRpcEnabled).toBe(false); | ||||
|     expect(++i).toBeLessThan(10); | ||||
|     await sleep(500); | ||||
|   } | ||||
|   await connection.confirmTransaction(signature, 1); | ||||
|  | ||||
|   mockRpc.push([ | ||||
|     url, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user