2529 lines
		
	
	
		
			62 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			2529 lines
		
	
	
		
			62 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| // @flow
 | |
| import bs58 from 'bs58';
 | |
| import {Token, u64} from '@solana/spl-token';
 | |
| 
 | |
| import {
 | |
|   Account,
 | |
|   Authorized,
 | |
|   Connection,
 | |
|   SystemProgram,
 | |
|   Transaction,
 | |
|   sendAndConfirmTransaction,
 | |
|   LAMPORTS_PER_SOL,
 | |
|   Lockup,
 | |
|   PublicKey,
 | |
|   StakeProgram,
 | |
| } from '../src';
 | |
| import {DEFAULT_TICKS_PER_SLOT, NUM_TICKS_PER_SECOND} from '../src/timing';
 | |
| 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 {BLOCKHASH_CACHE_TIMEOUT_MS} from '../src/connection';
 | |
| import type {TransactionSignature} from '../src/transaction';
 | |
| import type {SignatureStatus, TransactionError} from '../src/connection';
 | |
| import {mockConfirmTransaction} from './mockrpc/confirm-transaction';
 | |
| import {mockRpcSocket} from './__mocks__/rpc-websockets';
 | |
| 
 | |
| // Testing tokens and blockhash cache each take around 30s to complete
 | |
| jest.setTimeout(40000);
 | |
| 
 | |
| const errorMessage = 'Invalid';
 | |
| const errorResponse = {
 | |
|   error: {
 | |
|     code: -32602,
 | |
|     message: errorMessage,
 | |
|   },
 | |
|   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).toBeGreaterThanOrEqual(0);
 | |
|   } else {
 | |
|     expect(confirmations).toBeNull();
 | |
|   }
 | |
|   return status;
 | |
| };
 | |
| 
 | |
| test('get account info - not found', async () => {
 | |
|   const account = new Account();
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getAccountInfo',
 | |
|       params: [account.publicKey.toBase58(), {encoding: 'base64'}],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         context: {
 | |
|           slot: 11,
 | |
|         },
 | |
|         value: null,
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   expect(await connection.getAccountInfo(account.publicKey)).toBeNull();
 | |
| 
 | |
|   if (!mockRpcEnabled) {
 | |
|     expect(
 | |
|       (await connection.getParsedAccountInfo(account.publicKey)).value,
 | |
|     ).toBeNull();
 | |
|   }
 | |
| });
 | |
| 
 | |
| test('get program accounts', async () => {
 | |
|   const connection = new Connection(url, 'recent');
 | |
|   const account0 = new Account();
 | |
|   const account1 = new Account();
 | |
|   const programId = new Account();
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'requestAirdrop',
 | |
|       params: [account0.publicKey.toBase58(), LAMPORTS_PER_SOL],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result:
 | |
|         '2WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
 | |
|     },
 | |
|   ]);
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'requestAirdrop',
 | |
|       params: [account1.publicKey.toBase58(), 0.5 * LAMPORTS_PER_SOL],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result:
 | |
|         '2WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
 | |
|     },
 | |
|   ]);
 | |
|   await connection.requestAirdrop(account0.publicKey, LAMPORTS_PER_SOL);
 | |
|   await connection.requestAirdrop(account1.publicKey, 0.5 * LAMPORTS_PER_SOL);
 | |
| 
 | |
|   mockGetRecentBlockhash('max');
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'sendTransaction',
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result:
 | |
|         '3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   let transaction = new Transaction().add(
 | |
|     SystemProgram.assign({
 | |
|       accountPubkey: account0.publicKey,
 | |
|       programId: programId.publicKey,
 | |
|     }),
 | |
|   );
 | |
| 
 | |
|   mockConfirmTransaction(
 | |
|     '3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
 | |
|   );
 | |
|   await sendAndConfirmTransaction(connection, transaction, [account0], {
 | |
|     commitment: 'single',
 | |
|     skipPreflight: true,
 | |
|   });
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'sendTransaction',
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result:
 | |
|         '3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   transaction = new Transaction().add(
 | |
|     SystemProgram.assign({
 | |
|       accountPubkey: account1.publicKey,
 | |
|       programId: programId.publicKey,
 | |
|     }),
 | |
|   );
 | |
| 
 | |
|   mockConfirmTransaction(
 | |
|     '3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
 | |
|   );
 | |
|   await sendAndConfirmTransaction(connection, transaction, [account1], {
 | |
|     commitment: 'single',
 | |
|     skipPreflight: true,
 | |
|   });
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getFeeCalculatorForBlockhash',
 | |
|       params: [transaction.recentBlockhash, {commitment: 'recent'}],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         context: {
 | |
|           slot: 11,
 | |
|         },
 | |
|         value: {
 | |
|           feeCalculator: {
 | |
|             lamportsPerSignature: 42,
 | |
|           },
 | |
|         },
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   if (transaction.recentBlockhash === null) {
 | |
|     expect(transaction.recentBlockhash).not.toBeNull();
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   const feeCalculator = (
 | |
|     await connection.getFeeCalculatorForBlockhash(transaction.recentBlockhash)
 | |
|   ).value;
 | |
| 
 | |
|   if (feeCalculator === null) {
 | |
|     expect(feeCalculator).not.toBeNull();
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getProgramAccounts',
 | |
|       params: [
 | |
|         programId.publicKey.toBase58(),
 | |
|         {commitment: 'recent', encoding: 'base64'},
 | |
|       ],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: [
 | |
|         {
 | |
|           account: {
 | |
|             data: ['', 'base64'],
 | |
|             executable: false,
 | |
|             lamports: LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
 | |
|             owner: programId.publicKey.toBase58(),
 | |
|             rentEpoch: 20,
 | |
|           },
 | |
|           pubkey: account0.publicKey.toBase58(),
 | |
|         },
 | |
|         {
 | |
|           account: {
 | |
|             data: ['', 'base64'],
 | |
|             executable: false,
 | |
|             lamports:
 | |
|               0.5 * LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
 | |
|             owner: programId.publicKey.toBase58(),
 | |
|             rentEpoch: 20,
 | |
|           },
 | |
|           pubkey: account1.publicKey.toBase58(),
 | |
|         },
 | |
|       ],
 | |
|     },
 | |
|   ]);
 | |
|   const programAccounts = await connection.getProgramAccounts(
 | |
|     programId.publicKey,
 | |
|   );
 | |
|   expect(programAccounts.length).toBe(2);
 | |
| 
 | |
|   programAccounts.forEach(function (element) {
 | |
|     if (element.pubkey.equals(account0.publicKey)) {
 | |
|       expect(element.account.lamports).toBe(
 | |
|         LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
 | |
|       );
 | |
|     } else if (element.pubkey.equals(account1.publicKey)) {
 | |
|       expect(element.account.lamports).toBe(
 | |
|         0.5 * LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
 | |
|       );
 | |
|     } else {
 | |
|       expect(element.pubkey.equals(account1.publicKey)).toBe(true);
 | |
|     }
 | |
|   });
 | |
| 
 | |
|   if (!mockRpcEnabled) {
 | |
|     const programAccounts = await connection.getParsedProgramAccounts(
 | |
|       programId.publicKey,
 | |
|     );
 | |
|     expect(programAccounts.length).toBe(2);
 | |
| 
 | |
|     programAccounts.forEach(function (element) {
 | |
|       if (element.pubkey.equals(account0.publicKey)) {
 | |
|         expect(element.account.lamports).toBe(
 | |
|           LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
 | |
|         );
 | |
|       } else if (element.pubkey.equals(account1.publicKey)) {
 | |
|         expect(element.account.lamports).toBe(
 | |
|           0.5 * LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
 | |
|         );
 | |
|       } else {
 | |
|         expect(element.pubkey.equals(account1.publicKey)).toBe(true);
 | |
|       }
 | |
|     });
 | |
|   }
 | |
| });
 | |
| 
 | |
| test('validatorExit', async () => {
 | |
|   if (!mockRpcEnabled) {
 | |
|     console.log('validatorExit skipped on live node');
 | |
|     return;
 | |
|   }
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'validatorExit',
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: false,
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const result = await connection.validatorExit();
 | |
|   expect(result).toBe(false);
 | |
| });
 | |
| 
 | |
| test('get balance', async () => {
 | |
|   const account = new Account();
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getBalance',
 | |
|       params: [account.publicKey.toBase58()],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         context: {
 | |
|           slot: 11,
 | |
|         },
 | |
|         value: 0,
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const balance = await connection.getBalance(account.publicKey);
 | |
|   expect(balance).toBeGreaterThanOrEqual(0);
 | |
| });
 | |
| 
 | |
| test('get inflation', async () => {
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getInflationGovernor',
 | |
|       params: [],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         foundation: 0.05,
 | |
|         foundationTerm: 7.0,
 | |
|         initial: 0.15,
 | |
|         taper: 0.15,
 | |
|         terminal: 0.015,
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const inflation = await connection.getInflationGovernor();
 | |
| 
 | |
|   for (const key of [
 | |
|     'initial',
 | |
|     'terminal',
 | |
|     'taper',
 | |
|     'foundation',
 | |
|     'foundationTerm',
 | |
|   ]) {
 | |
|     expect(inflation).toHaveProperty(key);
 | |
|     expect(inflation[key]).toBeGreaterThan(0);
 | |
|   }
 | |
| });
 | |
| 
 | |
