solana/web3.js/test/connection.test.js

2589 lines
63 KiB
JavaScript
Raw Normal View History

2018-08-23 10:52:48 -07:00
// @flow
import bs58 from 'bs58';
2020-07-30 21:33:54 -07:00
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';
2018-10-10 14:00:59 -07:00
import {mockRpc, mockRpcEnabled} from './__mocks__/node-fetch';
2019-03-04 08:06:33 -08:00
import {mockGetRecentBlockhash} from './mockrpc/get-recent-blockhash';
import {url} from './url';
import {sleep} from '../src/util/sleep';
2020-06-11 00:00:59 -07:00
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';
2018-08-23 10:52:48 -07:00
// Testing tokens and blockhash cache each take around 30s to complete
2020-06-11 00:00:59 -07:00
jest.setTimeout(40000);
2018-10-10 14:00:59 -07:00
const errorMessage = 'Invalid';
2018-08-24 11:12:48 -07:00
const errorResponse = {
error: {
code: -32602,
2018-08-24 11:12:48 -07:00
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') {
2020-06-11 00:00:59 -07:00
expect(confirmations).toBeGreaterThanOrEqual(0);
} else {
expect(confirmations).toBeNull();
}
return status;
};
test('get account info - not found', async () => {
2018-09-20 15:08:52 -07:00
const account = new Account();
const connection = new Connection(url);
mockRpc.push([
url,
{
method: 'getAccountInfo',
params: [account.publicKey.toBase58(), {encoding: 'base64'}],
2018-09-20 15:08:52 -07:00
},
{
error: null,
result: {
context: {
slot: 11,
},
value: null,
},
},
2018-09-20 15:08:52 -07:00
]);
expect(await connection.getAccountInfo(account.publicKey)).toBeNull();
2020-08-06 08:47:22 -07:00
if (!mockRpcEnabled) {
expect(
(await connection.getParsedAccountInfo(account.publicKey)).value,
).toBeNull();
}
2018-09-20 15:08:52 -07:00
});
test('get program accounts', async () => {
const connection = new Connection(url, 'recent');
const account0 = new Account();
const account1 = new Account();
const programId = new Account();
2020-01-16 12:09:21 -08:00
mockRpc.push([
url,
{
method: 'requestAirdrop',
params: [account0.publicKey.toBase58(), LAMPORTS_PER_SOL],
2020-01-16 12:09:21 -08:00
},
{
error: null,
result:
'2WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
2020-01-16 12:09:21 -08:00
},
]);
mockRpc.push([
url,
{
method: 'requestAirdrop',
params: [account1.publicKey.toBase58(), 0.5 * LAMPORTS_PER_SOL],
2020-01-16 12:09:21 -08:00
},
{
error: null,
result:
'2WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
2020-01-16 12:09:21 -08:00
},
]);
await connection.requestAirdrop(account0.publicKey, LAMPORTS_PER_SOL);
await connection.requestAirdrop(account1.publicKey, 0.5 * LAMPORTS_PER_SOL);
2020-06-03 04:55:42 -07:00
mockGetRecentBlockhash('max');
2020-01-16 12:09:21 -08:00
mockRpc.push([
url,
{
method: 'sendTransaction',
},
{
error: null,
result:
'3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
},
]);
let transaction = new Transaction().add(
SystemProgram.assign({
accountPubkey: account0.publicKey,
programId: programId.publicKey,
}),
);
mockConfirmTransaction(
'3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
);
2020-06-03 04:55:42 -07:00
await sendAndConfirmTransaction(connection, transaction, [account0], {
commitment: 'single',
2020-06-03 04:55:42 -07:00
skipPreflight: true,
});
2020-01-16 12:09:21 -08:00
mockRpc.push([
url,
{
method: 'sendTransaction',
},
{
error: null,
result:
'3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
},
]);
transaction = new Transaction().add(
SystemProgram.assign({
accountPubkey: account1.publicKey,
programId: programId.publicKey,
}),
);
mockConfirmTransaction(
'3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
);
2020-06-03 04:55:42 -07:00
await sendAndConfirmTransaction(connection, transaction, [account1], {
commitment: 'single',
2020-06-03 04:55:42 -07:00
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) {
expect(transaction.recentBlockhash).toBeTruthy();
return;
}
const feeCalculator = (
await connection.getFeeCalculatorForBlockhash(transaction.recentBlockhash)
).value;
if (feeCalculator === null) {
expect(feeCalculator).not.toBeNull();
return;
}
2020-01-16 12:09:21 -08:00
mockRpc.push([
url,
{
method: 'getProgramAccounts',
params: [
programId.publicKey.toBase58(),
{commitment: 'recent', encoding: 'base64'},
],
2020-01-16 12:09:21 -08:00
},
{
error: null,
result: [
{
account: {
data: ['', 'base64'],
2020-01-16 12:09:21 -08:00
executable: false,
lamports: LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
owner: programId.publicKey.toBase58(),
rentEpoch: 20,
},
pubkey: account0.publicKey.toBase58(),
},
{
account: {
data: ['', 'base64'],
2020-01-16 12:09:21 -08:00
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);
2020-03-23 21:03:26 -07:00
programAccounts.forEach(function (element) {
2020-08-06 08:47:22 -07:00
if (element.pubkey.equals(account0.publicKey)) {
expect(element.account.lamports).toBe(
LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
2019-10-22 16:10:21 -07:00
);
2020-08-06 08:47:22 -07:00
} else if (element.pubkey.equals(account1.publicKey)) {
expect(element.account.lamports).toBe(
0.5 * LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
2019-10-22 16:10:21 -07:00
);
2020-08-06 08:47:22 -07:00
} else {
expect(element.pubkey.equals(account1.publicKey)).toBe(true);
}
});
2020-08-06 08:47:22 -07:00
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 () => {
2019-04-24 15:22:50 -07:00
if (!mockRpcEnabled) {
console.log('validatorExit skipped on live node');
2019-04-24 15:22:50 -07:00
return;
}
const connection = new Connection(url);
mockRpc.push([
url,
{
method: 'validatorExit',
2019-04-24 15:22:50 -07:00
},
{
error: null,
result: false,
},
]);
const result = await connection.validatorExit();
2019-04-24 15:22:50 -07:00
expect(result).toBe(false);
});
2018-08-24 10:39:51 -07:00
test('get balance', async () => {
2018-08-23 10:52:48 -07:00
const account = new Account();
const connection = new Connection(url);
2018-08-24 10:39:51 -07:00
mockRpc.push([
url,
{
method: 'getBalance',
2018-09-30 18:42:45 -07:00
params: [account.publicKey.toBase58()],
2018-08-24 10:39:51 -07:00
},
{
error: null,
2020-01-08 12:59:58 -08:00
result: {
context: {
slot: 11,
},
value: 0,
},
2018-11-04 11:41:21 -08:00
},
2018-08-24 10:39:51 -07:00
]);
2018-08-23 10:52:48 -07:00
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);
}
});
2019-10-23 06:48:24 -07:00
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,
2019-10-23 06:48:24 -07:00
warmup: true,
},
},
]);
const epochSchedule = await connection.getEpochSchedule();
for (const key of [
'firstNormalEpoch',
'firstNormalSlot',
'leaderScheduleSlotOffset',
'slotsPerEpoch',
2019-10-23 06:48:24 -07:00
]) {
2020-02-25 11:30:08 -08:00
expect(epochSchedule).toHaveProperty('warmup');
2019-10-23 06:48:24 -07:00
expect(epochSchedule).toHaveProperty(key);
2020-02-25 11:30:08 -08:00
if (epochSchedule.warmup) {
expect(epochSchedule[key]).toBeGreaterThan(0);
}
2019-10-23 06:48:24 -07:00
}
});
2020-07-17 08:16:44 -07:00
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);
}
});
2019-08-02 16:06:54 -07:00
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,
2020-05-13 08:14:03 -07:00
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();
2019-09-26 15:00:54 -07:00
expect(
voteAccounts.current.concat(voteAccounts.delinquent).length,
).toBeGreaterThan(0);
});
test('confirm transaction - error', async () => {
2018-08-23 10:52:48 -07:00
const connection = new Connection(url);
2018-08-24 10:39:51 -07:00
const badTransactionSignature = 'bad transaction signature';
await expect(
2018-11-04 11:41:21 -08:00
connection.confirmTransaction(badTransactionSignature),
).rejects.toThrow('signature must be base58 encoded');
2018-09-26 19:16:17 -07:00
mockRpc.push([
url,
{
method: 'getSignatureStatuses',
2020-03-23 08:01:12 -07:00
params: [[badTransactionSignature]],
2018-09-26 19:16:17 -07:00
},
errorResponse,
2018-11-04 11:41:21 -08:00
]);
2018-09-26 19:16:17 -07:00
await expect(
2018-11-04 11:41:21 -08:00
connection.getSignatureStatus(badTransactionSignature),
).rejects.toThrow(errorMessage);
2018-08-23 10:52:48 -07:00
});
2018-08-24 10:39:51 -07:00
test('get transaction count', async () => {
2018-08-23 10:52:48 -07:00
const connection = new Connection(url);
2018-08-24 10:39:51 -07:00
mockRpc.push([
url,
{
method: 'getTransactionCount',
params: [],
},
{
error: null,
result: 1000000,
2018-11-04 11:41:21 -08:00
},
]);
2018-08-24 10:39:51 -07:00
2018-08-23 10:52:48 -07:00
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();
});
2019-11-16 08:28:14 -08:00
test('get confirmed block', async () => {
2019-11-12 08:21:19 -08:00
const connection = new Connection(url);
2020-01-16 12:09:21 -08:00
mockRpc.push([
url,
{
method: 'getSlot',
params: [],
},
{
error: null,
result: 1,
},
]);
2019-11-26 00:58:00 -08:00
while ((await connection.getSlot()) <= 0) {
continue;
}
2020-01-16 12:09:21 -08:00
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);
2020-01-16 12:09:21 -08:00
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},
2020-04-04 06:35:08 -07:00
err: null,
2020-01-16 12:09:21 -08:00
},
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();
2019-11-12 08:21:19 -08:00
});
test('get recent blockhash', async () => {
2018-08-23 10:52:48 -07:00
const connection = new Connection(url);
2020-06-03 04:55:42 -07:00
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);
}
2018-08-23 10:52:48 -07:00
});
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);
});
2020-05-18 20:27:36 -07:00
test('get block time', async () => {
const connection = new Connection(url);
mockRpc.push([
url,
{
method: 'getBlockTime',
params: [1],
2020-05-18 20:27:36 -07:00
},
{
error: null,
result: 10000,
},
]);
const blockTime = await connection.getBlockTime(1);
2020-05-18 20:27:36 -07:00
if (blockTime === null) {
// TODO: enable after https://github.com/solana-labs/solana/issues/11849 fixed
// expect(blockTime).not.toBeNull();
2020-05-18 20:27:36 -07:00
} else {
expect(blockTime).toBeGreaterThan(0);
}
});
2020-05-21 01:58:17 -07:00
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);
});
test('get performance samples', async () => {
const connection = new Connection(url);
if (mockRpcEnabled) {
mockRpc.push([
url,
{
method: 'getRecentPerformanceSamples',
params: [],
},
{
error: null,
result: [
{
slot: 1234,
numTransactions: 1000,
numSlots: 60,
samplePeriodSecs: 60,
},
],
},
]);
}
const perfSamples = await connection.getRecentPerformanceSamples();
expect(Array.isArray(perfSamples)).toBe(true);
if (perfSamples.length > 0) {
expect(perfSamples[0].slot).toBeGreaterThan(0);
expect(perfSamples[0].numTransactions).toBeGreaterThan(0);
expect(perfSamples[0].numSlots).toBeGreaterThan(0);
expect(perfSamples[0].samplePeriodSecs).toBeGreaterThan(0);
}
});
test('get performance samples limit too high', async () => {
const connection = new Connection(url);
if (mockRpcEnabled) {
mockRpc.push([
url,
{
method: 'getRecentPerformanceSamples',
params: [100000],
},
{
error: {
code: -32602,
message: 'Invalid limit; max 720',
},
result: null,
},
]);
}
await expect(
connection.getRecentPerformanceSamples(100000),
).rejects.toThrow();
});
2020-07-30 21:33:54 -07:00
const TOKEN_PROGRAM_ID = new PublicKey(
2020-09-01 10:58:40 -07:00
'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
2020-07-30 21:33:54 -07:00
);
describe('token methods', () => {
2020-07-30 21:33:54 -07:00
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',
);
2020-07-30 21:33:54 -07:00
const mintOwner = new Account();
const accountOwner = new Account();
2020-09-01 10:58:40 -07:00
const token = await Token.createMint(
connection,
payerAccount,
mintOwner.publicKey,
2020-09-01 10:58:40 -07:00
null,
2,
TOKEN_PROGRAM_ID,
);
2020-07-30 21:33:54 -07:00
2020-09-01 10:58:40 -07:00
const tokenAccount = await token.createAccount(accountOwner.publicKey);
await token.mintTo(tokenAccount, mintOwner, [], 11111);
const token2 = await Token.createMint(
connection,
payerAccount,
mintOwner.publicKey,
2020-09-01 10:58:40 -07:00
null,
2,
TOKEN_PROGRAM_ID,
);
2020-07-30 21:33:54 -07:00
2020-09-01 10:58:40 -07:00
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),
);
2020-07-30 21:33:54 -07:00
await connection.confirmTransaction(testSignature);
2020-07-30 21:33:54 -07:00
testOwner = accountOwner;
testToken = token;
testTokenAccount = tokenAccount;
});
2020-07-30 21:33:54 -07:00
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');
2020-07-30 21:33:54 -07:00
await expect(connection.getTokenSupply(newAccount)).rejects.toThrow();
});
2020-07-30 21:33:54 -07:00
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);
}
2020-07-30 21:33:54 -07:00
const missingSignature =
'45pGoC4Rr3fJ1TKrsiRkhHRbdUeX7633XAGVec6XzVdpRbzQgHhe6ZC6Uq164MPWtiqMg7wCkC6Wy3jy2BqsDEKf';
const nullResponse = await connection.getParsedConfirmedTransaction(
missingSignature,
);
2020-07-30 21:33:54 -07:00
expect(nullResponse).toBeNull();
});
2020-07-30 21:33:54 -07:00
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();
});
2020-08-06 08:47:22 -07:00
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();
});
2020-07-30 21:33:54 -07:00
});
2020-05-22 10:23:29 -07:00
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();
});
2019-11-11 17:09:00 -08:00
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();
});
2018-08-24 10:39:51 -07:00
test('request airdrop', async () => {
2018-08-23 16:39:52 -07:00
const account = new Account();
const connection = new Connection(url, 'recent');
2018-08-23 16:39:52 -07:00
mockRpc.push([
url,
{
method: 'getMinimumBalanceForRentExemption',
params: [0, {commitment: 'recent'}],
},
{
error: null,
result: 50,
},
]);
const minimumAmount = await connection.getMinimumBalanceForRentExemption(
0,
'recent',
);
2018-08-24 10:39:51 -07:00
mockRpc.push([
url,
{
method: 'requestAirdrop',
params: [account.publicKey.toBase58(), minimumAmount + 42],
2018-08-24 10:39:51 -07:00
},
{
error: null,
2018-11-04 11:41:21 -08:00
result:
'1WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
},
2018-08-24 10:39:51 -07:00
]);
const signature = await connection.requestAirdrop(
account.publicKey,
minimumAmount + 42,
);
mockConfirmTransaction(signature);
await connection.confirmTransaction(signature, 'single');
2018-08-24 10:39:51 -07:00
mockRpc.push([
url,
{
method: 'getBalance',
params: [account.publicKey.toBase58(), {commitment: 'recent'}],
2018-08-24 10:39:51 -07:00
},
{
error: null,
2020-01-08 12:59:58 -08:00
result: {
context: {
slot: 11,
},
value: minimumAmount + 42,
2020-01-08 12:59:58 -08:00
},
2018-11-04 11:41:21 -08:00
},
2018-08-24 10:39:51 -07:00
]);
2018-08-23 16:39:52 -07:00
const balance = await connection.getBalance(account.publicKey);
expect(balance).toBe(minimumAmount + 42);
2018-09-20 15:08:52 -07:00
mockRpc.push([
url,
{
method: 'getAccountInfo',
params: [
account.publicKey.toBase58(),
{commitment: 'recent', encoding: 'base64'},
],
2018-09-20 15:08:52 -07:00
},
{
error: null,
result: {
2020-01-08 12:59:58 -08:00
context: {
slot: 11,
},
value: {
owner: '11111111111111111111111111111111',
lamports: minimumAmount + 42,
data: ['', 'base64'],
2020-01-08 12:59:58 -08:00
executable: false,
},
2018-11-04 11:41:21 -08:00
},
},
2018-09-20 15:08:52 -07:00
]);
const accountInfo = await connection.getAccountInfo(account.publicKey);
if (accountInfo === null) {
expect(accountInfo).not.toBeNull();
return;
}
expect(accountInfo.lamports).toBe(minimumAmount + 42);
2019-03-14 13:27:47 -07:00
expect(accountInfo.data).toHaveLength(0);
expect(accountInfo.owner).toEqual(SystemProgram.programId);
2020-08-06 08:47:22 -07:00
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'],
2020-08-06 08:47:22 -07:00
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);
2018-08-24 11:12:48 -07:00
});
2020-04-04 23:54:48 -07:00
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],
2020-04-04 23:54:48 -07:00
},
{
error: null,
result:
'2WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
2020-04-04 23:54:48 -07:00
},
]);
2020-07-22 06:58:04 -07:00
const airdropSignature = await connection.requestAirdrop(
account.publicKey,
minimumAmount + 100010,
);
mockConfirmTransaction(airdropSignature);
await connection.confirmTransaction(airdropSignature, 'single');
2020-07-22 06:58:04 -07:00
2020-04-04 23:54:48 -07:00
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,
);
2020-06-03 04:55:42 -07:00
mockGetRecentBlockhash('max');
2020-04-04 23:54:48 -07:00
mockRpc.push([
url,
{
method: 'sendTransaction',
},
{
error: null,
result:
'3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
},
]);
2020-06-03 04:55:42 -07:00
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},
);
2020-06-03 04:55:42 -07:00
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},
);
2020-04-04 23:54:48 -07:00
2020-06-03 04:55:42 -07:00
const expectedErr = {InstructionError: [0, {Custom: 0}]};
mockRpcSocket.push([
2020-04-04 23:54:48 -07:00
{
method: 'signatureSubscribe',
params: [signature, {commitment: 'single'}],
2020-04-04 23:54:48 -07:00
},
{
context: {
slot: 11,
2020-04-04 23:54:48 -07:00
},
value: {err: expectedErr},
2020-04-04 23:54:48 -07:00
},
]);
// Wait for one confirmation
const confirmResult = (
await connection.confirmTransaction(signature, 'single')
).value;
expect(confirmResult.err).toEqual(expectedErr);
2020-04-04 23:54:48 -07:00
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);
2020-04-04 23:54:48 -07:00
});
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',
);
2018-08-24 10:39:51 -07:00
mockRpc.push([
url,
{
method: 'requestAirdrop',
params: [accountFrom.publicKey.toBase58(), minimumAmount + 100010],
2018-08-24 10:39:51 -07:00
},
{
error: null,
2018-11-04 11:41:21 -08:00
result:
'2WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
2018-11-04 11:41:21 -08:00
},
2018-08-24 10:39:51 -07:00
]);
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,
2020-01-08 12:59:58 -08:00
result: {
context: {
slot: 11,
},
value: minimumAmount + 100010,
2020-01-08 12:59:58 -08:00
},
2018-11-04 11:41:21 -08:00
},
]);
expect(await connection.getBalance(accountFrom.publicKey)).toBe(
minimumAmount + 100010,
);
2018-08-24 10:39:51 -07:00
mockRpc.push([
url,
{
method: 'requestAirdrop',
2020-06-11 00:00:59 -07:00
params: [accountTo.publicKey.toBase58(), minimumAmount],
},
{
error: null,
2018-11-04 11:41:21 -08:00
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,
2020-01-08 12:59:58 -08:00
result: {
context: {
slot: 11,
},
2020-06-11 00:00:59 -07:00
value: minimumAmount,
2020-01-08 12:59:58 -08:00
},
2018-11-04 11:41:21 -08:00
},
]);
2020-06-11 00:00:59 -07:00
expect(await connection.getBalance(accountTo.publicKey)).toBe(minimumAmount);
2018-08-24 10:39:51 -07:00
2020-06-03 04:55:42 -07:00
mockGetRecentBlockhash('max');
mockRpc.push([
url,
{
method: 'sendTransaction',
},
{
error: null,
2018-11-04 11:41:21 -08:00
result:
2020-06-11 00:00:59 -07:00
'1WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
2018-11-04 11:41:21 -08:00
},
]);
2018-09-14 08:27:40 -07:00
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();
2020-06-11 00:00:59 -07:00
mockGetRecentBlockhash('max');
2018-09-26 19:16:17 -07:00
mockRpc.push([
url,
{
2020-06-11 00:00:59 -07:00
method: 'sendTransaction',
2018-09-26 19:16:17 -07:00
},
{
error: null,
2020-06-11 00:00:59 -07:00
result:
'2WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
2018-11-04 11:41:21 -08:00
},
]);
2020-03-23 08:01:12 -07:00
2020-06-11 00:00:59 -07:00
// 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,
}),
);
2020-06-11 00:00:59 -07:00
const signature2 = await connection.sendTransaction(
transaction2,
[accountFrom],
{skipPreflight: true},
);
2020-06-11 00:00:59 -07:00
expect(signature).not.toEqual(signature2);
expect(transaction.recentBlockhash).not.toEqual(transaction2.recentBlockhash);
2020-03-26 06:37:45 -07:00
mockConfirmTransaction(signature2);
await connection.confirmTransaction(signature2, 'single');
2020-03-23 08:01:12 -07:00
mockRpc.push([
url,
{
2020-06-11 00:00:59 -07:00
method: 'sendTransaction',
2020-03-23 08:01:12 -07:00
},
{
error: null,
2020-06-11 00:00:59 -07:00
result:
'3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
2020-03-23 08:01:12 -07:00
},
]);
2020-06-11 00:00:59 -07:00
// 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,
},
);
2020-06-11 00:00:59 -07:00
expect(transaction2.recentBlockhash).toEqual(transaction3.recentBlockhash);
mockConfirmTransaction(signature3);
await connection.confirmTransaction(signature3, 'single');
2020-06-11 00:00:59 -07:00
// 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,
}),
);
2020-06-11 00:00:59 -07:00
const signature4 = await connection.sendTransaction(
transaction4,
[accountFrom],
{
skipPreflight: true,
},
);
mockConfirmTransaction(signature4);
await connection.confirmTransaction(signature4, 'single');
2020-06-11 00:00:59 -07:00
expect(transaction4.recentBlockhash).not.toEqual(
transaction3.recentBlockhash,
);
2020-03-26 06:37:45 -07:00
mockRpc.push([
url,
{
method: 'getBalance',
params: [accountFrom.publicKey.toBase58(), {commitment: 'recent'}],
},
{
error: null,
2020-01-08 12:59:58 -08:00
result: {
context: {
slot: 11,
},
value: minimumAmount + 2,
2020-01-08 12:59:58 -08:00
},
2018-11-04 11:41:21 -08:00
},
]);
// 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);
2018-08-23 10:52:48 -07:00
mockRpc.push([
url,
{
method: 'getBalance',
params: [accountTo.publicKey.toBase58(), {commitment: 'recent'}],
},
{
error: null,
2020-01-08 12:59:58 -08:00
result: {
context: {
slot: 11,
},
2020-06-11 00:00:59 -07:00
value: minimumAmount + 42,
2020-01-08 12:59:58 -08:00
},
2018-11-04 11:41:21 -08:00
},
]);
expect(await connection.getBalance(accountTo.publicKey)).toBe(
2020-06-11 00:00:59 -07:00
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');
2020-06-11 00:00:59 -07:00
let signature = await connection.requestAirdrop(
accountFrom.publicKey,
LAMPORTS_PER_SOL,
);
await connection.confirmTransaction(signature, 'single');
2020-01-02 08:56:15 -08:00
expect(await connection.getBalance(accountFrom.publicKey)).toBe(
LAMPORTS_PER_SOL,
);
const minimumAmount = await connection.getMinimumBalanceForRentExemption(
0,
'recent',
);
2020-06-11 00:00:59 -07:00
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,
}),
);
2020-06-11 00:00:59 -07:00
signature = await connection.sendTransaction(
transaction,
[accountFrom, accountTo],
{skipPreflight: true},
);
await connection.confirmTransaction(signature, 'single');
2020-03-23 08:01:12 -07:00
2020-03-26 06:37:45 -07:00
const response = (await connection.getSignatureStatus(signature)).value;
2020-03-23 08:01:12 -07:00
if (response !== null) {
expect(typeof response.slot).toEqual('number');
2020-04-04 06:35:08 -07:00
expect(response.err).toBeNull();
2020-03-23 08:01:12 -07:00
} 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,
);
});
2018-10-26 21:37:39 -07:00
test('account change notification', async () => {
if (mockRpcEnabled) {
console.log('non-live test skipped');
return;
}
const connection = new Connection(url, 'recent');
2018-10-26 21:37:39 -07:00
const owner = new Account();
const programAccount = new Account();
const mockCallback = jest.fn();
2018-11-04 11:41:21 -08:00
const subscriptionId = connection.onAccountChange(
programAccount.publicKey,
mockCallback,
'recent',
2018-11-04 11:41:21 -08:00
);
2018-10-26 21:37:39 -07:00
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,
}),
);
2020-06-03 04:55:42 -07:00
await sendAndConfirmTransaction(connection, transaction, [owner], {
commitment: 'single',
2020-06-03 04:55:42 -07:00
skipPreflight: true,
});
} catch (err) {
await connection.removeAccountChangeListener(subscriptionId);
throw err;
}
2018-10-26 21:37:39 -07:00
// 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);
}
2018-10-26 21:37:39 -07:00
2018-11-16 19:29:14 -08:00
await connection.removeAccountChangeListener(subscriptionId);
expect(mockCallback.mock.calls[0][0].lamports).toBe(balanceNeeded);
expect(mockCallback.mock.calls[0][0].owner).toEqual(SystemProgram.programId);
2018-10-26 21:37:39 -07:00
});
2019-03-08 16:02:39 -08:00
test('program account change notification', async () => {
if (mockRpcEnabled) {
console.log('non-live test skipped');
return;
}
const connection = new Connection(url, 'recent');
2019-03-08 16:02:39 -08:00
const owner = new Account();
const programAccount = new Account();
// const mockCallback = jest.fn();
2019-03-08 16:02:39 -08:00
const balanceNeeded = await connection.getMinimumBalanceForRentExemption(0);
let notified = false;
2019-03-08 16:02:39 -08:00
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;
},
2019-03-08 16:02:39 -08:00
);
await connection.requestAirdrop(owner.publicKey, LAMPORTS_PER_SOL);
try {
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: owner.publicKey,
toPubkey: programAccount.publicKey,
lamports: balanceNeeded,
}),
);
2020-06-03 04:55:42 -07:00
await sendAndConfirmTransaction(connection, transaction, [owner], {
commitment: 'single',
2020-06-03 04:55:42 -07:00
skipPreflight: true,
});
} catch (err) {
await connection.removeProgramAccountChangeListener(subscriptionId);
throw err;
}
2019-03-08 16:02:39 -08:00
// Wait for mockCallback to receive a call
let i = 0;
while (!notified) {
//for (;;) {
if (++i === 30) {
throw new Error('Program change notification not observed');
2019-03-08 16:02:39 -08:00
}
// 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');
2020-03-27 07:22:53 -07:00
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);
2020-03-27 07:22:53 -07:00
notified = true;
});
2020-03-27 07:22:53 -07:00
// 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);
});
2020-03-27 07:22:53 -07:00
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();
});