| test('get epoch info', async () => {
 | |
|   const connection = new Connection(url, 'recent');
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getEpochInfo',
 | |
|       params: [{commitment: 'recent'}],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         epoch: 0,
 | |
|         slotIndex: 1,
 | |
|         slotsInEpoch: 8192,
 | |
|         absoluteSlot: 1,
 | |
|         blockHeight: 1,
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const epochInfo = await connection.getEpochInfo();
 | |
| 
 | |
|   for (const key of [
 | |
|     'epoch',
 | |
|     'slotIndex',
 | |
|     'slotsInEpoch',
 | |
|     'absoluteSlot' /*, 'blockHeight'*/, // Uncomment blockHeight after 1.1.20 ships
 | |
|   ]) {
 | |
|     expect(epochInfo).toHaveProperty(key);
 | |
|     expect(epochInfo[key]).toBeGreaterThanOrEqual(0);
 | |
|   }
 | |
| });
 | |
| 
 | |
| test('get epoch schedule', async () => {
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getEpochSchedule',
 | |
|       params: [],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         firstNormalEpoch: 8,
 | |
|         firstNormalSlot: 8160,
 | |
|         leaderScheduleSlotOffset: 8192,
 | |
|         slotsPerEpoch: 8192,
 | |
|         warmup: true,
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const epochSchedule = await connection.getEpochSchedule();
 | |
| 
 | |
|   for (const key of [
 | |
|     'firstNormalEpoch',
 | |
|     'firstNormalSlot',
 | |
|     'leaderScheduleSlotOffset',
 | |
|     'slotsPerEpoch',
 | |
|   ]) {
 | |
|     expect(epochSchedule).toHaveProperty('warmup');
 | |
|     expect(epochSchedule).toHaveProperty(key);
 | |
|     if (epochSchedule.warmup) {
 | |
|       expect(epochSchedule[key]).toBeGreaterThan(0);
 | |
|     }
 | |
|   }
 | |
| });
 | |
| 
 | |
| test('get leader schedule', async () => {
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getLeaderSchedule',
 | |
|       params: [],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         '123vij84ecQEKUvQ7gYMKxKwKF6PbYSzCzzURYA4xULY': [0, 1, 2, 3],
 | |
|         '8PTjAikKoAybKXcEPnDSoy8wSNNikUBJ1iKawJKQwXnB': [4, 5, 6, 7],
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const leaderSchedule = await connection.getLeaderSchedule();
 | |
|   expect(Object.keys(leaderSchedule).length).toBeGreaterThanOrEqual(1);
 | |
|   for (const key in leaderSchedule) {
 | |
|     const slots = leaderSchedule[key];
 | |
|     expect(Array.isArray(slots)).toBe(true);
 | |
|     expect(slots.length).toBeGreaterThanOrEqual(4);
 | |
|   }
 | |
| });
 | |
| 
 | |
| test('get slot', async () => {
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getSlot',
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: 123,
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const slotLeader = await connection.getSlot();
 | |
|   if (mockRpcEnabled) {
 | |
|     expect(slotLeader).toBe(123);
 | |
|   } else {
 | |
|     // No idea what the correct slot value should be on a live cluster, so
 | |
|     // just check the type
 | |
|     expect(typeof slotLeader).toBe('number');
 | |
|   }
 | |
| });
 | |
| 
 | |
| test('get slot leader', async () => {
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getSlotLeader',
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: '11111111111111111111111111111111',
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const slotLeader = await connection.getSlotLeader();
 | |
|   if (mockRpcEnabled) {
 | |
|     expect(slotLeader).toBe('11111111111111111111111111111111');
 | |
|   } else {
 | |
|     // No idea what the correct slotLeader value should be on a live cluster, so
 | |
|     // just check the type
 | |
|     expect(typeof slotLeader).toBe('string');
 | |
|   }
 | |
| });
 | |
| 
 | |
| test('get cluster nodes', async () => {
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getClusterNodes',
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: [
 | |
|         {
 | |
|           pubkey: '11111111111111111111111111111111',
 | |
|           gossip: '127.0.0.0:1234',
 | |
|           tpu: '127.0.0.0:1235',
 | |
|           rpc: null,
 | |
|           version: '1.1.10',
 | |
|         },
 | |
|       ],
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const clusterNodes = await connection.getClusterNodes();
 | |
|   if (mockRpcEnabled) {
 | |
|     expect(clusterNodes).toHaveLength(1);
 | |
|     expect(clusterNodes[0].pubkey).toBe('11111111111111111111111111111111');
 | |
|     expect(typeof clusterNodes[0].gossip).toBe('string');
 | |
|     expect(typeof clusterNodes[0].tpu).toBe('string');
 | |
|     expect(clusterNodes[0].rpc).toBeNull();
 | |
|   } else {
 | |
|     // There should be at least one node (the node that we're talking to)
 | |
|     expect(clusterNodes.length).toBeGreaterThan(0);
 | |
|   }
 | |
| });
 | |
| 
 | |
| test('getVoteAccounts', async () => {
 | |
|   if (mockRpcEnabled) {
 | |
|     console.log('non-live test skipped');
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   const connection = new Connection(url);
 | |
|   const voteAccounts = await connection.getVoteAccounts();
 | |
|   expect(
 | |
|     voteAccounts.current.concat(voteAccounts.delinquent).length,
 | |
|   ).toBeGreaterThan(0);
 | |
| });
 | |
| 
 | |
| test('confirm transaction - error', async () => {
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   const badTransactionSignature = 'bad transaction signature';
 | |
| 
 | |
|   await expect(
 | |
|     connection.confirmTransaction(badTransactionSignature),
 | |
|   ).rejects.toThrow('signature must be base58 encoded');
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getSignatureStatuses',
 | |
|       params: [[badTransactionSignature]],
 | |
|     },
 | |
|     errorResponse,
 | |
|   ]);
 | |
| 
 | |
|   await expect(
 | |
|     connection.getSignatureStatus(badTransactionSignature),
 | |
|   ).rejects.toThrow(errorMessage);
 | |
| });
 | |
| 
 | |
| test('get transaction count', async () => {
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getTransactionCount',
 | |
|       params: [],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: 1000000,
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const count = await connection.getTransactionCount();
 | |
|   expect(count).toBeGreaterThanOrEqual(0);
 | |
| });
 | |
| 
 | |
| test('get total supply', async () => {
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getTotalSupply',
 | |
|       params: [],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: 1000000,
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const count = await connection.getTotalSupply();
 | |
|   expect(count).toBeGreaterThanOrEqual(0);
 | |
| });
 | |
| 
 | |
| test('get minimum balance for rent exemption', async () => {
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getMinimumBalanceForRentExemption',
 | |
|       params: [512],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: 1000000,
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const count = await connection.getMinimumBalanceForRentExemption(512);
 | |
|   expect(count).toBeGreaterThanOrEqual(0);
 | |
| });
 | |
| 
 | |
| test('get confirmed signatures for address', async () => {
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getSlot',
 | |
|       params: [],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: 1,
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   while ((await connection.getSlot()) <= 0) {
 | |
|     continue;
 | |
|   }
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getConfirmedBlock',
 | |
|       params: [1],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         blockhash: '57zQNBZBEiHsCZFqsaY6h176ioXy5MsSLmcvHkEyaLGy',
 | |
|         previousBlockhash: 'H5nJ91eGag3B5ZSRHZ7zG5ZwXJ6ywCt2hyR8xCsV7xMo',
 | |
|         parentSlot: 0,
 | |
|         transactions: [
 | |
|           {
 | |
|             meta: {
 | |
|               fee: 10000,
 | |
|               postBalances: [499260347380, 15298080, 1, 1, 1],
 | |
|               preBalances: [499260357380, 15298080, 1, 1, 1],
 | |
|               status: {Ok: null},
 | |
|               err: null,
 | |
|             },
 | |
|             transaction: {
 | |
|               message: {
 | |
|                 accountKeys: [
 | |
|                   'va12u4o9DipLEB2z4fuoHszroq1U9NcAB9aooFDPJSf',
 | |
|                   '57zQNBZBEiHsCZFqsaY6h176ioXy5MsSLmcvHkEyaLGy',
 | |
|                   'SysvarS1otHashes111111111111111111111111111',
 | |
|                   'SysvarC1ock11111111111111111111111111111111',
 | |
|                   'Vote111111111111111111111111111111111111111',
 | |
|                 ],
 | |
|                 header: {
 | |
|                   numReadonlySignedAccounts: 0,
 | |
|                   numReadonlyUnsignedAccounts: 3,
 | |
|                   numRequiredSignatures: 2,
 | |
|                 },
 | |
|                 instructions: [
 | |
|                   {
 | |
|                     accounts: [1, 2, 3],
 | |
|                     data:
 | |
|                       '37u9WtQpcm6ULa3VtWDFAWoQc1hUvybPrA3dtx99tgHvvcE7pKRZjuGmn7VX2tC3JmYDYGG7',
 | |
|                     programIdIndex: 4,
 | |
|                   },
 | |
|                 ],
 | |
|                 recentBlockhash: 'GeyAFFRY3WGpmam2hbgrKw4rbU2RKzfVLm5QLSeZwTZE',
 | |
|               },
 | |
|               signatures: [
 | |
|                 'w2Zeq8YkpyB463DttvfzARD7k9ZxGEwbsEw4boEK7jDp3pfoxZbTdLFSsEPhzXhpCcjGi2kHtHFobgX49MMhbWt',
 | |
|                 '4oCEqwGrMdBeMxpzuWiukCYqSfV4DsSKXSiVVCh1iJ6pS772X7y219JZP3mgqBz5PhsvprpKyhzChjYc3VSBQXzG',
 | |
|               ],
 | |
|             },
 | |
|           },
 | |
|         ],
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   // Find a block that has a transaction, usually Block 1
 | |
|   let slot = 0;
 | |
|   let address: ?PublicKey;
 | |
|   let expectedSignature: ?string;
 | |
|   while (!address || !expectedSignature) {
 | |
|     slot++;
 | |
|     const block = await connection.getConfirmedBlock(slot);
 | |
|     if (block.transactions.length > 0) {
 | |
|       const {
 | |
|         signature,
 | |
|         publicKey,
 | |
|       } = block.transactions[0].transaction.signatures[0];
 | |
|       if (signature) {
 | |
|         address = publicKey;
 | |
|         expectedSignature = bs58.encode(signature);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // getConfirmedSignaturesForAddress tests...
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getConfirmedSignaturesForAddress',
 | |
|       params: [address.toBase58(), slot, slot + 1],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: [expectedSignature],
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const confirmedSignatures = await connection.getConfirmedSignaturesForAddress(
 | |
|     address,
 | |
|     slot,
 | |
|     slot + 1,
 | |
|   );
 | |
|   expect(confirmedSignatures.includes(expectedSignature)).toBe(true);
 | |
| 
 | |
|   const badSlot = Number.MAX_SAFE_INTEGER - 1;
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getConfirmedSignaturesForAddress',
 | |
|       params: [address.toBase58(), badSlot, badSlot + 1],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: [],
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const emptySignatures = await connection.getConfirmedSignaturesForAddress(
 | |
|     address,
 | |
|     badSlot,
 | |
|     badSlot + 1,
 | |
|   );
 | |
|   expect(emptySignatures.length).toBe(0);
 | |
| 
 | |
|   // getConfirmedSignaturesForAddress2 tests...
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getConfirmedSignaturesForAddress2',
 | |
|       params: [address.toBase58(), {limit: 1}],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: [
 | |
|         {
 | |
|           signature: expectedSignature,
 | |
|           slot,
 | |
|           err: null,
 | |
|           memo: null,
 | |
|         },
 | |
|       ],
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const confirmedSignatures2 = await connection.getConfirmedSignaturesForAddress2(
 | |
|     address,
 | |
|     {limit: 1},
 | |
|   );
 | |
|   expect(confirmedSignatures2.length).toBe(1);
 | |
|   if (mockRpcEnabled) {
 | |
|     expect(confirmedSignatures2[0].signature).toBe(expectedSignature);
 | |
|     expect(confirmedSignatures2[0].slot).toBe(slot);
 | |
|     expect(confirmedSignatures2[0].err).toBeNull();
 | |
|     expect(confirmedSignatures2[0].memo).toBeNull();
 | |
|   }
 | |
| });
 | |
| 
 | |
| test('get confirmed transaction', async () => {
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getSlot',
 | |
|       params: [],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: 1,
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   while ((await connection.getSlot()) <= 0) {
 | |
|     continue;
 | |
|   }
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getConfirmedBlock',
 | |
|       params: [1],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         blockhash: '57zQNBZBEiHsCZFqsaY6h176ioXy5MsSLmcvHkEyaLGy',
 | |
|         previousBlockhash: 'H5nJ91eGag3B5ZSRHZ7zG5ZwXJ6ywCt2hyR8xCsV7xMo',
 | |
|         parentSlot: 0,
 | |
|         transactions: [
 | |
|           {
 | |
|             meta: {
 | |
|               fee: 10000,
 | |
|               postBalances: [499260347380, 15298080, 1, 1, 1],
 | |
|               preBalances: [499260357380, 15298080, 1, 1, 1],
 | |
|               status: {Ok: null},
 | |
|               err: null,
 | |
|             },
 | |
|             transaction: {
 | |
|               message: {
 | |
|                 accountKeys: [
 | |
|                   'va12u4o9DipLEB2z4fuoHszroq1U9NcAB9aooFDPJSf',
 | |
|                   '57zQNBZBEiHsCZFqsaY6h176ioXy5MsSLmcvHkEyaLGy',
 | |
|                   'SysvarS1otHashes111111111111111111111111111',
 | |
|                   'SysvarC1ock11111111111111111111111111111111',
 | |
|                   'Vote111111111111111111111111111111111111111',
 | |
|                 ],
 | |
|                 header: {
 | |
|                   numReadonlySignedAccounts: 0,
 | |
|                   numReadonlyUnsignedAccounts: 3,
 | |
|                   numRequiredSignatures: 2,
 | |
|                 },
 | |
|                 instructions: [
 | |
|                   {
 | |
|                     accounts: [1, 2, 3],
 | |
|                     data:
 | |
|                       '37u9WtQpcm6ULa3VtWDFAWoQc1hUvybPrA3dtx99tgHvvcE7pKRZjuGmn7VX2tC3JmYDYGG7',
 | |
|                     programIdIndex: 4,
 | |
|                   },
 | |
|                 ],
 | |
|                 recentBlockhash: 'GeyAFFRY3WGpmam2hbgrKw4rbU2RKzfVLm5QLSeZwTZE',
 | |
|               },
 | |
|               signatures: [
 | |
|                 'w2Zeq8YkpyB463DttvfzARD7k9ZxGEwbsEw4boEK7jDp3pfoxZbTdLFSsEPhzXhpCcjGi2kHtHFobgX49MMhbWt',
 | |
|                 '4oCEqwGrMdBeMxpzuWiukCYqSfV4DsSKXSiVVCh1iJ6pS772X7y219JZP3mgqBz5PhsvprpKyhzChjYc3VSBQXzG',
 | |
|               ],
 | |
|             },
 | |
|           },
 | |
|         ],
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   // Find a block that has a transaction, usually Block 1
 | |
|   let slot = 0;
 | |
|   let confirmedTransaction: ?string;
 | |
|   while (!confirmedTransaction) {
 | |
|     slot++;
 | |
|     const block = await connection.getConfirmedBlock(slot);
 | |
|     for (const tx of block.transactions) {
 | |
|       if (tx.transaction.signature) {
 | |
|         confirmedTransaction = bs58.encode(tx.transaction.signature);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getConfirmedTransaction',
 | |
|       params: [confirmedTransaction],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         slot,
 | |
|         transaction: {
 | |
|           message: {
 | |
|             accountKeys: [
 | |
|               'va12u4o9DipLEB2z4fuoHszroq1U9NcAB9aooFDPJSf',
 | |
|               '57zQNBZBEiHsCZFqsaY6h176ioXy5MsSLmcvHkEyaLGy',
 | |
|               'SysvarS1otHashes111111111111111111111111111',
 | |
|               'SysvarC1ock11111111111111111111111111111111',
 | |
|               'Vote111111111111111111111111111111111111111',
 | |
|             ],
 | |
|             header: {
 | |
|               numReadonlySignedAccounts: 0,
 | |
|               numReadonlyUnsignedAccounts: 3,
 | |
|               numRequiredSignatures: 2,
 | |
|             },
 | |
|             instructions: [
 | |
|               {
 | |
|                 accounts: [1, 2, 3],
 | |
|                 data:
 | |
|                   '37u9WtQpcm6ULa3VtWDFAWoQc1hUvybPrA3dtx99tgHvvcE7pKRZjuGmn7VX2tC3JmYDYGG7',
 | |
|                 programIdIndex: 4,
 | |
|               },
 | |
|             ],
 | |
|             recentBlockhash: 'GeyAFFRY3WGpmam2hbgrKw4rbU2RKzfVLm5QLSeZwTZE',
 | |
|           },
 | |
|           signatures: [
 | |
|             'w2Zeq8YkpyB463DttvfzARD7k9ZxGEwbsEw4boEK7jDp3pfoxZbTdLFSsEPhzXhpCcjGi2kHtHFobgX49MMhbWt',
 | |
|             '4oCEqwGrMdBeMxpzuWiukCYqSfV4DsSKXSiVVCh1iJ6pS772X7y219JZP3mgqBz5PhsvprpKyhzChjYc3VSBQXzG',
 | |
|           ],
 | |
|         },
 | |
|         meta: {
 | |
|           fee: 10000,
 | |
|           postBalances: [499260347380, 15298080, 1, 1, 1],
 | |
|           preBalances: [499260357380, 15298080, 1, 1, 1],
 | |
|           status: {Ok: null},
 | |
|           err: null,
 | |
|         },
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const result = await connection.getConfirmedTransaction(confirmedTransaction);
 | |
| 
 | |
|   if (!result) {
 | |
|     expect(result).toBeDefined();
 | |
|     expect(result).not.toBeNull();
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (result.transaction.signature === null) {
 | |
|     expect(result.transaction.signature).not.toBeNull();
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   const resultSignature = bs58.encode(result.transaction.signature);
 | |
|   expect(resultSignature).toEqual(confirmedTransaction);
 | |
| 
 | |
|   const newAddress = new Account().publicKey;
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'requestAirdrop',
 | |
|       params: [newAddress.toBase58(), 1],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result:
 | |
|         '1WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const recentSignature = await connection.requestAirdrop(newAddress, 1);
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getConfirmedTransaction',
 | |
|       params: [recentSignature],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: null,
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const nullResponse = await connection.getConfirmedTransaction(
 | |
|     recentSignature,
 | |
|   );
 | |
|   expect(nullResponse).toBeNull();
 | |
| });
 | |
| 
 | |
| test('get confirmed block', async () => {
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getSlot',
 | |
|       params: [],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: 1,
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   while ((await connection.getSlot()) <= 0) {
 | |
|     continue;
 | |
|   }
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getConfirmedBlock',
 | |
|       params: [0],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         blockhash: 'H5nJ91eGag3B5ZSRHZ7zG5ZwXJ6ywCt2hyR8xCsV7xMo',
 | |
|         previousBlockhash: 'H5nJ91eGag3B5ZSRHZ7zG5ZwXJ6ywCt2hyR8xCsV7xMo',
 | |
|         parentSlot: 0,
 | |
|         transactions: [],
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   // Block 0 never has any transactions in automation localnet
 | |
|   const block0 = await connection.getConfirmedBlock(0);
 | |
|   const blockhash0 = block0.blockhash;
 | |
|   expect(block0.transactions.length).toBe(0);
 | |
|   expect(blockhash0).not.toBeNull();
 | |
|   expect(block0.previousBlockhash).not.toBeNull();
 | |
|   expect(block0.parentSlot).toBe(0);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getConfirmedBlock',
 | |
|       params: [1],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         blockhash: '57zQNBZBEiHsCZFqsaY6h176ioXy5MsSLmcvHkEyaLGy',
 | |
|         previousBlockhash: 'H5nJ91eGag3B5ZSRHZ7zG5ZwXJ6ywCt2hyR8xCsV7xMo',
 | |
|         parentSlot: 0,
 | |
|         transactions: [
 | |
|           {
 | |
|             meta: {
 | |
|               fee: 10000,
 | |
|               postBalances: [499260347380, 15298080, 1, 1, 1],
 | |
|               preBalances: [499260357380, 15298080, 1, 1, 1],
 | |
|               status: {Ok: null},
 | |
|               err: null,
 | |
|             },
 | |
|             transaction: {
 | |
|               message: {
 | |
|                 accountKeys: [
 | |
|                   'va12u4o9DipLEB2z4fuoHszroq1U9NcAB9aooFDPJSf',
 | |
|                   '57zQNBZBEiHsCZFqsaY6h176ioXy5MsSLmcvHkEyaLGy',
 | |
|                   'SysvarS1otHashes111111111111111111111111111',
 | |
|                   'SysvarC1ock11111111111111111111111111111111',
 | |
|                   'Vote111111111111111111111111111111111111111',
 | |
|                 ],
 | |
|                 header: {
 | |
|                   numReadonlySignedAccounts: 0,
 | |
|                   numReadonlyUnsignedAccounts: 3,
 | |
|                   numRequiredSignatures: 2,
 | |
|                 },
 | |
|                 instructions: [
 | |
|                   {
 | |
|                     accounts: [1, 2, 3],
 | |
|                     data:
 | |
|                       '37u9WtQpcm6ULa3VtWDFAWoQc1hUvybPrA3dtx99tgHvvcE7pKRZjuGmn7VX2tC3JmYDYGG7',
 | |
|                     programIdIndex: 4,
 | |
|                   },
 | |
|                 ],
 | |
|                 recentBlockhash: 'GeyAFFRY3WGpmam2hbgrKw4rbU2RKzfVLm5QLSeZwTZE',
 | |
|               },
 | |
|               signatures: [
 | |
|                 'w2Zeq8YkpyB463DttvfzARD7k9ZxGEwbsEw4boEK7jDp3pfoxZbTdLFSsEPhzXhpCcjGi2kHtHFobgX49MMhbWt',
 | |
|                 '4oCEqwGrMdBeMxpzuWiukCYqSfV4DsSKXSiVVCh1iJ6pS772X7y219JZP3mgqBz5PhsvprpKyhzChjYc3VSBQXzG',
 | |
|               ],
 | |
|             },
 | |
|           },
 | |
|         ],
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   // Find a block that has a transaction, usually Block 1
 | |
|   let x = 1;
 | |
|   while (x < 10) {
 | |
|     const block1 = await connection.getConfirmedBlock(x);
 | |
|     if (block1.transactions.length >= 1) {
 | |
|       expect(block1.previousBlockhash).toBe(blockhash0);
 | |
|       expect(block1.blockhash).not.toBeNull();
 | |
|       expect(block1.parentSlot).toBe(0);
 | |
|       expect(block1.transactions[0].transaction).not.toBeNull();
 | |
|       break;
 | |
|     }
 | |
|     x++;
 | |
|   }
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getConfirmedBlock',
 | |
|       params: [Number.MAX_SAFE_INTEGER],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: null,
 | |
|     },
 | |
|   ]);
 | |
|   await expect(
 | |
|     connection.getConfirmedBlock(Number.MAX_SAFE_INTEGER),
 | |
|   ).rejects.toThrow();
 | |
| });
 | |
| 
 | |
| test('get recent blockhash', async () => {
 | |
|   const connection = new Connection(url);
 | |
|   for (const commitment of [
 | |
|     'max',
 | |
|     'recent',
 | |
|     'root',
 | |
|     'single',
 | |
|     'singleGossip',
 | |
|   ]) {
 | |
|     mockGetRecentBlockhash(commitment);
 | |
| 
 | |
|     const {blockhash, feeCalculator} = await connection.getRecentBlockhash(
 | |
|       commitment,
 | |
|     );
 | |
|     expect(blockhash.length).toBeGreaterThanOrEqual(43);
 | |
|     expect(feeCalculator.lamportsPerSignature).toBeGreaterThanOrEqual(0);
 | |
|   }
 | |
| });
 | |
| 
 | |
| test('get fee calculator', async () => {
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   mockGetRecentBlockhash('recent');
 | |
|   const {blockhash} = await connection.getRecentBlockhash('recent');
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getFeeCalculatorForBlockhash',
 | |
|       params: [blockhash, {commitment: 'recent'}],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         context: {
 | |
|           slot: 11,
 | |
|         },
 | |
|         value: {
 | |
|           feeCalculator: {
 | |
|             lamportsPerSignature: 5000,
 | |
|           },
 | |
|         },
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const feeCalculator = (
 | |
|     await connection.getFeeCalculatorForBlockhash(blockhash, 'recent')
 | |
|   ).value;
 | |
|   if (feeCalculator === null) {
 | |
|     expect(feeCalculator).not.toBeNull();
 | |
|     return;
 | |
|   }
 | |
|   expect(feeCalculator.lamportsPerSignature).toEqual(5000);
 | |
| });
 | |
| 
 | |
| test('get block time', async () => {
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getBlockTime',
 | |
|       params: [1],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: 10000,
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const blockTime = await connection.getBlockTime(1);
 | |
|   if (blockTime === null) {
 | |
|     // TODO: enable after https://github.com/solana-labs/solana/issues/11849 fixed
 | |
|     // expect(blockTime).not.toBeNull();
 | |
|   } else {
 | |
|     expect(blockTime).toBeGreaterThan(0);
 | |
|   }
 | |
| });
 | |
| 
 | |
| test('get minimum ledger slot', async () => {
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'minimumLedgerSlot',
 | |
|       params: [],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: 0,
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const minimumLedgerSlot = await connection.getMinimumLedgerSlot();
 | |
|   expect(minimumLedgerSlot).toBeGreaterThanOrEqual(0);
 | |
| });
 | |
| 
 | |
| test('get first available block', async () => {
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getFirstAvailableBlock',
 | |
|       params: [],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: 0,
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const firstAvailableBlock = await connection.getFirstAvailableBlock();
 | |
|   expect(firstAvailableBlock).toBeGreaterThanOrEqual(0);
 | |
| });
 | |
| 
 | |
| test('get supply', async () => {
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getSupply',
 | |
|       params: [],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         context: {
 | |
|           slot: 1,
 | |
|         },
 | |
|         value: {
 | |
|           total: 1000,
 | |
|           circulating: 100,
 | |
|           nonCirculating: 900,
 | |
|           nonCirculatingAccounts: [new Account().publicKey.toBase58()],
 | |
|         },
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const supply = (await connection.getSupply()).value;
 | |
|   expect(supply.total).toBeGreaterThan(0);
 | |
|   expect(supply.circulating).toBeGreaterThan(0);
 | |
|   expect(supply.nonCirculating).toBeGreaterThan(0);
 | |
|   expect(supply.nonCirculatingAccounts.length).toBeGreaterThan(0);
 | |
| });
 | |
| 
 | |
| const TOKEN_PROGRAM_ID = new PublicKey(
 | |
|   'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
 | |
| );
 | |
| 
 | |
| describe('token methods', () => {
 | |
|   if (mockRpcEnabled) {
 | |
|     console.log('non-live test skipped');
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   const connection = new Connection(url);
 | |
|   const newAccount = new Account().publicKey;
 | |
| 
 | |
|   let testToken: Token;
 | |
|   let testTokenAccount: PublicKey;
 | |
|   let testSignature: TransactionSignature;
 | |
|   let testOwner: Account;
 | |
| 
 | |
|   // Setup token mints and accounts for token tests
 | |
|   beforeAll(async () => {
 | |
|     const payerAccount = new Account();
 | |
|     await connection.confirmTransaction(
 | |
|       await connection.requestAirdrop(payerAccount.publicKey, 100000000000),
 | |
|       'single',
 | |
|     );
 | |
| 
 | |
|     const mintOwner = new Account();
 | |
|     const accountOwner = new Account();
 | |
|     const token = await Token.createMint(
 | |
|       connection,
 | |
|       payerAccount,
 | |
|       mintOwner.publicKey,
 | |
|       null,
 | |
|       2,
 | |
|       TOKEN_PROGRAM_ID,
 | |
|     );
 | |
| 
 | |
|     const tokenAccount = await token.createAccount(accountOwner.publicKey);
 | |
|     await token.mintTo(tokenAccount, mintOwner, [], 11111);
 | |
| 
 | |
|     const token2 = await Token.createMint(
 | |
|       connection,
 | |
|       payerAccount,
 | |
|       mintOwner.publicKey,
 | |
|       null,
 | |
|       2,
 | |
|       TOKEN_PROGRAM_ID,
 | |
|     );
 | |
| 
 | |
|     const token2Account = await token2.createAccount(accountOwner.publicKey);
 | |
|     await token2.mintTo(token2Account, mintOwner, [], 100);
 | |
| 
 | |
|     const tokenAccountDest = await token.createAccount(accountOwner.publicKey);
 | |
|     testSignature = await token.transfer(
 | |
|       tokenAccount,
 | |
|       tokenAccountDest,
 | |
|       accountOwner,
 | |
|       [],
 | |
|       new u64(1),
 | |
|     );
 | |
| 
 | |
|     await connection.confirmTransaction(testSignature);
 | |
| 
 | |
|     testOwner = accountOwner;
 | |
|     testToken = token;
 | |
|     testTokenAccount = tokenAccount;
 | |
|   });
 | |
| 
 | |
|   test('get token supply', async () => {
 | |
|     const supply = (await connection.getTokenSupply(testToken.publicKey)).value;
 | |
|     expect(supply.uiAmount).toEqual(111.11);
 | |
|     expect(supply.decimals).toEqual(2);
 | |
|     expect(supply.amount).toEqual('11111');
 | |
| 
 | |
|     await expect(connection.getTokenSupply(newAccount)).rejects.toThrow();
 | |
|   });
 | |
| 
 | |
|   test('get token largest accounts', async () => {
 | |
|     const largestAccounts = (
 | |
|       await connection.getTokenLargestAccounts(testToken.publicKey)
 | |
|     ).value;
 | |
| 
 | |
|     expect(largestAccounts.length).toEqual(2);
 | |
|     const largestAccount = largestAccounts[0];
 | |
|     expect(largestAccount.address.equals(testTokenAccount)).toBe(true);
 | |
|     expect(largestAccount.amount).toEqual('11110');
 | |
|     expect(largestAccount.decimals).toEqual(2);
 | |
|     expect(largestAccount.uiAmount).toEqual(111.1);
 | |
| 
 | |
|     await expect(
 | |
|       connection.getTokenLargestAccounts(newAccount),
 | |
|     ).rejects.toThrow();
 | |
|   });
 | |
| 
 | |
|   test('get confirmed token transaction', async () => {
 | |
|     const parsedTx = await connection.getParsedConfirmedTransaction(
 | |
|       testSignature,
 | |
|     );
 | |
|     if (parsedTx === null) {
 | |
|       expect(parsedTx).not.toBeNull();
 | |
|       return;
 | |
|     }
 | |
|     const {signatures, message} = parsedTx.transaction;
 | |
|     expect(signatures[0]).toEqual(testSignature);
 | |
|     const ix = message.instructions[0];
 | |
|     if (ix.parsed) {
 | |
|       expect(ix.program).toEqual('spl-token');
 | |
|       expect(ix.programId.equals(TOKEN_PROGRAM_ID)).toBe(true);
 | |
|     } else {
 | |
|       expect('parsed' in ix).toBe(true);
 | |
|     }
 | |
| 
 | |
|     const missingSignature =
 | |
|       '45pGoC4Rr3fJ1TKrsiRkhHRbdUeX7633XAGVec6XzVdpRbzQgHhe6ZC6Uq164MPWtiqMg7wCkC6Wy3jy2BqsDEKf';
 | |
|     const nullResponse = await connection.getParsedConfirmedTransaction(
 | |
|       missingSignature,
 | |
|     );
 | |
| 
 | |
|     expect(nullResponse).toBeNull();
 | |
|   });
 | |
| 
 | |
|   test('get token account balance', async () => {
 | |
|     const balance = (await connection.getTokenAccountBalance(testTokenAccount))
 | |
|       .value;
 | |
|     expect(balance.amount).toEqual('11110');
 | |
|     expect(balance.decimals).toEqual(2);
 | |
|     expect(balance.uiAmount).toEqual(111.1);
 | |
| 
 | |
|     await expect(
 | |
|       connection.getTokenAccountBalance(newAccount),
 | |
|     ).rejects.toThrow();
 | |
|   });
 | |
| 
 | |
|   test('get parsed token account info', async () => {
 | |
|     const accountInfo = (
 | |
|       await connection.getParsedAccountInfo(testTokenAccount)
 | |
|     ).value;
 | |
|     if (accountInfo) {
 | |
|       const data = accountInfo.data;
 | |
|       if (data instanceof Buffer) {
 | |
|         expect(data instanceof Buffer).toBe(false);
 | |
|       } else {
 | |
|         expect(data.program).toEqual('spl-token');
 | |
|         expect(data.parsed).toBeTruthy();
 | |
|       }
 | |
|     }
 | |
|   });
 | |
| 
 | |
|   test('get parsed token program accounts', async () => {
 | |
|     const tokenAccounts = await connection.getParsedProgramAccounts(
 | |
|       TOKEN_PROGRAM_ID,
 | |
|     );
 | |
|     tokenAccounts.forEach(({account}) => {
 | |
|       expect(account.owner.equals(TOKEN_PROGRAM_ID)).toBe(true);
 | |
|       const data = account.data;
 | |
|       if (data instanceof Buffer) {
 | |
|         expect(data instanceof Buffer).toBe(false);
 | |
|       } else {
 | |
|         expect(data.parsed).toBeTruthy();
 | |
|         expect(data.program).toEqual('spl-token');
 | |
|       }
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   test('get parsed token accounts by owner', async () => {
 | |
|     const tokenAccounts = (
 | |
|       await connection.getParsedTokenAccountsByOwner(testOwner.publicKey, {
 | |
|         mint: testToken.publicKey,
 | |
|       })
 | |
|     ).value;
 | |
|     tokenAccounts.forEach(({account}) => {
 | |
|       expect(account.owner.equals(TOKEN_PROGRAM_ID)).toBe(true);
 | |
|       const data = account.data;
 | |
|       if (data instanceof Buffer) {
 | |
|         expect(data instanceof Buffer).toBe(false);
 | |
|       } else {
 | |
|         expect(data.parsed).toBeTruthy();
 | |
|         expect(data.program).toEqual('spl-token');
 | |
|       }
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   test('get token accounts by owner', async () => {
 | |
|     const accountsWithMintFilter = (
 | |
|       await connection.getTokenAccountsByOwner(testOwner.publicKey, {
 | |
|         mint: testToken.publicKey,
 | |
|       })
 | |
|     ).value;
 | |
|     expect(accountsWithMintFilter.length).toEqual(2);
 | |
| 
 | |
|     const accountsWithProgramFilter = (
 | |
|       await connection.getTokenAccountsByOwner(testOwner.publicKey, {
 | |
|         programId: TOKEN_PROGRAM_ID,
 | |
|       })
 | |
|     ).value;
 | |
|     expect(accountsWithProgramFilter.length).toEqual(3);
 | |
| 
 | |
|     const noAccounts = (
 | |
|       await connection.getTokenAccountsByOwner(newAccount, {
 | |
|         mint: testToken.publicKey,
 | |
|       })
 | |
|     ).value;
 | |
|     expect(noAccounts.length).toEqual(0);
 | |
| 
 | |
|     await expect(
 | |
|       connection.getTokenAccountsByOwner(testOwner.publicKey, {
 | |
|         mint: newAccount,
 | |
|       }),
 | |
|     ).rejects.toThrow();
 | |
| 
 | |
|     await expect(
 | |
|       connection.getTokenAccountsByOwner(testOwner.publicKey, {
 | |
|         programId: newAccount,
 | |
|       }),
 | |
|     ).rejects.toThrow();
 | |
|   });
 | |
| });
 | |
| 
 | |
| test('get largest accounts', async () => {
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getLargestAccounts',
 | |
|       params: [],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         context: {
 | |
|           slot: 1,
 | |
|         },
 | |
|         value: new Array(20).fill(0).map(() => ({
 | |
|           address: new Account().publicKey.toBase58(),
 | |
|           lamports: 1000,
 | |
|         })),
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const largestAccounts = (await connection.getLargestAccounts()).value;
 | |
|   expect(largestAccounts.length).toEqual(20);
 | |
| });
 | |
| 
 | |
| test('stake activation should throw when called for not delegated account', async () => {
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   const publicKey = new Account().publicKey;
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getStakeActivation',
 | |
|       params: [publicKey.toBase58(), {}],
 | |
|     },
 | |
|     {
 | |
|       error: {message: 'account not delegated'},
 | |
|       result: undefined,
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   await expect(connection.getStakeActivation(publicKey)).rejects.toThrow();
 | |
| });
 | |
| 
 | |
| test('stake activation should return activating for new accounts', async () => {
 | |
|   if (mockRpcEnabled) {
 | |
|     console.log('non-live test skipped');
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   const connection = new Connection(url, 'recent');
 | |
|   const voteAccounts = await connection.getVoteAccounts();
 | |
|   const voteAccount = voteAccounts.current.concat(voteAccounts.delinquent)[0];
 | |
|   const votePubkey = new PublicKey(voteAccount.votePubkey);
 | |
| 
 | |
|   const authorized = new Account();
 | |
|   await connection.requestAirdrop(authorized.publicKey, 2 * LAMPORTS_PER_SOL);
 | |
| 
 | |
|   const minimumAmount = await connection.getMinimumBalanceForRentExemption(
 | |
|     StakeProgram.space,
 | |
|     'recent',
 | |
|   );
 | |
| 
 | |
|   const newStakeAccount = new Account();
 | |
|   let createAndInitialize = StakeProgram.createAccount({
 | |
|     fromPubkey: authorized.publicKey,
 | |
|     stakePubkey: newStakeAccount.publicKey,
 | |
|     authorized: new Authorized(authorized.publicKey, authorized.publicKey),
 | |
|     lockup: new Lockup(0, 0, new PublicKey(0)),
 | |
|     lamports: minimumAmount + 42,
 | |
|   });
 | |
| 
 | |
|   await sendAndConfirmTransaction(
 | |
|     connection,
 | |
|     createAndInitialize,
 | |
|     [authorized, newStakeAccount],
 | |
|     {commitment: 'single', skipPreflight: true},
 | |
|   );
 | |
|   let delegation = StakeProgram.delegate({
 | |
|     stakePubkey: newStakeAccount.publicKey,
 | |
|     authorizedPubkey: authorized.publicKey,
 | |
|     votePubkey,
 | |
|   });
 | |
|   await sendAndConfirmTransaction(connection, delegation, [authorized], {
 | |
|     commitment: 'single',
 | |
|     skipPreflight: true,
 | |
|   });
 | |
| 
 | |
|   const LARGE_EPOCH = 4000;
 | |
|   await expect(
 | |
|     connection.getStakeActivation(
 | |
|       newStakeAccount.publicKey,
 | |
|       'recent',
 | |
|       LARGE_EPOCH,
 | |
|     ),
 | |
|   ).rejects.toThrow(
 | |
|     `failed to get Stake Activation ${newStakeAccount.publicKey.toBase58()}: Invalid param: epoch ${LARGE_EPOCH} has not yet started`,
 | |
|   );
 | |
| 
 | |
|   const activationState = await connection.getStakeActivation(
 | |
|     newStakeAccount.publicKey,
 | |
|   );
 | |
|   expect(activationState.state).toBe('activating');
 | |
|   expect(activationState.inactive).toBe(42);
 | |
|   expect(activationState.active).toBe(0);
 | |
| });
 | |
| 
 | |
| test('stake activation should only accept state with valid string literals', async () => {
 | |
|   if (!mockRpcEnabled) {
 | |
|     console.log('live test skipped');
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   const connection = new Connection(url, 'recent');
 | |
|   const publicKey = new Account().publicKey;
 | |
| 
 | |
|   const addStakeActivationMock = state => {
 | |
|     mockRpc.push([
 | |
|       url,
 | |
|       {
 | |
|         method: 'getStakeActivation',
 | |
|         params: [publicKey.toBase58(), {}],
 | |
|       },
 | |
|       {
 | |
|         error: undefined,
 | |
|         result: {
 | |
|           state: state,
 | |
|           active: 0,
 | |
|           inactive: 80,
 | |
|         },
 | |
|       },
 | |
|     ]);
 | |
|   };
 | |
| 
 | |
|   addStakeActivationMock('active');
 | |
|   let activation = await connection.getStakeActivation(publicKey);
 | |
|   expect(activation.state).toBe('active');
 | |
|   expect(activation.active).toBe(0);
 | |
|   expect(activation.inactive).toBe(80);
 | |
| 
 | |
|   addStakeActivationMock('invalid');
 | |
|   await expect(connection.getStakeActivation(publicKey)).rejects.toThrow();
 | |
| });
 | |
| 
 | |
| test('getVersion', async () => {
 | |
|   const connection = new Connection(url);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getVersion',
 | |
|       params: [],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {'solana-core': '0.20.4'},
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const version = await connection.getVersion();
 | |
|   expect(version['solana-core']).toBeTruthy();
 | |
| });
 | |
| 
 | |
| test('request airdrop', async () => {
 | |
|   const account = new Account();
 | |
|   const connection = new Connection(url, 'recent');
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getMinimumBalanceForRentExemption',
 | |
|       params: [0, {commitment: 'recent'}],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: 50,
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const minimumAmount = await connection.getMinimumBalanceForRentExemption(
 | |
|     0,
 | |
|     'recent',
 | |
|   );
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'requestAirdrop',
 | |
|       params: [account.publicKey.toBase58(), minimumAmount + 42],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result:
 | |
|         '1WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const signature = await connection.requestAirdrop(
 | |
|     account.publicKey,
 | |
|     minimumAmount + 42,
 | |
|   );
 | |
| 
 | |
|   mockConfirmTransaction(signature);
 | |
|   await connection.confirmTransaction(signature, 'single');
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getBalance',
 | |
|       params: [account.publicKey.toBase58(), {commitment: 'recent'}],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         context: {
 | |
|           slot: 11,
 | |
|         },
 | |
|         value: minimumAmount + 42,
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const balance = await connection.getBalance(account.publicKey);
 | |
|   expect(balance).toBe(minimumAmount + 42);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getAccountInfo',
 | |
|       params: [
 | |
|         account.publicKey.toBase58(),
 | |
|         {commitment: 'recent', encoding: 'base64'},
 | |
|       ],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         context: {
 | |
|           slot: 11,
 | |
|         },
 | |
|         value: {
 | |
|           owner: '11111111111111111111111111111111',
 | |
|           lamports: minimumAmount + 42,
 | |
|           data: ['', 'base64'],
 | |
|           executable: false,
 | |
|         },
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const accountInfo = await connection.getAccountInfo(account.publicKey);
 | |
|   if (accountInfo === null) {
 | |
|     expect(accountInfo).not.toBeNull();
 | |
|     return;
 | |
|   }
 | |
|   expect(accountInfo.lamports).toBe(minimumAmount + 42);
 | |
|   expect(accountInfo.data).toHaveLength(0);
 | |
|   expect(accountInfo.owner).toEqual(SystemProgram.programId);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getAccountInfo',
 | |
|       params: [
 | |
|         account.publicKey.toBase58(),
 | |
|         {commitment: 'recent', encoding: 'jsonParsed'},
 | |
|       ],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         context: {
 | |
|           slot: 11,
 | |
|         },
 | |
|         value: {
 | |
|           owner: '11111111111111111111111111111111',
 | |
|           lamports: minimumAmount + 42,
 | |
|           data: ['', 'base64'],
 | |
|           executable: false,
 | |
|         },
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const parsedAccountInfo = (
 | |
|     await connection.getParsedAccountInfo(account.publicKey)
 | |
|   ).value;
 | |
|   if (parsedAccountInfo === null) {
 | |
|     expect(parsedAccountInfo).not.toBeNull();
 | |
|     return;
 | |
|   } else if (parsedAccountInfo.data.parsed) {
 | |
|     expect(parsedAccountInfo.data.parsed).not.toBeTruthy();
 | |
|     return;
 | |
|   }
 | |
|   expect(parsedAccountInfo.lamports).toBe(minimumAmount + 42);
 | |
|   expect(parsedAccountInfo.data).toHaveLength(0);
 | |
|   expect(parsedAccountInfo.owner).toEqual(SystemProgram.programId);
 | |
| });
 | |
| 
 | |
| test('transaction failure', async () => {
 | |
|   const account = new Account();
 | |
|   const connection = new Connection(url, 'recent');
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getMinimumBalanceForRentExemption',
 | |
|       params: [0, {commitment: 'recent'}],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: 50,
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const minimumAmount = await connection.getMinimumBalanceForRentExemption(
 | |
|     0,
 | |
|     'recent',
 | |
|   );
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'requestAirdrop',
 | |
|       params: [account.publicKey.toBase58(), minimumAmount + 100010],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result:
 | |
|         '2WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
 | |
|     },
 | |
|   ]);
 | |
|   const airdropSignature = await connection.requestAirdrop(
 | |
|     account.publicKey,
 | |
|     minimumAmount + 100010,
 | |
|   );
 | |
| 
 | |
|   mockConfirmTransaction(airdropSignature);
 | |
|   await connection.confirmTransaction(airdropSignature, 'single');
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getBalance',
 | |
|       params: [account.publicKey.toBase58(), {commitment: 'recent'}],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         context: {
 | |
|           slot: 11,
 | |
|         },
 | |
|         value: minimumAmount + 100010,
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
|   expect(await connection.getBalance(account.publicKey)).toBe(
 | |
|     minimumAmount + 100010,
 | |
|   );
 | |
| 
 | |
|   mockGetRecentBlockhash('max');
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'sendTransaction',
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result:
 | |
|         '3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const newAccount = new Account();
 | |
|   let transaction = new Transaction().add(
 | |
|     SystemProgram.createAccount({
 | |
|       fromPubkey: account.publicKey,
 | |
|       newAccountPubkey: newAccount.publicKey,
 | |
|       lamports: 1000,
 | |
|       space: 0,
 | |
|       programId: SystemProgram.programId,
 | |
|     }),
 | |
|   );
 | |
| 
 | |
|   mockConfirmTransaction(
 | |
|     '3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
 | |
|   );
 | |
|   await sendAndConfirmTransaction(
 | |
|     connection,
 | |
|     transaction,
 | |
|     [account, newAccount],
 | |
|     {commitment: 'single', skipPreflight: true},
 | |
|   );
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'sendTransaction',
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result:
 | |
|         '3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   // This should fail because the account is already created
 | |
|   transaction = new Transaction().add(
 | |
|     SystemProgram.createAccount({
 | |
|       fromPubkey: account.publicKey,
 | |
|       newAccountPubkey: newAccount.publicKey,
 | |
|       lamports: 10,
 | |
|       space: 0,
 | |
|       programId: SystemProgram.programId,
 | |
|     }),
 | |
|   );
 | |
|   const signature = await connection.sendTransaction(
 | |
|     transaction,
 | |
|     [account, newAccount],
 | |
|     {skipPreflight: true},
 | |
|   );
 | |
| 
 | |
|   const expectedErr = {InstructionError: [0, {Custom: 0}]};
 | |
|   mockRpcSocket.push([
 | |
|     {
 | |
|       method: 'signatureSubscribe',
 | |
|       params: [signature, {commitment: 'single'}],
 | |
|     },
 | |
|     {
 | |
|       context: {
 | |
|         slot: 11,
 | |
|       },
 | |
|       value: {err: expectedErr},
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   // Wait for one confirmation
 | |
|   const confirmResult = (
 | |
|     await connection.confirmTransaction(signature, 'single')
 | |
|   ).value;
 | |
|   expect(confirmResult.err).toEqual(expectedErr);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getSignatureStatuses',
 | |
|       params: [
 | |
|         [
 | |
|           '3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
 | |
|         ],
 | |
|       ],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         context: {
 | |
|           slot: 11,
 | |
|         },
 | |
|         value: [
 | |
|           {
 | |
|             slot: 0,
 | |
|             confirmations: 11,
 | |
|             status: {Err: expectedErr},
 | |
|             err: expectedErr,
 | |
|           },
 | |
|         ],
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const response = (await connection.getSignatureStatus(signature)).value;
 | |
|   verifySignatureStatus(response, expectedErr);
 | |
| });
 | |
| 
 | |
| test('transaction', async () => {
 | |
|   const accountFrom = new Account();
 | |
|   const accountTo = new Account();
 | |
|   const connection = new Connection(url, 'recent');
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getMinimumBalanceForRentExemption',
 | |
|       params: [0, {commitment: 'recent'}],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: 50,
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const minimumAmount = await connection.getMinimumBalanceForRentExemption(
 | |
|     0,
 | |
|     'recent',
 | |
|   );
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'requestAirdrop',
 | |
|       params: [accountFrom.publicKey.toBase58(), minimumAmount + 100010],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result:
 | |
|         '2WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
 | |
|     },
 | |
|   ]);
 | |
|   const airdropFromSig = await connection.requestAirdrop(
 | |
|     accountFrom.publicKey,
 | |
|     minimumAmount + 100010,
 | |
|   );
 | |
| 
 | |
|   mockConfirmTransaction(airdropFromSig);
 | |
|   await connection.confirmTransaction(airdropFromSig, 'single');
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getBalance',
 | |
|       params: [accountFrom.publicKey.toBase58(), {commitment: 'recent'}],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         context: {
 | |
|           slot: 11,
 | |
|         },
 | |
|         value: minimumAmount + 100010,
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
|   expect(await connection.getBalance(accountFrom.publicKey)).toBe(
 | |
|     minimumAmount + 100010,
 | |
|   );
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'requestAirdrop',
 | |
|       params: [accountTo.publicKey.toBase58(), minimumAmount],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result:
 | |
|         '8WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const airdropToSig = await connection.requestAirdrop(
 | |
|     accountTo.publicKey,
 | |
|     minimumAmount,
 | |
|   );
 | |
| 
 | |
|   mockConfirmTransaction(airdropToSig);
 | |
|   await connection.confirmTransaction(airdropToSig, 'single');
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getBalance',
 | |
|       params: [accountTo.publicKey.toBase58(), {commitment: 'recent'}],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         context: {
 | |
|           slot: 11,
 | |
|         },
 | |
|         value: minimumAmount,
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   expect(await connection.getBalance(accountTo.publicKey)).toBe(minimumAmount);
 | |
| 
 | |
|   mockGetRecentBlockhash('max');
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'sendTransaction',
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result:
 | |
|         '1WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const transaction = new Transaction().add(
 | |
|     SystemProgram.transfer({
 | |
|       fromPubkey: accountFrom.publicKey,
 | |
|       toPubkey: accountTo.publicKey,
 | |
|       lamports: 10,
 | |
|     }),
 | |
|   );
 | |
|   const signature = await connection.sendTransaction(
 | |
|     transaction,
 | |
|     [accountFrom],
 | |
|     {skipPreflight: true},
 | |
|   );
 | |
| 
 | |
|   mockConfirmTransaction(signature);
 | |
|   let confirmResult = (await connection.confirmTransaction(signature, 'single'))
 | |
|     .value;
 | |
|   expect(confirmResult.err).toBeNull();
 | |
| 
 | |
|   mockGetRecentBlockhash('max');
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'sendTransaction',
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result:
 | |
|         '2WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   // Send again and ensure that new blockhash is used
 | |
|   const lastFetch = Date.now();
 | |
|   const transaction2 = new Transaction().add(
 | |
|     SystemProgram.transfer({
 | |
|       fromPubkey: accountFrom.publicKey,
 | |
|       toPubkey: accountTo.publicKey,
 | |
|       lamports: 10,
 | |
|     }),
 | |
|   );
 | |
|   const signature2 = await connection.sendTransaction(
 | |
|     transaction2,
 | |
|     [accountFrom],
 | |
|     {skipPreflight: true},
 | |
|   );
 | |
|   expect(signature).not.toEqual(signature2);
 | |
|   expect(transaction.recentBlockhash).not.toEqual(transaction2.recentBlockhash);
 | |
| 
 | |
|   mockConfirmTransaction(signature2);
 | |
|   await connection.confirmTransaction(signature2, 'single');
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'sendTransaction',
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result:
 | |
|         '3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   // Send new transaction and ensure that same blockhash is used
 | |
|   const transaction3 = new Transaction().add(
 | |
|     SystemProgram.transfer({
 | |
|       fromPubkey: accountFrom.publicKey,
 | |
|       toPubkey: accountTo.publicKey,
 | |
|       lamports: 9,
 | |
|     }),
 | |
|   );
 | |
|   const signature3 = await connection.sendTransaction(
 | |
|     transaction3,
 | |
|     [accountFrom],
 | |
|     {
 | |
|       skipPreflight: true,
 | |
|     },
 | |
|   );
 | |
|   expect(transaction2.recentBlockhash).toEqual(transaction3.recentBlockhash);
 | |
| 
 | |
|   mockConfirmTransaction(signature3);
 | |
|   await connection.confirmTransaction(signature3, 'single');
 | |
| 
 | |
|   // Sleep until blockhash cache times out
 | |
|   await sleep(
 | |
|     Math.max(0, 1000 + BLOCKHASH_CACHE_TIMEOUT_MS - (Date.now() - lastFetch)),
 | |
|   );
 | |
| 
 | |
|   mockGetRecentBlockhash('max');
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'sendTransaction',
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result:
 | |
|         '3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   const transaction4 = new Transaction().add(
 | |
|     SystemProgram.transfer({
 | |
|       fromPubkey: accountFrom.publicKey,
 | |
|       toPubkey: accountTo.publicKey,
 | |
|       lamports: 13,
 | |
|     }),
 | |
|   );
 | |
| 
 | |
|   const signature4 = await connection.sendTransaction(
 | |
|     transaction4,
 | |
|     [accountFrom],
 | |
|     {
 | |
|       skipPreflight: true,
 | |
|     },
 | |
|   );
 | |
|   mockConfirmTransaction(signature4);
 | |
|   await connection.confirmTransaction(signature4, 'single');
 | |
| 
 | |
|   expect(transaction4.recentBlockhash).not.toEqual(
 | |
|     transaction3.recentBlockhash,
 | |
|   );
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getBalance',
 | |
|       params: [accountFrom.publicKey.toBase58(), {commitment: 'recent'}],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         context: {
 | |
|           slot: 11,
 | |
|         },
 | |
|         value: minimumAmount + 2,
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
| 
 | |
|   // accountFrom may have less than 100000 due to transaction fees
 | |
|   const balance = await connection.getBalance(accountFrom.publicKey);
 | |
|   expect(balance).toBeGreaterThan(0);
 | |
|   expect(balance).toBeLessThanOrEqual(minimumAmount + 100000);
 | |
| 
 | |
|   mockRpc.push([
 | |
|     url,
 | |
|     {
 | |
|       method: 'getBalance',
 | |
|       params: [accountTo.publicKey.toBase58(), {commitment: 'recent'}],
 | |
|     },
 | |
|     {
 | |
|       error: null,
 | |
|       result: {
 | |
|         context: {
 | |
|           slot: 11,
 | |
|         },
 | |
|         value: minimumAmount + 42,
 | |
|       },
 | |
|     },
 | |
|   ]);
 | |
|   expect(await connection.getBalance(accountTo.publicKey)).toBe(
 | |
|     minimumAmount + 42,
 | |
|   );
 | |
| });
 | |
| 
 | |
| test('multi-instruction transaction', async () => {
 | |
|   if (mockRpcEnabled) {
 | |
|     console.log('non-live test skipped');
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   const accountFrom = new Account();
 | |
|   const accountTo = new Account();
 | |
|   const connection = new Connection(url, 'recent');
 | |
| 
 | |
|   let signature = await connection.requestAirdrop(
 | |
|     accountFrom.publicKey,
 | |
|     LAMPORTS_PER_SOL,
 | |
|   );
 | |
|   await connection.confirmTransaction(signature, 'single');
 | |
|   expect(await connection.getBalance(accountFrom.publicKey)).toBe(
 | |
|     LAMPORTS_PER_SOL,
 | |
|   );
 | |
| 
 | |
|   const minimumAmount = await connection.getMinimumBalanceForRentExemption(
 | |
|     0,
 | |
|     'recent',
 | |
|   );
 | |
| 
 | |
|   signature = await connection.requestAirdrop(
 | |
|     accountTo.publicKey,
 | |
|     minimumAmount + 21,
 | |
|   );
 | |
|   await connection.confirmTransaction(signature, 'single');
 | |
|   expect(await connection.getBalance(accountTo.publicKey)).toBe(
 | |
|     minimumAmount + 21,
 | |
|   );
 | |
| 
 | |
|   // 1. Move(accountFrom, accountTo)
 | |
|   // 2. Move(accountTo, accountFrom)
 | |
|   const transaction = new Transaction()
 | |
|     .add(
 | |
|       SystemProgram.transfer({
 | |
|         fromPubkey: accountFrom.publicKey,
 | |
|         toPubkey: accountTo.publicKey,
 | |
|         lamports: 100,
 | |
|       }),
 | |
|     )
 | |
|     .add(
 | |
|       SystemProgram.transfer({
 | |
|         fromPubkey: accountTo.publicKey,
 | |
|         toPubkey: accountFrom.publicKey,
 | |
|         lamports: 100,
 | |
|       }),
 | |
|     );
 | |
|   signature = await connection.sendTransaction(
 | |
|     transaction,
 | |
|     [accountFrom, accountTo],
 | |
|     {skipPreflight: true},
 | |
|   );
 | |
| 
 | |
|   await connection.confirmTransaction(signature, 'single');
 | |
| 
 | |
|   const response = (await connection.getSignatureStatus(signature)).value;
 | |
|   if (response !== null) {
 | |
|     expect(typeof response.slot).toEqual('number');
 | |
|     expect(response.err).toBeNull();
 | |
|   } 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);
 | |
|   expect(
 | |
|     await connection.getBalance(accountFrom.publicKey),
 | |
|   ).toBeLessThanOrEqual(LAMPORTS_PER_SOL);
 | |
| 
 | |
|   expect(await connection.getBalance(accountTo.publicKey)).toBe(
 | |
|     minimumAmount + 21,
 | |
|   );
 | |
| });
 | |
| 
 | |
| test('account change notification', async () => {
 | |
|   if (mockRpcEnabled) {
 | |
|     console.log('non-live test skipped');
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   const connection = new Connection(url, 'recent');
 | |
|   const owner = new Account();
 | |
|   const programAccount = new Account();
 | |
| 
 | |
|   const mockCallback = jest.fn();
 | |
| 
 | |
|   const subscriptionId = connection.onAccountChange(
 | |
|     programAccount.publicKey,
 | |
|     mockCallback,
 | |
|     'recent',
 | |
|   );
 | |
| 
 | |
|   const balanceNeeded = Math.max(
 | |
|     await connection.getMinimumBalanceForRentExemption(0),
 | |
|     1,
 | |
|   );
 | |
| 
 | |
|   await connection.requestAirdrop(owner.publicKey, LAMPORTS_PER_SOL);
 | |
|   try {
 | |
|     const transaction = new Transaction().add(
 | |
|       SystemProgram.transfer({
 | |
|         fromPubkey: owner.publicKey,
 | |
|         toPubkey: programAccount.publicKey,
 | |
|         lamports: balanceNeeded,
 | |
|       }),
 | |
|     );
 | |
|     await sendAndConfirmTransaction(connection, transaction, [owner], {
 | |
|       commitment: 'single',
 | |
|       skipPreflight: true,
 | |
|     });
 | |
|   } catch (err) {
 | |
|     await connection.removeAccountChangeListener(subscriptionId);
 | |
|     throw err;
 | |
|   }
 | |
| 
 | |
|   // Wait for mockCallback to receive a call
 | |
|   let i = 0;
 | |
|   for (;;) {
 | |
|     if (mockCallback.mock.calls.length > 0) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     if (++i === 30) {
 | |
|       throw new Error('Account change notification not observed');
 | |
|     }
 | |
|     // Sleep for a 1/4 of a slot, notifications only occur after a block is
 | |
|     // processed
 | |
|     await sleep((250 * DEFAULT_TICKS_PER_SLOT) / NUM_TICKS_PER_SECOND);
 | |
|   }
 | |
| 
 | |
|   await connection.removeAccountChangeListener(subscriptionId);
 | |
| 
 | |
|   expect(mockCallback.mock.calls[0][0].lamports).toBe(balanceNeeded);
 | |
|   expect(mockCallback.mock.calls[0][0].owner).toEqual(SystemProgram.programId);
 | |
| });
 | |
| 
 | |
| test('program account change notification', async () => {
 | |
|   if (mockRpcEnabled) {
 | |
|     console.log('non-live test skipped');
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   const connection = new Connection(url, 'recent');
 | |
|   const owner = new Account();
 | |
|   const programAccount = new Account();
 | |
| 
 | |
|   // const mockCallback = jest.fn();
 | |
| 
 | |
|   const balanceNeeded = await connection.getMinimumBalanceForRentExemption(0);
 | |
| 
 | |
|   let notified = false;
 | |
|   const subscriptionId = connection.onProgramAccountChange(
 | |
|     SystemProgram.programId,
 | |
|     keyedAccountInfo => {
 | |
|       if (keyedAccountInfo.accountId !== programAccount.publicKey.toString()) {
 | |
|         //console.log('Ignoring another account', keyedAccountInfo);
 | |
|         return;
 | |
|       }
 | |
|       expect(keyedAccountInfo.accountInfo.lamports).toBe(balanceNeeded);
 | |
|       expect(keyedAccountInfo.accountInfo.owner).toEqual(
 | |
|         SystemProgram.programId,
 | |
|       );
 | |
|       notified = true;
 | |
|     },
 | |
|   );
 | |
| 
 | |
|   await connection.requestAirdrop(owner.publicKey, LAMPORTS_PER_SOL);
 | |
|   try {
 | |
|     const transaction = new Transaction().add(
 | |
|       SystemProgram.transfer({
 | |
|         fromPubkey: owner.publicKey,
 | |
|         toPubkey: programAccount.publicKey,
 | |
|         lamports: balanceNeeded,
 | |
|       }),
 | |
|     );
 | |
|     await sendAndConfirmTransaction(connection, transaction, [owner], {
 | |
|       commitment: 'single',
 | |
|       skipPreflight: true,
 | |
|     });
 | |
|   } catch (err) {
 | |
|     await connection.removeProgramAccountChangeListener(subscriptionId);
 | |
|     throw err;
 | |
|   }
 | |
| 
 | |
|   // Wait for mockCallback to receive a call
 | |
|   let i = 0;
 | |
|   while (!notified) {
 | |
|     //for (;;) {
 | |
|     if (++i === 30) {
 | |
|       throw new Error('Program change notification not observed');
 | |
|     }
 | |
|     // Sleep for a 1/4 of a slot, notifications only occur after a block is
 | |
|     // processed
 | |
|     await sleep((250 * DEFAULT_TICKS_PER_SLOT) / NUM_TICKS_PER_SECOND);
 | |
|   }
 | |
| 
 | |
|   await connection.removeProgramAccountChangeListener(subscriptionId);
 | |
| });
 | |
| 
 | |
| test('slot notification', async () => {
 | |
|   if (mockRpcEnabled) {
 | |
|     console.log('non-live test skipped');
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   const connection = new Connection(url, 'recent');
 | |
| 
 | |
|   let notified = false;
 | |
|   const subscriptionId = connection.onSlotChange(slotInfo => {
 | |
|     expect(slotInfo.parent).toBeDefined();
 | |
|     expect(slotInfo.slot).toBeDefined();
 | |
|     expect(slotInfo.root).toBeDefined();
 | |
|     expect(slotInfo.slot).toBeGreaterThan(slotInfo.parent);
 | |
|     expect(slotInfo.slot).toBeGreaterThanOrEqual(slotInfo.root);
 | |
|     notified = true;
 | |
|   });
 | |
| 
 | |
|   // Wait for mockCallback to receive a call
 | |
|   let i = 0;
 | |
|   while (!notified) {
 | |
|     if (++i === 30) {
 | |
|       throw new Error('Slot change notification not observed');
 | |
|     }
 | |
|     // Sleep for a 1/4 of a slot, notifications only occur after a block is
 | |
|     // processed
 | |
|     await sleep((250 * DEFAULT_TICKS_PER_SLOT) / NUM_TICKS_PER_SECOND);
 | |
|   }
 | |
| 
 | |
|   await connection.removeSlotChangeListener(subscriptionId);
 | |
| });
 | |
| 
 | |
| test('root notification', async () => {
 | |
|   if (mockRpcEnabled) {
 | |
|     console.log('non-live test skipped');
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   const connection = new Connection(url, 'recent');
 | |
| 
 | |
|   let roots = [];
 | |
|   const subscriptionId = connection.onRootChange(root => {
 | |
|     roots.push(root);
 | |
|   });
 | |
| 
 | |
|   // Wait for mockCallback to receive a call
 | |
|   let i = 0;
 | |
|   while (roots.length < 2) {
 | |
|     if (++i === 30) {
 | |
|       throw new Error('Root change notification not observed');
 | |
|     }
 | |
|     // Sleep for a 1/4 of a slot, notifications only occur after a block is
 | |
|     // processed
 | |
|     await sleep((250 * DEFAULT_TICKS_PER_SLOT) / NUM_TICKS_PER_SECOND);
 | |
|   }
 | |
| 
 | |
|   expect(roots[1]).toBeGreaterThan(roots[0]);
 | |
|   await connection.removeRootChangeListener(subscriptionId);
 | |
| });
 | |
| 
 | |
| test('https request', async () => {
 | |
|   if (mockRpcEnabled) {
 | |
|     console.log('non-live test skipped');
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   const connection = new Connection('https://devnet.solana.com');
 | |
|   const version = await connection.getVersion();
 | |
|   expect(version['solana-core']).toBeTruthy();
 | |
| });
 |