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

3736 lines
110 KiB
TypeScript
Raw Normal View History

import bs58 from 'bs58';
2021-02-05 18:59:00 -08:00
import {Buffer} from 'buffer';
2020-07-30 21:33:54 -07:00
import {Token, u64} from '@solana/spl-token';
2021-02-05 18:59:00 -08:00
import {expect, use} from 'chai';
import chaiAsPromised from 'chai-as-promised';
import {
Account,
Authorized,
Connection,
EpochSchedule,
SystemProgram,
Transaction,
LAMPORTS_PER_SOL,
Lockup,
PublicKey,
StakeProgram,
2021-02-05 18:59:00 -08:00
sendAndConfirmTransaction,
Keypair,
Message,
} from '../src';
import invariant from '../src/util/assert';
import {DEFAULT_TICKS_PER_SLOT, NUM_TICKS_PER_SECOND} from '../src/timing';
2021-02-05 18:59:00 -08:00
import {MOCK_PORT, url} from './url';
2021-03-14 22:08:10 -07:00
import {
AccountInfo,
2021-03-14 22:08:10 -07:00
BLOCKHASH_CACHE_TIMEOUT_MS,
BlockResponse,
BlockSignatures,
2021-03-14 22:08:10 -07:00
Commitment,
ConfirmedBlock,
2021-03-14 22:08:10 -07:00
EpochInfo,
InflationGovernor,
SlotInfo,
} from '../src/connection';
2021-02-05 18:59:00 -08:00
import {sleep} from '../src/util/sleep';
import {
helpers,
mockErrorMessage,
mockErrorResponse,
mockRpcBatchResponse,
2021-02-05 18:59:00 -08:00
mockRpcResponse,
mockServer,
} from './mocks/rpc-http';
2021-02-07 08:57:12 -08:00
import {stubRpcWebSocket, restoreRpcWebSocket} from './mocks/rpc-websockets';
import type {TransactionSignature} from '../src/transaction';
import type {
SignatureStatus,
TransactionError,
KeyedAccountInfo,
} from '../src/connection';
2021-02-05 18:59:00 -08:00
use(chaiAsPromised);
2018-08-24 11:12:48 -07:00
const verifySignatureStatus = (
status: SignatureStatus | null,
err?: TransactionError,
): SignatureStatus => {
if (status === null) {
2021-02-05 18:59:00 -08:00
expect(status).not.to.be.null;
throw new Error(); // unreachable
}
const expectedErr = err || null;
2021-02-05 18:59:00 -08:00
expect(status.err).to.eql(expectedErr);
expect(status.slot).to.be.at.least(0);
if (expectedErr !== null) return status;
const confirmations = status.confirmations;
if (typeof confirmations === 'number') {
2021-02-05 18:59:00 -08:00
expect(confirmations).to.be.at.least(0);
} else {
2021-02-05 18:59:00 -08:00
expect(confirmations).to.be.null;
}
return status;
};
describe('Connection', function () {
2021-02-05 18:59:00 -08:00
let connection: Connection;
beforeEach(() => {
connection = new Connection(url);
});
2018-09-20 15:08:52 -07:00
2021-03-14 22:08:10 -07:00
if (mockServer) {
const server = mockServer;
2021-02-05 18:59:00 -08:00
beforeEach(() => {
2021-03-14 22:08:10 -07:00
server.start(MOCK_PORT);
2021-02-05 18:59:00 -08:00
stubRpcWebSocket(connection);
});
2020-08-06 08:47:22 -07:00
2021-02-05 18:59:00 -08:00
afterEach(() => {
2021-03-14 22:08:10 -07:00
server.stop();
2021-02-05 18:59:00 -08:00
restoreRpcWebSocket(connection);
});
2020-08-06 08:47:22 -07:00
}
2018-09-20 15:08:52 -07:00
if (mockServer) {
it('should pass HTTP headers to RPC', async () => {
const headers = {
Authorization: 'Bearer 123',
};
let connection = new Connection(url, {
httpHeaders: headers,
});
await mockRpcResponse({
method: 'getVersion',
params: [],
value: {'solana-core': '0.20.4'},
withHeaders: headers,
});
expect(await connection.getVersion()).to.be.not.null;
});
it('should allow middleware to augment request', async () => {
let connection = new Connection(url, {
fetchMiddleware: (url, options, fetch) => {
options.headers = Object.assign(options.headers, {
Authorization: 'Bearer 123',
});
fetch(url, options);
},
});
await mockRpcResponse({
method: 'getVersion',
params: [],
value: {'solana-core': '0.20.4'},
withHeaders: {
Authorization: 'Bearer 123',
},
});
expect(await connection.getVersion()).to.be.not.null;
});
}
it('should attribute middleware fatals to the middleware', async () => {
let connection = new Connection(url, {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
fetchMiddleware: (_url, _options, _fetch) => {
throw new Error('This middleware experienced a fatal error');
},
});
const error = await expect(connection.getVersion()).to.be.rejectedWith(
'This middleware experienced a fatal error',
);
expect(error)
.to.be.an.instanceOf(Error)
.and.to.have.property('stack')
.that.include('fetchMiddleware');
});
it('should not attribute fetch errors to the middleware', async () => {
let connection = new Connection(url, {
fetchMiddleware: (url, _options, fetch) => {
fetch(url, 'An `Object` was expected here; this is a `TypeError`.');
},
});
const error = await expect(connection.getVersion()).to.be.rejected;
expect(error)
.to.be.an.instanceOf(Error)
.and.to.have.property('stack')
.that.does.not.include('fetchMiddleware');
});
2021-02-05 18:59:00 -08:00
it('get account info - not found', async () => {
const account = Keypair.generate();
2020-12-24 10:43:45 -08:00
2021-02-05 18:59:00 -08:00
await mockRpcResponse({
method: 'getAccountInfo',
params: [account.publicKey.toBase58(), {encoding: 'base64'}],
value: null,
withContext: true,
});
2021-02-05 18:59:00 -08:00
expect(await connection.getAccountInfo(account.publicKey)).to.be.null;
2021-02-05 18:59:00 -08:00
await mockRpcResponse({
method: 'getAccountInfo',
params: [account.publicKey.toBase58(), {encoding: 'jsonParsed'}],
value: null,
withContext: true,
});
expect((await connection.getParsedAccountInfo(account.publicKey)).value).to
.be.null;
2020-06-03 04:55:42 -07:00
});
it('get multiple accounts info', async () => {
const account1 = Keypair.generate();
const account2 = Keypair.generate();
{
await helpers.airdrop({
connection,
address: account1.publicKey,
amount: LAMPORTS_PER_SOL,
});
await helpers.airdrop({
connection,
address: account2.publicKey,
amount: LAMPORTS_PER_SOL,
});
}
const value = [
{
owner: '11111111111111111111111111111111',
lamports: LAMPORTS_PER_SOL,
data: ['', 'base64'],
executable: false,
rentEpoch: 0,
},
{
owner: '11111111111111111111111111111111',
lamports: LAMPORTS_PER_SOL,
data: ['', 'base64'],
executable: false,
rentEpoch: 0,
},
];
await mockRpcResponse({
method: 'getMultipleAccounts',
params: [
[account1.publicKey.toBase58(), account2.publicKey.toBase58()],
{encoding: 'base64'},
],
value: value,
withContext: true,
});
const res = await connection.getMultipleAccountsInfo(
[account1.publicKey, account2.publicKey],
'confirmed',
);
const expectedValue = [
{
owner: new PublicKey('11111111111111111111111111111111'),
lamports: LAMPORTS_PER_SOL,
data: Buffer.from([]),
executable: false,
rentEpoch: 0,
},
{
owner: new PublicKey('11111111111111111111111111111111'),
lamports: LAMPORTS_PER_SOL,
data: Buffer.from([]),
executable: false,
rentEpoch: 0,
},
];
expect(res).to.eql(expectedValue);
});
2021-02-05 18:59:00 -08:00
it('get program accounts', async () => {
const account0 = Keypair.generate();
const account1 = Keypair.generate();
const programId = Keypair.generate();
2021-02-05 18:59:00 -08:00
2020-01-16 12:09:21 -08:00
{
2021-02-05 18:59:00 -08:00
await helpers.airdrop({
connection,
address: account0.publicKey,
amount: LAMPORTS_PER_SOL,
});
2021-02-05 18:59:00 -08:00
const transaction = new Transaction().add(
SystemProgram.assign({
accountPubkey: account0.publicKey,
programId: programId.publicKey,
}),
);
await helpers.processTransaction({
connection,
transaction,
signers: [account0],
commitment: 'confirmed',
2021-02-05 18:59:00 -08:00
});
}
{
2021-02-05 18:59:00 -08:00
await helpers.airdrop({
connection,
address: account1.publicKey,
amount: 0.5 * LAMPORTS_PER_SOL,
});
2021-02-05 18:59:00 -08:00
const transaction = new Transaction().add(
SystemProgram.assign({
accountPubkey: account1.publicKey,
programId: programId.publicKey,
}),
);
2021-02-05 18:59:00 -08:00
await helpers.processTransaction({
connection,
transaction,
signers: [account1],
commitment: 'confirmed',
2021-02-05 18:59:00 -08:00
});
}
2021-02-05 18:59:00 -08:00
const feeCalculator = (await helpers.recentBlockhash({connection}))
.feeCalculator;
{
await mockRpcResponse({
method: 'getProgramAccounts',
params: [
programId.publicKey.toBase58(),
{commitment: 'confirmed', encoding: 'base64'},
],
value: [
{
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,
{
commitment: 'confirmed',
},
);
expect(programAccounts).to.have.length(2);
programAccounts.forEach(function (keyedAccount) {
if (keyedAccount.pubkey.equals(account0.publicKey)) {
expect(keyedAccount.account.lamports).to.eq(
LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
);
} else {
expect(keyedAccount.pubkey).to.eql(account1.publicKey);
expect(keyedAccount.account.lamports).to.eq(
0.5 * LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
);
}
});
}
2020-01-16 12:09:21 -08:00
{
2021-02-05 18:59:00 -08:00
await mockRpcResponse({
method: 'getProgramAccounts',
params: [
programId.publicKey.toBase58(),
{commitment: 'confirmed', encoding: 'base64'},
2021-02-05 18:59:00 -08:00
],
value: [
{
account: {
data: ['', 'base64'],
executable: false,
lamports: LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
owner: programId.publicKey.toBase58(),
rentEpoch: 20,
},
pubkey: account0.publicKey.toBase58(),
2020-01-16 12:09:21 -08:00
},
2021-02-05 18:59:00 -08:00
{
account: {
data: ['', 'base64'],
executable: false,
lamports:
0.5 * LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
owner: programId.publicKey.toBase58(),
rentEpoch: 20,
},
pubkey: account1.publicKey.toBase58(),
2020-01-16 12:09:21 -08:00
},
2021-02-05 18:59:00 -08:00
],
});
2021-02-05 18:59:00 -08:00
const programAccounts = await connection.getProgramAccounts(
programId.publicKey,
'confirmed',
2019-10-22 16:10:21 -07:00
);
2021-02-05 18:59:00 -08:00
expect(programAccounts).to.have.length(2);
programAccounts.forEach(function (keyedAccount) {
if (keyedAccount.pubkey.equals(account0.publicKey)) {
expect(keyedAccount.account.lamports).to.eq(
LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
);
} else {
expect(keyedAccount.pubkey).to.eql(account1.publicKey);
expect(keyedAccount.account.lamports).to.eq(
0.5 * LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
);
}
});
}
{
await mockRpcResponse({
method: 'getProgramAccounts',
params: [
programId.publicKey.toBase58(),
{
commitment: 'confirmed',
encoding: 'base64',
filters: [
{
dataSize: 0,
},
],
},
],
value: [
{
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 programAccountsDoMatchFilter = await connection.getProgramAccounts(
programId.publicKey,
{
commitment: 'confirmed',
encoding: 'base64',
filters: [{dataSize: 0}],
},
);
expect(programAccountsDoMatchFilter).to.have.length(2);
}
{
await mockRpcResponse({
method: 'getProgramAccounts',
params: [
programId.publicKey.toBase58(),
{
commitment: 'confirmed',
encoding: 'base64',
filters: [
{
memcmp: {
offset: 0,
bytes: 'XzdZ3w',
},
},
],
},
],
value: [],
});
const programAccountsDontMatchFilter =
await connection.getProgramAccounts(programId.publicKey, {
commitment: 'confirmed',
filters: [
{
memcmp: {
offset: 0,
bytes: 'XzdZ3w',
},
},
],
});
expect(programAccountsDontMatchFilter).to.have.length(0);
}
2021-02-05 18:59:00 -08:00
{
await mockRpcResponse({
method: 'getProgramAccounts',
params: [
programId.publicKey.toBase58(),
{commitment: 'confirmed', encoding: 'jsonParsed'},
2021-02-05 18:59:00 -08:00
],
value: [
{
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.getParsedProgramAccounts(
programId.publicKey,
{
commitment: 'confirmed',
},
2019-10-22 16:10:21 -07:00
);
2021-02-05 18:59:00 -08:00
expect(programAccounts).to.have.length(2);
programAccounts.forEach(function (element) {
if (element.pubkey.equals(account0.publicKey)) {
expect(element.account.lamports).to.eq(
LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
);
} else {
expect(element.pubkey).to.eql(account1.publicKey);
expect(element.account.lamports).to.eq(
0.5 * LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
);
}
});
}
{
await mockRpcResponse({
method: 'getProgramAccounts',
params: [
programId.publicKey.toBase58(),
{
commitment: 'confirmed',
encoding: 'jsonParsed',
filters: [
{
dataSize: 2,
},
],
},
],
value: [],
});
const programAccountsDontMatchFilter =
await connection.getParsedProgramAccounts(programId.publicKey, {
commitment: 'confirmed',
filters: [{dataSize: 2}],
});
expect(programAccountsDontMatchFilter).to.have.length(0);
}
{
await mockRpcResponse({
method: 'getProgramAccounts',
params: [
programId.publicKey.toBase58(),
{
commitment: 'confirmed',
encoding: 'jsonParsed',
filters: [
{
memcmp: {
offset: 0,
bytes: '',
},
},
],
},
],
value: [
{
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 programAccountsDoMatchFilter =
await connection.getParsedProgramAccounts(programId.publicKey, {
commitment: 'confirmed',
filters: [
{
memcmp: {
offset: 0,
bytes: '',
},
},
],
});
expect(programAccountsDoMatchFilter).to.have.length(2);
}
2022-01-21 13:01:48 -08:00
}).timeout(30 * 1000);
2020-08-06 08:47:22 -07:00
2021-02-05 18:59:00 -08:00
it('get balance', async () => {
const account = Keypair.generate();
2018-08-23 10:52:48 -07:00
2021-02-05 18:59:00 -08:00
await mockRpcResponse({
2018-08-24 10:39:51 -07:00
method: 'getBalance',
2018-09-30 18:42:45 -07:00
params: [account.publicKey.toBase58()],
2021-02-05 18:59:00 -08:00
value: {
2020-01-08 12:59:58 -08:00
context: {
slot: 11,
},
value: 0,
},
2021-02-05 18:59:00 -08:00
});
2018-08-23 10:52:48 -07:00
2021-02-05 18:59:00 -08:00
const balance = await connection.getBalance(account.publicKey);
expect(balance).to.be.at.least(0);
});
2021-02-05 18:59:00 -08:00
it('get inflation', async () => {
await mockRpcResponse({
method: 'getInflationGovernor',
params: [],
2021-02-05 18:59:00 -08:00
value: {
foundation: 0.05,
foundationTerm: 7.0,
initial: 0.15,
taper: 0.15,
terminal: 0.015,
},
2021-02-05 18:59:00 -08:00
});
2021-02-05 18:59:00 -08:00
const inflation = await connection.getInflationGovernor();
2021-03-14 22:08:10 -07:00
const inflationKeys: (keyof InflationGovernor)[] = [
2021-02-05 18:59:00 -08:00
'initial',
'terminal',
'taper',
'foundation',
'foundationTerm',
2021-03-14 22:08:10 -07:00
];
for (const key of inflationKeys) {
2021-02-05 18:59:00 -08:00
expect(inflation).to.have.property(key);
expect(inflation[key]).to.be.greaterThan(0);
}
});
it('get inflation reward', async () => {
if (mockServer) {
await mockRpcResponse({
method: 'getInflationReward',
params: [
[
'7GHnTRB8Rz14qZQhDXf8ox1Kfu7mPcPLpKaBJJirmYj2',
'CrinLuHjVGDDcQfrEoCmM4k31Ni9sMoTCEEvNSUSh7Jg',
],
{
epoch: 0,
},
],
value: [
{
amount: 3646143,
effectiveSlot: 432000,
epoch: 0,
postBalance: 30504783,
},
null,
],
});
const inflationReward = await connection.getInflationReward(
[
new PublicKey('7GHnTRB8Rz14qZQhDXf8ox1Kfu7mPcPLpKaBJJirmYj2'),
new PublicKey('CrinLuHjVGDDcQfrEoCmM4k31Ni9sMoTCEEvNSUSh7Jg'),
],
0,
);
expect(inflationReward).to.have.lengthOf(2);
}
});
2021-02-05 18:59:00 -08:00
it('get epoch info', async () => {
await mockRpcResponse({
method: 'getEpochInfo',
params: [{commitment: 'confirmed'}],
2021-02-05 18:59:00 -08:00
value: {
epoch: 0,
slotIndex: 1,
slotsInEpoch: 8192,
absoluteSlot: 1,
blockHeight: 1,
},
2021-02-05 18:59:00 -08:00
});
const epochInfo = await connection.getEpochInfo('confirmed');
2021-03-14 22:08:10 -07:00
const epochInfoKeys: (keyof EpochInfo)[] = [
2021-02-05 18:59:00 -08:00
'epoch',
'slotIndex',
'slotsInEpoch',
'absoluteSlot',
'blockHeight',
2021-03-14 22:08:10 -07:00
];
for (const key of epochInfoKeys) {
2021-02-05 18:59:00 -08:00
expect(epochInfo).to.have.property(key);
expect(epochInfo[key]).to.be.at.least(0);
}
});
2019-10-23 06:48:24 -07:00
2021-02-05 18:59:00 -08:00
it('get epoch schedule', async () => {
await mockRpcResponse({
2019-10-23 06:48:24 -07:00
method: 'getEpochSchedule',
params: [],
2021-02-05 18:59:00 -08:00
value: {
firstNormalEpoch: 8,
firstNormalSlot: 8160,
leaderScheduleSlotOffset: 8192,
slotsPerEpoch: 8192,
2019-10-23 06:48:24 -07:00
warmup: true,
},
2021-02-05 18:59:00 -08:00
});
2019-10-23 06:48:24 -07:00
2021-02-05 18:59:00 -08:00
const epochSchedule = await connection.getEpochSchedule();
2021-03-14 22:08:10 -07:00
const epochScheduleKeys: (keyof EpochSchedule)[] = [
2021-02-05 18:59:00 -08:00
'firstNormalEpoch',
'firstNormalSlot',
'leaderScheduleSlotOffset',
'slotsPerEpoch',
2021-03-14 22:08:10 -07:00
];
for (const key of epochScheduleKeys) {
2021-02-05 18:59:00 -08:00
expect(epochSchedule).to.have.property('warmup');
expect(epochSchedule).to.have.property(key);
if (epochSchedule.warmup) {
expect(epochSchedule[key]).to.be.greaterThan(0);
}
}
});
2020-07-17 08:16:44 -07:00
2021-02-05 18:59:00 -08:00
it('get leader schedule', async () => {
await mockRpcResponse({
2020-07-17 08:16:44 -07:00
method: 'getLeaderSchedule',
params: [],
2021-02-05 18:59:00 -08:00
value: {
2020-07-17 08:16:44 -07:00
'123vij84ecQEKUvQ7gYMKxKwKF6PbYSzCzzURYA4xULY': [0, 1, 2, 3],
'8PTjAikKoAybKXcEPnDSoy8wSNNikUBJ1iKawJKQwXnB': [4, 5, 6, 7],
},
2021-02-05 18:59:00 -08:00
});
2020-07-17 08:16:44 -07:00
2021-02-05 18:59:00 -08:00
const leaderSchedule = await connection.getLeaderSchedule();
expect(Object.keys(leaderSchedule).length).to.be.at.least(1);
for (const key in leaderSchedule) {
const slots = leaderSchedule[key];
expect(Array.isArray(slots)).to.be.true;
expect(slots.length).to.be.at.least(4);
}
});
2019-08-02 16:06:54 -07:00
2021-02-05 18:59:00 -08:00
it('get slot', async () => {
await mockRpcResponse({
2019-08-02 16:06:54 -07:00
method: 'getSlot',
2021-02-05 18:59:00 -08:00
params: [],
value: 123,
});
2019-08-02 16:06:54 -07:00
2021-02-05 18:59:00 -08:00
const slot = await connection.getSlot();
2021-03-14 22:08:10 -07:00
if (mockServer) {
2021-02-05 18:59:00 -08:00
expect(slot).to.eq(123);
} else {
// No idea what the correct slot value should be on a live cluster, so
// just check the type
expect(typeof slot).to.eq('number');
}
});
2021-02-05 18:59:00 -08:00
it('get slot leader', async () => {
await mockRpcResponse({
method: 'getSlotLeader',
2021-02-05 18:59:00 -08:00
params: [],
value: '11111111111111111111111111111111',
});
2021-02-05 18:59:00 -08:00
const slotLeader = await connection.getSlotLeader();
2021-03-14 22:08:10 -07:00
if (mockServer) {
2021-02-05 18:59:00 -08:00
expect(slotLeader).to.eq('11111111111111111111111111111111');
} else {
// No idea what the correct slotLeader value should be on a live cluster, so
// just check the type
expect(typeof slotLeader).to.eq('string');
}
});
it('get slot leaders', async () => {
await mockRpcResponse({
method: 'getSlotLeaders',
params: [0, 1],
value: ['11111111111111111111111111111111'],
});
const slotLeaders = await connection.getSlotLeaders(0, 1);
expect(slotLeaders).to.have.length(1);
expect(slotLeaders[0]).to.be.instanceOf(PublicKey);
});
2021-02-05 18:59:00 -08:00
it('get cluster nodes', async () => {
await mockRpcResponse({
method: 'getClusterNodes',
2021-02-05 18:59:00 -08:00
params: [],
value: [
{
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',
},
],
2021-02-05 18:59:00 -08:00
});
2021-02-05 18:59:00 -08:00
const clusterNodes = await connection.getClusterNodes();
2021-03-14 22:08:10 -07:00
if (mockServer) {
2021-02-05 18:59:00 -08:00
expect(clusterNodes).to.have.length(1);
expect(clusterNodes[0].pubkey).to.eq('11111111111111111111111111111111');
expect(typeof clusterNodes[0].gossip).to.eq('string');
expect(typeof clusterNodes[0].tpu).to.eq('string');
expect(clusterNodes[0].rpc).to.be.null;
} else {
// There should be at least one node (the node that we're talking to)
expect(clusterNodes.length).to.be.greaterThan(0);
}
});
2021-02-05 18:59:00 -08:00
if (process.env.TEST_LIVE) {
it('get vote accounts', async () => {
const voteAccounts = await connection.getVoteAccounts();
expect(
voteAccounts.current.concat(voteAccounts.delinquent).length,
).to.be.greaterThan(0);
});
}
2018-08-23 10:52:48 -07:00
2021-02-05 18:59:00 -08:00
it('confirm transaction - error', async () => {
const badTransactionSignature = 'bad transaction signature';
2018-08-24 10:39:51 -07:00
2021-02-05 18:59:00 -08:00
await expect(
connection.confirmTransaction(badTransactionSignature),
).to.be.rejectedWith('signature must be base58 encoded');
2018-09-26 19:16:17 -07:00
2021-02-05 18:59:00 -08:00
await mockRpcResponse({
method: 'getSignatureStatuses',
2020-03-23 08:01:12 -07:00
params: [[badTransactionSignature]],
2021-02-05 18:59:00 -08:00
error: mockErrorResponse,
});
2018-08-23 10:52:48 -07:00
2021-02-05 18:59:00 -08:00
await expect(
connection.getSignatureStatus(badTransactionSignature),
).to.be.rejectedWith(mockErrorMessage);
});
2018-08-23 10:52:48 -07:00
2021-02-05 18:59:00 -08:00
it('get transaction count', async () => {
await mockRpcResponse({
2018-08-24 10:39:51 -07:00
method: 'getTransactionCount',
params: [],
2021-02-05 18:59:00 -08:00
value: 1000000,
});
2018-08-23 10:52:48 -07:00
2021-02-05 18:59:00 -08:00
const count = await connection.getTransactionCount();
expect(count).to.be.at.least(0);
});
2021-02-05 18:59:00 -08:00
it('get total supply', async () => {
await mockRpcResponse({
2021-04-14 01:28:40 -07:00
method: 'getSupply',
params: [],
2021-04-14 01:28:40 -07:00
value: {
total: 1000000,
circulating: 100000,
nonCirculating: 900000,
nonCirculatingAccounts: [],
2021-04-14 01:28:40 -07:00
},
withContext: true,
2021-02-05 18:59:00 -08:00
});
2021-02-05 18:59:00 -08:00
const count = await connection.getTotalSupply();
expect(count).to.be.at.least(0);
});
2021-02-05 18:59:00 -08:00
it('get minimum balance for rent exemption', async () => {
await mockRpcResponse({
method: 'getMinimumBalanceForRentExemption',
params: [512],
2021-02-05 18:59:00 -08:00
value: 1000000,
});
2021-02-05 18:59:00 -08:00
const count = await connection.getMinimumBalanceForRentExemption(512);
expect(count).to.be.at.least(0);
});
2021-02-05 18:59:00 -08:00
it('get confirmed signatures for address', async () => {
const connection = new Connection(url);
2021-02-05 18:59:00 -08:00
await mockRpcResponse({
method: 'getSlot',
params: [],
2021-02-05 18:59:00 -08:00
value: 1,
});
2021-02-05 18:59:00 -08:00
while ((await connection.getSlot()) <= 0) {
continue;
}
2021-02-05 18:59:00 -08:00
await mockRpcResponse({
method: 'getConfirmedBlock',
params: [1],
2021-02-05 18:59:00 -08:00
value: {
blockTime: 1614281964,
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',
],
},
},
],
},
2021-02-05 18:59:00 -08:00
});
// Find a block that has a transaction.
await mockRpcResponse({
method: 'getFirstAvailableBlock',
params: [],
value: 1,
});
let slot = await connection.getFirstAvailableBlock();
2021-03-14 22:08:10 -07:00
let address: PublicKey | undefined;
let expectedSignature: string | undefined;
2021-02-05 18:59:00 -08:00
while (!address || !expectedSignature) {
const block = await connection.getConfirmedBlock(slot);
if (block.transactions.length > 0) {
const {signature, publicKey} =
block.transactions[0].transaction.signatures[0];
2021-02-05 18:59:00 -08:00
if (signature) {
address = publicKey;
expectedSignature = bs58.encode(signature);
break;
2021-02-05 18:59:00 -08:00
}
}
slot++;
}
2021-02-05 18:59:00 -08:00
// getConfirmedSignaturesForAddress tests...
await mockRpcResponse({
method: 'getFirstAvailableBlock',
params: [],
value: 0,
});
const mockSignature =
'5SHZ9NwpnS9zYnauN7pnuborKf39zGMr11XpMC59VvRSeDJNcnYLecmdxXCVuBFPNQLdCBBjyZiNCL4KoHKr3tvz';
await mockRpcResponse({
method: 'getConfirmedBlock',
params: [slot, {transactionDetails: 'signatures', rewards: false}],
value: {
blockTime: 1614281964,
blockhash: 'H5nJ91eGag3B5ZSRHZ7zG5ZwXJ6ywCt2hyR8xCsV7xMo',
previousBlockhash: 'H5nJ91eGag3B5ZSRHZ7zG5ZwXJ6ywCt2hyR8xCsV7xMo',
parentSlot: 1,
signatures: [mockSignature],
},
});
await mockRpcResponse({
method: 'getSlot',
params: [],
value: 123,
});
await mockRpcResponse({
method: 'getConfirmedBlock',
params: [slot + 2, {transactionDetails: 'signatures', rewards: false}],
value: {
blockTime: 1614281964,
blockhash: 'H5nJ91eGag3B5ZSRHZ7zG5ZwXJ6ywCt2hyR8xCsV7xMo',
previousBlockhash: 'H5nJ91eGag3B5ZSRHZ7zG5ZwXJ6ywCt2hyR8xCsV7xMo',
parentSlot: 1,
signatures: [mockSignature],
},
});
await mockRpcResponse({
method: 'getConfirmedSignaturesForAddress2',
params: [address.toBase58(), {before: mockSignature}],
value: [
{
signature: expectedSignature,
slot,
err: null,
memo: null,
},
],
2021-02-05 18:59:00 -08:00
});
const confirmedSignatures =
await connection.getConfirmedSignaturesForAddress(
address,
slot,
slot + 1,
);
2021-02-05 18:59:00 -08:00
expect(confirmedSignatures.includes(expectedSignature)).to.be.true;
const badSlot = Number.MAX_SAFE_INTEGER - 1;
await mockRpcResponse({
method: 'getConfirmedBlock',
params: [badSlot - 1, {transactionDetails: 'signatures', rewards: false}],
error: {message: 'Block not available for slot ' + badSlot},
2021-02-05 18:59:00 -08:00
});
expect(
connection.getConfirmedSignaturesForAddress(
address,
badSlot,
badSlot + 1,
),
).to.be.rejected;
2021-02-05 18:59:00 -08:00
// getConfirmedSignaturesForAddress2 tests...
await mockRpcResponse({
method: 'getConfirmedSignaturesForAddress2',
params: [address.toBase58(), {limit: 1}],
2021-02-05 18:59:00 -08:00
value: [
{
signature: expectedSignature,
slot,
err: null,
memo: null,
},
],
2021-02-05 18:59:00 -08:00
});
const confirmedSignatures2 =
await connection.getConfirmedSignaturesForAddress2(address, {limit: 1});
2021-02-05 18:59:00 -08:00
expect(confirmedSignatures2).to.have.length(1);
2021-03-14 22:08:10 -07:00
if (mockServer) {
2021-02-05 18:59:00 -08:00
expect(confirmedSignatures2[0].signature).to.eq(expectedSignature);
expect(confirmedSignatures2[0].slot).to.eq(slot);
expect(confirmedSignatures2[0].err).to.be.null;
expect(confirmedSignatures2[0].memo).to.be.null;
}
});
it('get signatures for address', async () => {
const connection = new Connection(url);
await mockRpcResponse({
method: 'getSlot',
params: [],
value: 1,
});
while ((await connection.getSlot()) <= 0) {
continue;
}
await mockRpcResponse({
method: 'getConfirmedBlock',
params: [1],
value: {
blockTime: 1614281964,
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.
await mockRpcResponse({
method: 'getFirstAvailableBlock',
params: [],
value: 1,
});
let slot = await connection.getFirstAvailableBlock();
let address: PublicKey | undefined;
let expectedSignature: string | undefined;
while (!address || !expectedSignature) {
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);
break;
}
}
slot++;
}
// getSignaturesForAddress tests...
await mockRpcResponse({
method: 'getSignaturesForAddress',
params: [address.toBase58(), {limit: 1}],
value: [
{
signature: expectedSignature,
slot,
err: null,
memo: null,
},
],
});
const signatures = await connection.getSignaturesForAddress(address, {
limit: 1,
});
expect(signatures).to.have.length(1);
if (mockServer) {
expect(signatures[0].signature).to.eq(expectedSignature);
expect(signatures[0].slot).to.eq(slot);
expect(signatures[0].err).to.be.null;
expect(signatures[0].memo).to.be.null;
}
});
it('get parsed confirmed transactions', async () => {
await mockRpcResponse({
method: 'getSlot',
params: [],
value: 1,
});
while ((await connection.getSlot()) <= 0) {
continue;
}
await mockRpcResponse({
method: 'getConfirmedBlock',
params: [1],
value: {
blockTime: 1614281964,
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.
await mockRpcResponse({
method: 'getFirstAvailableBlock',
params: [],
value: 1,
});
let slot = await connection.getFirstAvailableBlock();
let confirmedTransaction: string | undefined;
while (!confirmedTransaction) {
const block = await connection.getConfirmedBlock(slot);
for (const tx of block.transactions) {
if (tx.transaction.signature) {
confirmedTransaction = bs58.encode(tx.transaction.signature);
break;
}
}
slot++;
}
await mockRpcBatchResponse({
batch: [
{
methodName: 'getConfirmedTransaction',
args: [],
},
],
result: [
{
blockTime: 1616102519,
meta: {
err: null,
fee: 5000,
innerInstructions: [],
logMessages: [
'Program Vote111111111111111111111111111111111111111 invoke [1]',
'Program Vote111111111111111111111111111111111111111 success',
],
postBalances: [499999995000, 26858640, 1, 1, 1],
postTokenBalances: [],
preBalances: [500000000000, 26858640, 1, 1, 1],
preTokenBalances: [],
status: {
Ok: null,
},
},
slot: 2,
transaction: {
message: {
accountKeys: [
{
pubkey: 'jcU4R7JccGEvDpe1i6bahvHpe47XahMXacG73EzE198',
signer: true,
writable: true,
},
{
pubkey: 'GfBcnCAU7kWfAYqKRCNyWEHjdEJZmzRZvEcX5bbzEQqt',
signer: false,
writable: true,
},
{
pubkey: 'SysvarS1otHashes111111111111111111111111111',
signer: false,
writable: false,
},
{
pubkey: 'SysvarC1ock11111111111111111111111111111111',
signer: false,
writable: false,
},
{
pubkey: 'Vote111111111111111111111111111111111111111',
signer: false,
writable: false,
},
],
instructions: [
{
parsed: {
info: {
clockSysvar:
'SysvarC1ock11111111111111111111111111111111',
slotHashesSysvar:
'SysvarS1otHashes111111111111111111111111111',
vote: {
hash: 'GuCya3AAGxn1qhoqxqy3WEdZdZUkXKpa9pthQ3tqvbpx',
slots: [1],
timestamp: 1616102669,
},
voteAccount:
'GfBcnCAU7kWfAYqKRCNyWEHjdEJZmzRZvEcX5bbzEQqt',
voteAuthority:
'jcU4R7JccGEvDpe1i6bahvHpe47XahMXacG73EzE198',
},
type: 'vote',
},
program: 'vote',
programId: 'Vote111111111111111111111111111111111111111',
},
],
recentBlockhash: 'G9ywjV5CVgMtLXruXtrE7af4QgFKYNXgDTw4jp7SWcSo',
},
signatures: [
'4G4rTqnUdzrmBHsdKJSiMtonpQLWSw1avJ8YxWQ95jE6iFFHFsEkBnoYycxnkBS9xHWRc6EarDsrFG9USFBbjfjx',
],
},
},
{
blockTime: 1616102519,
meta: {
err: null,
fee: 5000,
innerInstructions: [],
logMessages: [
'Program Vote111111111111111111111111111111111111111 invoke [1]',
'Program Vote111111111111111111111111111111111111111 success',
],
postBalances: [499999995000, 26858640, 1, 1, 1],
postTokenBalances: [],
preBalances: [500000000000, 26858640, 1, 1, 1],
preTokenBalances: [],
status: {
Ok: null,
},
},
slot: 2,
transaction: {
message: {
accountKeys: [
{
pubkey: 'jcU4R7JccGEvDpe1i6bahvHpe47XahMXacG73EzE198',
signer: true,
writable: true,
},
{
pubkey: 'GfBcnCAU7kWfAYqKRCNyWEHjdEJZmzRZvEcX5bbzEQqt',
signer: false,
writable: true,
},
{
pubkey: 'SysvarS1otHashes111111111111111111111111111',
signer: false,
writable: false,
},
{
pubkey: 'SysvarC1ock11111111111111111111111111111111',
signer: false,
writable: false,
},
{
pubkey: 'Vote111111111111111111111111111111111111111',
signer: false,
writable: false,
},
],
instructions: [
{
parsed: {
info: {
clockSysvar:
'SysvarC1ock11111111111111111111111111111111',
slotHashesSysvar:
'SysvarS1otHashes111111111111111111111111111',
vote: {
hash: 'GuCya3AAGxn1qhoqxqy3WEdZdZUkXKpa9pthQ3tqvbpx',
slots: [1],
timestamp: 1616102669,
},
voteAccount:
'GfBcnCAU7kWfAYqKRCNyWEHjdEJZmzRZvEcX5bbzEQqt',
voteAuthority:
'jcU4R7JccGEvDpe1i6bahvHpe47XahMXacG73EzE198',
},
type: 'vote',
},
program: 'vote',
programId: 'Vote111111111111111111111111111111111111111',
},
],
recentBlockhash: 'G9ywjV5CVgMtLXruXtrE7af4QgFKYNXgDTw4jp7SWcSo',
},
signatures: [
'4G4rTqnUdzrmBHsdKJSiMtonpQLWSw1avJ8YxWQ95jE6iFFHFsEkBnoYycxnkBS9xHWRc6EarDsrFG9USFBbjfjx',
],
},
},
],
});
let result = await connection.getParsedConfirmedTransactions([
confirmedTransaction,
confirmedTransaction,
]);
if (!result) {
expect(result).to.be.ok;
return;
}
expect(result).to.be.length(2);
expect(result[0]).to.not.be.null;
expect(result[1]).to.not.be.null;
if (result[0] !== null) {
expect(result[0].transaction.signatures).not.to.be.null;
}
if (result[1] !== null) {
expect(result[1].transaction.signatures).not.to.be.null;
}
result = await connection.getParsedConfirmedTransactions([]);
if (!result) {
expect(result).to.be.ok;
return;
}
expect(result).to.be.empty;
});
2022-02-22 18:49:27 -08:00
it('get block height', async () => {
const commitment: Commitment = 'confirmed';
await mockRpcResponse({
method: 'getBlockHeight',
params: [{commitment: commitment}],
value: 10,
});
const blockHeight = await connection.getBlockHeight(commitment);
expect(blockHeight).to.be.a('number');
});
it('get block production', async () => {
const commitment: Commitment = 'processed';
// Find slot of the lowest confirmed block
await mockRpcResponse({
method: 'getFirstAvailableBlock',
params: [],
value: 1,
});
let firstSlot = await connection.getFirstAvailableBlock();
// Find current block height
await mockRpcResponse({
method: 'getBlockHeight',
params: [{commitment: commitment}],
value: 10,
});
let lastSlot = await connection.getBlockHeight(commitment);
const blockProductionConfig = {
commitment: commitment,
range: {
firstSlot,
lastSlot,
},
};
const blockProductionRet = {
byIdentity: {
'85iYT5RuzRTDgjyRa3cP8SYhM2j21fj7NhfJ3peu1DPr': [12, 10],
},
range: {
firstSlot,
lastSlot,
},
};
//mock RPC call with config specified
await mockRpcResponse({
method: 'getBlockProduction',
params: [blockProductionConfig],
value: blockProductionRet,
withContext: true,
});
//mock RPC call with commitment only
await mockRpcResponse({
method: 'getBlockProduction',
params: [{commitment: commitment}],
value: blockProductionRet,
withContext: true,
});
const result = await connection.getBlockProduction(blockProductionConfig);
if (!result) {
expect(result).to.be.ok;
return;
}
expect(result.context).to.be.ok;
expect(result.value).to.be.ok;
const resultContextSlot = result.context.slot;
expect(resultContextSlot).to.be.a('number');
const resultIdentityDictionary = result.value.byIdentity;
expect(resultIdentityDictionary).to.be.a('object');
for (var key in resultIdentityDictionary) {
expect(key).to.be.a('string');
expect(resultIdentityDictionary[key]).to.be.a('array');
expect(resultIdentityDictionary[key][0]).to.be.a('number');
expect(resultIdentityDictionary[key][1]).to.be.a('number');
}
const resultSlotRange = result.value.range;
expect(resultSlotRange.firstSlot).to.equal(firstSlot);
expect(resultSlotRange.lastSlot).to.equal(lastSlot);
const resultCommitmentOnly = await connection.getBlockProduction(
commitment,
);
if (!resultCommitmentOnly) {
expect(resultCommitmentOnly).to.be.ok;
return;
}
expect(resultCommitmentOnly.context).to.be.ok;
expect(resultCommitmentOnly.value).to.be.ok;
const resultCOContextSlot = result.context.slot;
expect(resultCOContextSlot).to.be.a('number');
const resultCOIdentityDictionary = result.value.byIdentity;
expect(resultCOIdentityDictionary).to.be.a('object');
for (var property in resultCOIdentityDictionary) {
expect(property).to.be.a('string');
expect(resultCOIdentityDictionary[property]).to.be.a('array');
expect(resultCOIdentityDictionary[property][0]).to.be.a('number');
expect(resultCOIdentityDictionary[property][1]).to.be.a('number');
}
const resultCOSlotRange = result.value.range;
expect(resultCOSlotRange.firstSlot).to.equal(firstSlot);
expect(resultCOSlotRange.lastSlot).to.equal(lastSlot);
});
it('get transaction', async () => {
await mockRpcResponse({
method: 'getSlot',
params: [],
value: 1,
});
while ((await connection.getSlot()) <= 0) {
continue;
}
await mockRpcResponse({
method: 'getBlock',
params: [1],
value: {
blockHeight: 0,
blockTime: 1614281964,
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],
2021-05-25 12:39:11 -07:00
data: '37u9WtQpcm6ULa3VtWDFAWoQc1hUvybPrA3dtx99tgHvvcE7pKRZjuGmn7VX2tC3JmYDYGG7',
programIdIndex: 4,
},
],
recentBlockhash: 'GeyAFFRY3WGpmam2hbgrKw4rbU2RKzfVLm5QLSeZwTZE',
},
signatures: [
'w2Zeq8YkpyB463DttvfzARD7k9ZxGEwbsEw4boEK7jDp3pfoxZbTdLFSsEPhzXhpCcjGi2kHtHFobgX49MMhbWt',
'4oCEqwGrMdBeMxpzuWiukCYqSfV4DsSKXSiVVCh1iJ6pS772X7y219JZP3mgqBz5PhsvprpKyhzChjYc3VSBQXzG',
],
},
},
],
},
});
// Find a block that has a transaction.
await mockRpcResponse({
method: 'getFirstAvailableBlock',
params: [],
value: 1,
});
let slot = await connection.getFirstAvailableBlock();
let transaction: string | undefined;
while (!transaction) {
const block = await connection.getBlock(slot);
if (block && block.transactions.length > 0) {
transaction = block.transactions[0].transaction.signatures[0];
continue;
}
slot++;
}
await mockRpcResponse({
method: 'getTransaction',
params: [transaction],
value: {
slot,
transaction: {
message: {
accountKeys: [
'va12u4o9DipLEB2z4fuoHszroq1U9NcAB9aooFDPJSf',
'57zQNBZBEiHsCZFqsaY6h176ioXy5MsSLmcvHkEyaLGy',
'SysvarS1otHashes111111111111111111111111111',
'SysvarC1ock11111111111111111111111111111111',
'Vote111111111111111111111111111111111111111',
],
header: {
numReadonlySignedAccounts: 0,
numReadonlyUnsignedAccounts: 3,
numRequiredSignatures: 2,
},
instructions: [
{
accounts: [1, 2, 3],
2021-05-25 12:39:11 -07:00
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.getTransaction(transaction);
if (!result) {
expect(result).to.be.ok;
return;
}
const resultSignature = result.transaction.signatures[0];
expect(resultSignature).to.eq(transaction);
const newAddress = Keypair.generate().publicKey;
const recentSignature = await helpers.airdrop({
connection,
address: newAddress,
amount: 1,
});
await mockRpcResponse({
method: 'getTransaction',
params: [recentSignature],
value: null,
});
// Signature hasn't been finalized yet
const nullResponse = await connection.getTransaction(recentSignature);
expect(nullResponse).to.be.null;
});
2021-02-05 18:59:00 -08:00
it('get confirmed transaction', async () => {
await mockRpcResponse({
method: 'getSlot',
params: [],
2021-02-05 18:59:00 -08:00
value: 1,
});
2021-02-05 18:59:00 -08:00
while ((await connection.getSlot()) <= 0) {
continue;
}
2021-02-05 18:59:00 -08:00
await mockRpcResponse({
method: 'getConfirmedBlock',
params: [1],
2021-02-05 18:59:00 -08:00
value: {
blockTime: 1614281964,
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',
],
},
},
],
},
2021-02-05 18:59:00 -08:00
});
// Find a block that has a transaction.
await mockRpcResponse({
method: 'getFirstAvailableBlock',
params: [],
value: 1,
});
let slot = await connection.getFirstAvailableBlock();
2021-03-14 22:08:10 -07:00
let confirmedTransaction: string | undefined;
2021-02-05 18:59:00 -08:00
while (!confirmedTransaction) {
const block = await connection.getConfirmedBlock(slot);
for (const tx of block.transactions) {
if (tx.transaction.signature) {
confirmedTransaction = bs58.encode(tx.transaction.signature);
break;
2021-02-05 18:59:00 -08:00
}
}
slot++;
}
2021-02-05 18:59:00 -08:00
await mockRpcResponse({
method: 'getConfirmedTransaction',
params: [confirmedTransaction],
2021-02-05 18:59:00 -08:00
value: {
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,
},
},
2021-02-05 18:59:00 -08:00
});
2021-02-05 18:59:00 -08:00
const result = await connection.getConfirmedTransaction(
confirmedTransaction,
);
2021-02-05 18:59:00 -08:00
if (!result) {
expect(result).to.be.ok;
return;
}
2021-02-05 18:59:00 -08:00
if (result.transaction.signature === null) {
expect(result.transaction.signature).not.to.be.null;
return;
}
2021-02-05 18:59:00 -08:00
const resultSignature = bs58.encode(result.transaction.signature);
expect(resultSignature).to.eq(confirmedTransaction);
const newAddress = Keypair.generate().publicKey;
2021-02-05 18:59:00 -08:00
const recentSignature = await helpers.airdrop({
connection,
address: newAddress,
amount: 1,
});
await mockRpcResponse({
method: 'getConfirmedTransaction',
params: [recentSignature],
2021-02-05 18:59:00 -08:00
value: null,
});
2021-02-05 18:59:00 -08:00
// Signature hasn't been finalized yet
const nullResponse = await connection.getConfirmedTransaction(
recentSignature,
);
expect(nullResponse).to.be.null;
});
2021-03-14 22:08:10 -07:00
if (mockServer) {
2021-02-05 18:59:00 -08:00
it('get parsed confirmed transaction coerces public keys of inner instructions', async () => {
const confirmedTransaction: TransactionSignature =
'4ADvAUQYxkh4qWKYE9QLW8gCLomGG94QchDLG4quvpBz1WqARYvzWQDDitKduAKspuy1DjcbnaDAnCAfnKpJYs48';
2021-03-14 22:08:10 -07:00
function getMockData(inner: any) {
2021-02-05 18:59:00 -08:00
return {
slot: 353050305,
transaction: {
message: {
accountKeys: [
{
pubkey: 'va12u4o9DipLEB2z4fuoHszroq1U9NcAB9aooFDPJSf',
signer: true,
writable: true,
},
],
instructions: [
{
accounts: ['va12u4o9DipLEB2z4fuoHszroq1U9NcAB9aooFDPJSf'],
data: '37u9WtQpcm6ULa3VtWDFAWoQc1hUvybPrA3dtx99tgHvvcE7pKRZjuGmn7VX2tC3JmYDYGG7',
2021-02-05 18:59:00 -08:00
programId: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
},
],
recentBlockhash: 'GeyAFFRY3WGpmam2hbgrKw4rbU2RKzfVLm5QLSeZwTZE',
},
signatures: [
'w2Zeq8YkpyB463DttvfzARD7k9ZxGEwbsEw4boEK7jDp3pfoxZbTdLFSsEPhzXhpCcjGi2kHtHFobgX49MMhbWt',
'4oCEqwGrMdBeMxpzuWiukCYqSfV4DsSKXSiVVCh1iJ6pS772X7y219JZP3mgqBz5PhsvprpKyhzChjYc3VSBQXzG',
],
2021-02-05 18:59:00 -08:00
},
meta: {
fee: 10000,
postBalances: [499260347380, 15298080, 1, 1, 1],
preBalances: [499260357380, 15298080, 1, 1, 1],
innerInstructions: [
{
2021-02-05 18:59:00 -08:00
index: 0,
instructions: [inner],
},
],
2021-02-05 18:59:00 -08:00
status: {Ok: null},
err: null,
},
2021-02-05 18:59:00 -08:00
};
}
2021-02-05 18:59:00 -08:00
await mockRpcResponse({
method: 'getConfirmedTransaction',
params: [confirmedTransaction, {encoding: 'jsonParsed'}],
2021-02-05 18:59:00 -08:00
value: getMockData({
parsed: {},
program: 'spl-token',
programId: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
}),
});
const result = await connection.getParsedConfirmedTransaction(
confirmedTransaction,
);
2021-03-14 22:08:10 -07:00
if (result && result.meta && result.meta.innerInstructions) {
const innerInstructions = result.meta.innerInstructions;
const firstIx = innerInstructions[0].instructions[0];
expect(firstIx.programId).to.be.instanceOf(PublicKey);
2021-02-05 18:59:00 -08:00
}
2021-02-05 18:59:00 -08:00
await mockRpcResponse({
method: 'getConfirmedTransaction',
params: [confirmedTransaction, {encoding: 'jsonParsed'}],
2021-02-05 18:59:00 -08:00
value: getMockData({
accounts: [
'EeJqWk5pczNjsqqY3jia9xfFNG1dD68te4s8gsdCuEk7',
'6tVrjJhFm5SAvvdh6tysjotQurCSELpxuW3JaAAYeC1m',
],
data: 'ai3535',
programId: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
}),
});
const result2 = await connection.getParsedConfirmedTransaction(
confirmedTransaction,
);
2021-03-14 22:08:10 -07:00
if (result2 && result2.meta && result2.meta.innerInstructions) {
const innerInstructions = result2.meta.innerInstructions;
const instruction = innerInstructions[0].instructions[0];
expect(instruction.programId).to.be.instanceOf(PublicKey);
if ('accounts' in instruction) {
expect(instruction.accounts[0]).to.be.instanceOf(PublicKey);
expect(instruction.accounts[1]).to.be.instanceOf(PublicKey);
} else {
expect('accounts' in instruction).to.be.true;
}
}
2021-02-05 18:59:00 -08:00
});
}
2019-11-12 08:21:19 -08:00
describe('get block', function () {
beforeEach(async function () {
await mockRpcResponse({
method: 'getSlot',
params: [],
value: 1,
});
while ((await connection.getSlot()) <= 0) {
continue;
}
});
it('gets the genesis block', async function () {
await mockRpcResponse({
method: 'getBlock',
params: [0],
value: {
blockHeight: 0,
blockTime: 1614281964,
blockhash: 'H5nJ91eGag3B5ZSRHZ7zG5ZwXJ6ywCt2hyR8xCsV7xMo',
previousBlockhash: 'H5nJ91eGag3B5ZSRHZ7zG5ZwXJ6ywCt2hyR8xCsV7xMo',
parentSlot: 0,
transactions: [],
},
});
let maybeBlock0: BlockResponse | null;
try {
maybeBlock0 = await connection.getBlock(0);
} catch (e) {
if (process.env.TEST_LIVE) {
console.warn(
'WARNING: We ran no assertions about the genesis block because block 0 ' +
'could not be found. See https://github.com/solana-labs/solana/issues/23853.',
);
this.skip();
} else {
throw e;
}
}
expect(maybeBlock0).not.to.be.null;
const block0 = maybeBlock0!;
// Block 0 never has any transactions in test validator
const blockhash0 = block0.blockhash;
expect(block0.transactions).to.have.length(0);
expect(blockhash0).not.to.be.null;
expect(block0.previousBlockhash).not.to.be.null;
expect(block0.parentSlot).to.eq(0);
});
it('gets a block having a parent', async function () {
// Mock parent of block with transaction.
await mockRpcResponse({
method: 'getBlock',
params: [0],
value: {
blockHeight: 0,
blockTime: 1614281964,
blockhash: 'H5nJ91eGag3B5ZSRHZ7zG5ZwXJ6ywCt2hyR8xCsV7xMo',
previousBlockhash: 'H5nJ91eGag3B5ZSRHZ7zG5ZwXJ6ywCt2hyR8xCsV7xMo',
parentSlot: 0,
transactions: [],
},
});
// Mock block with transaction.
await mockRpcResponse({
method: 'getBlock',
params: [1],
value: {
blockHeight: 0,
blockTime: 1614281964,
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 *and* a parent.
await mockRpcResponse({
method: 'getFirstAvailableBlock',
params: [],
value: 0,
});
let candidateSlot = (await connection.getFirstAvailableBlock()) + 1;
let result:
| {
blockWithTransaction: BlockResponse;
parentBlock: BlockResponse;
}
| undefined;
while (!result) {
const candidateBlock = await connection.getBlock(candidateSlot);
if (candidateBlock && candidateBlock.transactions.length) {
const parentBlock = await connection.getBlock(candidateSlot - 1);
if (parentBlock) {
result = {blockWithTransaction: candidateBlock, parentBlock};
break;
}
}
candidateSlot++;
}
// Compare data with parent
expect(result.blockWithTransaction.previousBlockhash).to.eq(
result.parentBlock.blockhash,
);
expect(result.blockWithTransaction.blockhash).not.to.be.null;
expect(result.blockWithTransaction.transactions[0].transaction).not.to.be
.null;
await mockRpcResponse({
method: 'getBlock',
params: [Number.MAX_SAFE_INTEGER],
error: {
message: `Block not available for slot ${Number.MAX_SAFE_INTEGER}`,
},
});
await expect(
connection.getBlock(Number.MAX_SAFE_INTEGER),
).to.be.rejectedWith(
`Block not available for slot ${Number.MAX_SAFE_INTEGER}`,
);
});
});
describe('get confirmed block', function () {
beforeEach(async function () {
await mockRpcResponse({
method: 'getSlot',
params: [],
value: 1,
});
while ((await connection.getSlot()) <= 0) {
continue;
}
2021-02-05 18:59:00 -08:00
});
2020-01-16 12:09:21 -08:00
it('gets the genesis block', async function () {
await mockRpcResponse({
method: 'getConfirmedBlock',
params: [0],
value: {
blockHeight: 0,
blockTime: 1614281964,
blockhash: 'H5nJ91eGag3B5ZSRHZ7zG5ZwXJ6ywCt2hyR8xCsV7xMo',
previousBlockhash: 'H5nJ91eGag3B5ZSRHZ7zG5ZwXJ6ywCt2hyR8xCsV7xMo',
parentSlot: 0,
transactions: [],
},
});
2019-11-26 00:58:00 -08:00
let block0: ConfirmedBlock;
try {
block0 = await connection.getConfirmedBlock(0);
} catch (e) {
if (process.env.TEST_LIVE) {
console.warn(
'WARNING: We ran no assertions about the genesis block because block 0 ' +
'could not be found. See https://github.com/solana-labs/solana/issues/23853.',
);
this.skip();
} else {
throw e;
}
}
2021-02-05 18:59:00 -08:00
// Block 0 never has any transactions in test validator
const blockhash0 = block0.blockhash;
expect(block0.transactions).to.have.length(0);
expect(blockhash0).not.to.be.null;
expect(block0.previousBlockhash).not.to.be.null;
expect(block0.parentSlot).to.eq(0);
});
2021-02-05 18:59:00 -08:00
it('gets a block having a parent', async function () {
// Mock parent of block with transaction.
await mockRpcResponse({
method: 'getConfirmedBlock',
params: [0],
value: {
blockHeight: 0,
blockTime: 1614281964,
blockhash: 'H5nJ91eGag3B5ZSRHZ7zG5ZwXJ6ywCt2hyR8xCsV7xMo',
previousBlockhash: 'H5nJ91eGag3B5ZSRHZ7zG5ZwXJ6ywCt2hyR8xCsV7xMo',
parentSlot: 0,
transactions: [],
},
});
// Mock block with transaction.
await mockRpcResponse({
method: 'getConfirmedBlock',
params: [1],
value: {
blockTime: 1614281964,
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,
2020-01-16 12:09:21 -08:00
},
instructions: [
{
accounts: [1, 2, 3],
data: '37u9WtQpcm6ULa3VtWDFAWoQc1hUvybPrA3dtx99tgHvvcE7pKRZjuGmn7VX2tC3JmYDYGG7',
programIdIndex: 4,
},
],
recentBlockhash:
'GeyAFFRY3WGpmam2hbgrKw4rbU2RKzfVLm5QLSeZwTZE',
},
signatures: [
'w2Zeq8YkpyB463DttvfzARD7k9ZxGEwbsEw4boEK7jDp3pfoxZbTdLFSsEPhzXhpCcjGi2kHtHFobgX49MMhbWt',
'4oCEqwGrMdBeMxpzuWiukCYqSfV4DsSKXSiVVCh1iJ6pS772X7y219JZP3mgqBz5PhsvprpKyhzChjYc3VSBQXzG',
2020-01-16 12:09:21 -08:00
],
},
},
],
},
});
2021-02-05 18:59:00 -08:00
// Find a block that has a transaction *and* a parent.
await mockRpcResponse({
method: 'getFirstAvailableBlock',
params: [],
value: 0,
});
let candidateSlot = (await connection.getFirstAvailableBlock()) + 1;
let result:
| {
blockWithTransaction: ConfirmedBlock;
parentBlock: ConfirmedBlock;
}
| undefined;
while (!result) {
const candidateBlock = await connection.getConfirmedBlock(
candidateSlot,
);
if (candidateBlock && candidateBlock.transactions.length) {
const parentBlock = await connection.getConfirmedBlock(
candidateSlot - 1,
);
if (parentBlock) {
result = {blockWithTransaction: candidateBlock, parentBlock};
break;
}
}
candidateSlot++;
2021-02-05 18:59:00 -08:00
}
// Compare data with parent
expect(result.blockWithTransaction.previousBlockhash).to.eq(
result.parentBlock.blockhash,
);
expect(result.blockWithTransaction.blockhash).not.to.be.null;
expect(result.blockWithTransaction.transactions[0].transaction).not.to.be
.null;
await mockRpcResponse({
method: 'getConfirmedBlock',
params: [Number.MAX_SAFE_INTEGER],
error: {
message: `Block not available for slot ${Number.MAX_SAFE_INTEGER}`,
},
});
await expect(
connection.getConfirmedBlock(Number.MAX_SAFE_INTEGER),
).to.be.rejectedWith(
`Block not available for slot ${Number.MAX_SAFE_INTEGER}`,
);
2021-02-05 18:59:00 -08:00
});
});
2021-09-20 20:08:12 -07:00
it('get blocks between two slots', async () => {
await mockRpcResponse({
method: 'getBlocks',
2021-09-20 20:08:12 -07:00
params: [0, 10],
value: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
});
await mockRpcResponse({
method: 'getSlot',
params: [],
value: 10,
});
const latestSlot = await connection.getSlot();
const blocks = await connection.getBlocks(0, latestSlot);
expect(blocks).to.have.length(latestSlot);
expect(blocks).to.contain(1);
expect(blocks).to.contain(latestSlot);
});
it('get blocks from starting slot', async () => {
await mockRpcResponse({
method: 'getBlocks',
2021-09-20 20:08:12 -07:00
params: [0],
value: [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42,
],
});
await mockRpcResponse({
method: 'getSlot',
params: [],
value: 20,
});
while ((await connection.getSlot()) <= 0) {
continue;
}
const blocks = await connection.getBlocks(0);
const latestSlot = await connection.getSlot();
expect(blocks).to.have.lengthOf.greaterThanOrEqual(latestSlot);
expect(blocks).to.contain(1);
expect(blocks).to.contain(latestSlot);
});
describe('get block signatures', function () {
beforeEach(async function () {
await mockRpcResponse({
method: 'getSlot',
params: [],
value: 1,
});
while ((await connection.getSlot()) <= 0) {
continue;
}
});
it('gets the genesis block', async function () {
await mockRpcResponse({
method: 'getBlock',
params: [
0,
{
transactionDetails: 'signatures',
rewards: false,
},
],
value: {
blockHeight: 0,
blockTime: 1614281964,
blockhash: 'H5nJ91eGag3B5ZSRHZ7zG5ZwXJ6ywCt2hyR8xCsV7xMo',
previousBlockhash: 'H5nJ91eGag3B5ZSRHZ7zG5ZwXJ6ywCt2hyR8xCsV7xMo',
parentSlot: 0,
signatures: [],
},
});
let block0: BlockSignatures;
try {
block0 = await connection.getBlockSignatures(0);
} catch (e) {
if (process.env.TEST_LIVE) {
console.warn(
'WARNING: We ran no assertions about the genesis block because block 0 ' +
'could not be found. See https://github.com/solana-labs/solana/issues/23853.',
);
this.skip();
} else {
throw e;
}
}
// Block 0 never has any transactions in test validator
const blockhash0 = block0.blockhash;
expect(block0.signatures).to.have.length(0);
expect(blockhash0).not.to.be.null;
expect(block0.previousBlockhash).not.to.be.null;
expect(block0.parentSlot).to.eq(0);
expect(block0).to.not.have.property('rewards');
});
it('gets a block having a parent', async function () {
// Mock parent of block with transaction.
await mockRpcResponse({
method: 'getBlock',
params: [
0,
{
transactionDetails: 'signatures',
rewards: false,
},
],
value: {
blockHeight: 0,
blockTime: 1614281964,
blockhash: 'H5nJ91eGag3B5ZSRHZ7zG5ZwXJ6ywCt2hyR8xCsV7xMo',
previousBlockhash: 'H5nJ91eGag3B5ZSRHZ7zG5ZwXJ6ywCt2hyR8xCsV7xMo',
parentSlot: 0,
signatures: [],
},
});
// Mock block with transaction.
await mockRpcResponse({
method: 'getBlock',
params: [
1,
{
transactionDetails: 'signatures',
rewards: false,
},
],
value: {
blockHeight: 1,
blockTime: 1614281964,
blockhash: '57zQNBZBEiHsCZFqsaY6h176ioXy5MsSLmcvHkEyaLGy',
previousBlockhash: 'H5nJ91eGag3B5ZSRHZ7zG5ZwXJ6ywCt2hyR8xCsV7xMo',
parentSlot: 0,
signatures: [
'w2Zeq8YkpyB463DttvfzARD7k9ZxGEwbsEw4boEK7jDp3pfoxZbTdLFSsEPhzXhpCcjGi2kHtHFobgX49MMhbWt',
'4oCEqwGrMdBeMxpzuWiukCYqSfV4DsSKXSiVVCh1iJ6pS772X7y219JZP3mgqBz5PhsvprpKyhzChjYc3VSBQXzG',
],
},
});
// Find a block that has a transaction *and* a parent.
await mockRpcResponse({
method: 'getFirstAvailableBlock',
params: [],
value: 0,
});
let candidateSlot = (await connection.getFirstAvailableBlock()) + 1;
let result:
| {
blockWithTransaction: BlockSignatures;
parentBlock: BlockSignatures;
}
| undefined;
while (!result) {
const candidateBlock = await connection.getBlockSignatures(
candidateSlot,
);
if (candidateBlock && candidateBlock.signatures.length) {
const parentBlock = await connection.getBlockSignatures(
candidateSlot - 1,
);
if (parentBlock) {
result = {blockWithTransaction: candidateBlock, parentBlock};
break;
}
}
candidateSlot++;
}
// Compare data with parent
expect(result.blockWithTransaction.previousBlockhash).to.eq(
result.parentBlock.blockhash,
);
expect(result.blockWithTransaction.blockhash).not.to.be.null;
expect(result.blockWithTransaction.signatures[0]).not.to.be.null;
expect(result.blockWithTransaction).to.not.have.property('rewards');
await mockRpcResponse({
method: 'getBlock',
params: [Number.MAX_SAFE_INTEGER],
error: {
message: `Block not available for slot ${Number.MAX_SAFE_INTEGER}`,
},
});
await expect(
connection.getBlockSignatures(Number.MAX_SAFE_INTEGER),
).to.be.rejectedWith(
`Block not available for slot ${Number.MAX_SAFE_INTEGER}`,
);
});
});
2021-02-05 18:59:00 -08:00
it('get recent blockhash', async () => {
2021-03-14 22:08:10 -07:00
const commitments: Commitment[] = ['processed', 'confirmed', 'finalized'];
for (const commitment of commitments) {
2021-02-05 18:59:00 -08:00
const {blockhash, feeCalculator} = await helpers.recentBlockhash({
connection,
commitment,
});
expect(bs58.decode(blockhash)).to.have.length(32);
expect(feeCalculator.lamportsPerSignature).to.be.at.least(0);
}
});
it('get latest blockhash', async () => {
const commitments: Commitment[] = ['processed', 'confirmed', 'finalized'];
for (const commitment of commitments) {
const {blockhash, lastValidBlockHeight} = await helpers.latestBlockhash({
connection,
commitment,
});
expect(bs58.decode(blockhash)).to.have.length(32);
expect(lastValidBlockHeight).to.be.at.least(0);
}
});
2021-02-05 18:59:00 -08:00
it('get fee calculator', async () => {
const {blockhash} = await helpers.recentBlockhash({connection});
await mockRpcResponse({
method: 'getFeeCalculatorForBlockhash',
params: [blockhash, {commitment: 'confirmed'}],
2021-02-05 18:59:00 -08:00
value: {
feeCalculator: {
lamportsPerSignature: 5000,
},
},
2021-02-05 18:59:00 -08:00
withContext: true,
});
2021-02-05 18:59:00 -08:00
const feeCalculator = (
await connection.getFeeCalculatorForBlockhash(blockhash, 'confirmed')
2021-02-05 18:59:00 -08:00
).value;
if (feeCalculator === null) {
expect(feeCalculator).not.to.be.null;
return;
}
expect(feeCalculator.lamportsPerSignature).to.eq(5000);
});
2020-05-18 20:27:36 -07:00
it('get fee for message', async () => {
const accountFrom = Keypair.generate();
const accountTo = Keypair.generate();
const {blockhash} = await helpers.latestBlockhash({connection});
const transaction = new Transaction({
feePayer: accountFrom.publicKey,
recentBlockhash: blockhash,
}).add(
SystemProgram.transfer({
fromPubkey: accountFrom.publicKey,
toPubkey: accountTo.publicKey,
lamports: 10,
}),
);
const message = transaction.compileMessage();
await mockRpcResponse({
method: 'getFeeForMessage',
params: [
message.serialize().toString('base64'),
{commitment: 'confirmed'},
],
value: 5000,
withContext: true,
});
const fee = (await connection.getFeeForMessage(message, 'confirmed')).value;
expect(fee).to.eq(5000);
});
2021-02-05 18:59:00 -08:00
it('get block time', async () => {
await mockRpcResponse({
2020-05-18 20:27:36 -07:00
method: 'getBlockTime',
params: [1],
2021-02-05 18:59:00 -08:00
value: 10000,
});
2020-05-18 20:27:36 -07:00
await mockRpcResponse({
method: 'getFirstAvailableBlock',
params: [],
value: 1,
});
const slot = await connection.getFirstAvailableBlock();
const blockTime = await connection.getBlockTime(slot);
2021-02-05 18:59:00 -08:00
if (blockTime === null) {
expect(blockTime).not.to.be.null;
} else {
expect(blockTime).to.be.greaterThan(0);
}
});
2020-05-21 01:58:17 -07:00
2021-02-05 18:59:00 -08:00
it('get minimum ledger slot', async () => {
await mockRpcResponse({
2020-05-21 01:58:17 -07:00
method: 'minimumLedgerSlot',
params: [],
2021-02-05 18:59:00 -08:00
value: 0,
});
2020-05-21 01:58:17 -07:00
2021-02-05 18:59:00 -08:00
const minimumLedgerSlot = await connection.getMinimumLedgerSlot();
expect(minimumLedgerSlot).to.be.at.least(0);
});
2021-02-05 18:59:00 -08:00
it('get first available block', async () => {
await mockRpcResponse({
method: 'getFirstAvailableBlock',
params: [],
2021-02-05 18:59:00 -08:00
value: 0,
});
2021-02-05 18:59:00 -08:00
const firstAvailableBlock = await connection.getFirstAvailableBlock();
expect(firstAvailableBlock).to.be.at.least(0);
});
2021-02-05 18:59:00 -08:00
it('get supply', async () => {
await mockRpcResponse({
method: 'getSupply',
params: [{commitment: 'finalized'}],
2021-02-05 18:59:00 -08:00
value: {
total: 1000,
circulating: 100,
nonCirculating: 900,
nonCirculatingAccounts: [Keypair.generate().publicKey.toBase58()],
},
2021-02-05 18:59:00 -08:00
withContext: true,
});
const supply = (await connection.getSupply('finalized')).value;
2021-02-05 18:59:00 -08:00
expect(supply.total).to.be.greaterThan(0);
expect(supply.circulating).to.be.greaterThan(0);
expect(supply.nonCirculating).to.be.at.least(0);
expect(supply.nonCirculatingAccounts.length).to.be.at.least(0);
});
it('get supply without accounts', async () => {
await mockRpcResponse({
method: 'getSupply',
params: [{commitment: 'finalized'}],
value: {
total: 1000,
circulating: 100,
nonCirculating: 900,
nonCirculatingAccounts: [],
},
withContext: true,
});
const supply = (
await connection.getSupply({
commitment: 'finalized',
excludeNonCirculatingAccountsList: true,
})
).value;
expect(supply.total).to.be.greaterThan(0);
expect(supply.circulating).to.be.greaterThan(0);
expect(supply.nonCirculating).to.be.at.least(0);
expect(supply.nonCirculatingAccounts.length).to.eq(0);
});
2021-02-05 18:59:00 -08:00
it('get performance samples', async () => {
await mockRpcResponse({
method: 'getRecentPerformanceSamples',
params: [],
value: [
{
slot: 1234,
numTransactions: 1000,
numSlots: 60,
samplePeriodSecs: 60,
},
],
});
2021-02-05 18:59:00 -08:00
const perfSamples = await connection.getRecentPerformanceSamples();
expect(Array.isArray(perfSamples)).to.be.true;
2021-02-05 18:59:00 -08:00
if (perfSamples.length > 0) {
expect(perfSamples[0].slot).to.be.greaterThan(0);
expect(perfSamples[0].numTransactions).to.be.greaterThan(0);
expect(perfSamples[0].numSlots).to.be.greaterThan(0);
expect(perfSamples[0].samplePeriodSecs).to.be.greaterThan(0);
}
});
2021-02-05 18:59:00 -08:00
it('get performance samples limit too high', async () => {
await mockRpcResponse({
method: 'getRecentPerformanceSamples',
params: [100000],
error: mockErrorResponse,
});
2021-02-05 18:59:00 -08:00
await expect(connection.getRecentPerformanceSamples(100000)).to.be.rejected;
});
2021-02-05 18:59:00 -08:00
const TOKEN_PROGRAM_ID = new PublicKey(
'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
);
2020-07-30 21:33:54 -07:00
2021-02-05 18:59:00 -08:00
if (process.env.TEST_LIVE) {
describe('token methods', () => {
const connection = new Connection(url, 'confirmed');
const newAccount = Keypair.generate().publicKey;
2020-07-30 21:33:54 -07:00
2021-02-05 18:59:00 -08:00
let testToken: Token;
let testTokenPubkey: PublicKey;
2021-02-05 18:59:00 -08:00
let testTokenAccount: PublicKey;
let testSignature: TransactionSignature;
let testOwner: Account;
2021-02-05 18:59:00 -08:00
// Setup token mints and accounts for token tests
before(async function () {
this.timeout(30 * 1000);
2021-02-05 18:59:00 -08:00
const payerAccount = new Account();
await connection.confirmTransaction(
await connection.requestAirdrop(payerAccount.publicKey, 100000000000),
);
2020-07-30 21:33:54 -07:00
2021-02-05 18:59:00 -08:00
const mintOwner = new Account();
const accountOwner = new Account();
const token = await Token.createMint(
connection as any,
2021-02-05 18:59:00 -08:00
payerAccount,
mintOwner.publicKey,
null,
2,
TOKEN_PROGRAM_ID,
);
2020-07-30 21:33:54 -07:00
2021-02-05 18:59:00 -08:00
const tokenAccount = await token.createAccount(accountOwner.publicKey);
await token.mintTo(tokenAccount, mintOwner, [], 11111);
2020-09-01 10:58:40 -07:00
2021-02-05 18:59:00 -08:00
const token2 = await Token.createMint(
connection as any,
2021-02-05 18:59:00 -08:00
payerAccount,
mintOwner.publicKey,
null,
2,
TOKEN_PROGRAM_ID,
);
2020-07-30 21:33:54 -07:00
2021-02-05 18:59:00 -08:00
const token2Account = await token2.createAccount(
accountOwner.publicKey,
);
await token2.mintTo(token2Account, mintOwner, [], 100);
2020-09-01 10:58:40 -07:00
2021-02-05 18:59:00 -08:00
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, 'finalized');
2021-02-05 18:59:00 -08:00
testOwner = accountOwner;
testToken = token;
2021-03-14 22:08:10 -07:00
testTokenAccount = tokenAccount as PublicKey;
testTokenPubkey = testToken.publicKey as PublicKey;
2021-02-05 18:59:00 -08:00
});
it('get token supply', async () => {
const supply = (await connection.getTokenSupply(testTokenPubkey)).value;
2021-02-05 18:59:00 -08:00
expect(supply.uiAmount).to.eq(111.11);
expect(supply.decimals).to.eq(2);
expect(supply.amount).to.eq('11111');
await expect(connection.getTokenSupply(newAccount)).to.be.rejected;
});
it('get token largest accounts', async () => {
const largestAccounts = (
await connection.getTokenLargestAccounts(testTokenPubkey)
2021-02-05 18:59:00 -08:00
).value;
expect(largestAccounts).to.have.length(2);
const largestAccount = largestAccounts[0];
expect(largestAccount.address).to.eql(testTokenAccount);
expect(largestAccount.amount).to.eq('11110');
expect(largestAccount.decimals).to.eq(2);
expect(largestAccount.uiAmount).to.eq(111.1);
await expect(connection.getTokenLargestAccounts(newAccount)).to.be
.rejected;
});
it('get confirmed token transaction', async () => {
const parsedTx = await connection.getParsedConfirmedTransaction(
testSignature,
);
if (parsedTx === null) {
expect(parsedTx).not.to.be.null;
return;
}
const {signatures, message} = parsedTx.transaction;
expect(signatures[0]).to.eq(testSignature);
const ix = message.instructions[0];
2021-03-14 22:08:10 -07:00
if ('parsed' in ix) {
2021-02-05 18:59:00 -08:00
expect(ix.program).to.eq('spl-token');
expect(ix.programId).to.eql(TOKEN_PROGRAM_ID);
} else {
expect('parsed' in ix).to.be.true;
}
const missingSignature =
'45pGoC4Rr3fJ1TKrsiRkhHRbdUeX7633XAGVec6XzVdpRbzQgHhe6ZC6Uq164MPWtiqMg7wCkC6Wy3jy2BqsDEKf';
const nullResponse = await connection.getParsedConfirmedTransaction(
missingSignature,
);
2020-07-30 21:33:54 -07:00
2021-02-05 18:59:00 -08:00
expect(nullResponse).to.be.null;
});
it('get token account balance', async () => {
const balance = (
await connection.getTokenAccountBalance(testTokenAccount)
).value;
expect(balance.amount).to.eq('11110');
expect(balance.decimals).to.eq(2);
expect(balance.uiAmount).to.eq(111.1);
await expect(connection.getTokenAccountBalance(newAccount)).to.be
.rejected;
});
it('get parsed token account info', async () => {
const accountInfo = (
await connection.getParsedAccountInfo(testTokenAccount)
).value;
if (accountInfo) {
const data = accountInfo.data;
if (Buffer.isBuffer(data)) {
expect(Buffer.isBuffer(data)).to.eq(false);
2021-02-05 18:59:00 -08:00
} else {
expect(data.program).to.eq('spl-token');
expect(data.parsed).to.be.ok;
}
}
});
it('get parsed token program accounts', async () => {
const tokenAccounts = await connection.getParsedProgramAccounts(
TOKEN_PROGRAM_ID,
);
tokenAccounts.forEach(({account}) => {
expect(account.owner).to.eql(TOKEN_PROGRAM_ID);
const data = account.data;
if (Buffer.isBuffer(data)) {
expect(Buffer.isBuffer(data)).to.eq(false);
2021-02-05 18:59:00 -08:00
} else {
expect(data.parsed).to.be.ok;
expect(data.program).to.eq('spl-token');
}
});
});
it('get parsed token accounts by owner', async () => {
const tokenAccounts = (
await connection.getParsedTokenAccountsByOwner(testOwner.publicKey, {
mint: testTokenPubkey,
2021-02-05 18:59:00 -08:00
})
).value;
tokenAccounts.forEach(({account}) => {
expect(account.owner).to.eql(TOKEN_PROGRAM_ID);
const data = account.data;
if (Buffer.isBuffer(data)) {
expect(Buffer.isBuffer(data)).to.eq(false);
2021-02-05 18:59:00 -08:00
} else {
expect(data.parsed).to.be.ok;
expect(data.program).to.eq('spl-token');
}
});
});
it('get token accounts by owner', async () => {
const accountsWithMintFilter = (
await connection.getTokenAccountsByOwner(testOwner.publicKey, {
mint: testTokenPubkey,
2021-02-05 18:59:00 -08:00
})
).value;
expect(accountsWithMintFilter).to.have.length(2);
const accountsWithProgramFilter = (
await connection.getTokenAccountsByOwner(testOwner.publicKey, {
programId: TOKEN_PROGRAM_ID,
})
).value;
expect(accountsWithProgramFilter).to.have.length(3);
const noAccounts = (
await connection.getTokenAccountsByOwner(newAccount, {
mint: testTokenPubkey,
2021-02-05 18:59:00 -08:00
})
).value;
expect(noAccounts).to.have.length(0);
await expect(
connection.getTokenAccountsByOwner(testOwner.publicKey, {
mint: newAccount,
}),
).to.be.rejected;
await expect(
connection.getTokenAccountsByOwner(testOwner.publicKey, {
programId: newAccount,
}),
).to.be.rejected;
});
});
it('consistent preflightCommitment', async () => {
const connection = new Connection(url, 'singleGossip');
const sender = Keypair.generate();
const recipient = Keypair.generate();
let signature = await connection.requestAirdrop(
sender.publicKey,
2 * LAMPORTS_PER_SOL,
);
await connection.confirmTransaction(signature, 'singleGossip');
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: sender.publicKey,
toPubkey: recipient.publicKey,
lamports: LAMPORTS_PER_SOL,
}),
);
await sendAndConfirmTransaction(connection, transaction, [sender]);
});
2021-02-05 18:59:00 -08:00
}
2020-07-30 21:33:54 -07:00
2021-02-05 18:59:00 -08:00
it('get largest accounts', async () => {
await mockRpcResponse({
method: 'getLargestAccounts',
params: [],
value: new Array(20).fill(0).map(() => ({
address: Keypair.generate().publicKey.toBase58(),
2021-02-05 18:59:00 -08:00
lamports: 1000,
})),
withContext: true,
});
2020-07-30 21:33:54 -07:00
2021-02-05 18:59:00 -08:00
const largestAccounts = (await connection.getLargestAccounts()).value;
expect(largestAccounts).to.have.length(20);
});
2020-07-30 21:33:54 -07:00
2021-02-05 18:59:00 -08:00
it('stake activation should throw when called for not delegated account', async () => {
const publicKey = Keypair.generate().publicKey;
2021-02-05 18:59:00 -08:00
await mockRpcResponse({
method: 'getStakeActivation',
params: [publicKey.toBase58(), {}],
error: {message: 'account not delegated'},
});
2021-02-05 18:59:00 -08:00
await expect(connection.getStakeActivation(publicKey)).to.be.rejected;
});
2021-02-05 18:59:00 -08:00
if (process.env.TEST_LIVE) {
it('stake activation should return activating for new accounts', async () => {
const voteAccounts = await connection.getVoteAccounts();
const voteAccount = voteAccounts.current.concat(
voteAccounts.delinquent,
)[0];
const votePubkey = new PublicKey(voteAccount.votePubkey);
const authorized = Keypair.generate();
2021-02-05 18:59:00 -08:00
let signature = await connection.requestAirdrop(
authorized.publicKey,
2 * LAMPORTS_PER_SOL,
);
await connection.confirmTransaction(signature, 'confirmed');
2020-07-30 21:33:54 -07:00
2021-02-05 18:59:00 -08:00
const minimumAmount = await connection.getMinimumBalanceForRentExemption(
StakeProgram.space,
);
2020-07-30 21:33:54 -07:00
const newStakeAccount = Keypair.generate();
2021-02-05 18:59:00 -08:00
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],
{
preflightCommitment: 'confirmed',
commitment: 'confirmed',
2021-02-05 18:59:00 -08:00
},
);
let delegation = StakeProgram.delegate({
stakePubkey: newStakeAccount.publicKey,
authorizedPubkey: authorized.publicKey,
votePubkey,
});
await sendAndConfirmTransaction(connection, delegation, [authorized], {
preflightCommitment: 'confirmed',
commitment: 'confirmed',
2021-02-05 18:59:00 -08:00
});
const LARGE_EPOCH = 4000;
await expect(
connection.getStakeActivation(
newStakeAccount.publicKey,
'confirmed',
2021-02-05 18:59:00 -08:00
LARGE_EPOCH,
),
).to.be.rejectedWith(
`failed to get Stake Activation ${newStakeAccount.publicKey.toBase58()}: Invalid param: epoch ${LARGE_EPOCH} has not yet started`,
);
2020-07-30 21:33:54 -07:00
2021-02-05 18:59:00 -08:00
const activationState = await connection.getStakeActivation(
newStakeAccount.publicKey,
'confirmed',
2021-02-05 18:59:00 -08:00
);
expect(activationState.state).to.eq('activating');
expect(activationState.inactive).to.eq(42);
expect(activationState.active).to.eq(0);
});
}
2021-03-14 22:08:10 -07:00
if (mockServer) {
2021-02-05 18:59:00 -08:00
it('stake activation should only accept state with valid string literals', async () => {
const publicKey = Keypair.generate().publicKey;
2021-02-05 18:59:00 -08:00
2021-03-14 22:08:10 -07:00
const addStakeActivationMock = async (state: any) => {
2021-02-05 18:59:00 -08:00
await mockRpcResponse({
method: 'getStakeActivation',
params: [publicKey.toBase58(), {}],
value: {
state: state,
active: 0,
inactive: 80,
},
});
};
2021-02-05 18:59:00 -08:00
await addStakeActivationMock('active');
let activation = await connection.getStakeActivation(
publicKey,
'confirmed',
2021-02-05 18:59:00 -08:00
);
expect(activation.state).to.eq('active');
expect(activation.active).to.eq(0);
expect(activation.inactive).to.eq(80);
2020-08-06 08:47:22 -07:00
2021-02-05 18:59:00 -08:00
await addStakeActivationMock('invalid');
await expect(connection.getStakeActivation(publicKey, 'confirmed')).to.be
.rejected;
2020-08-06 08:47:22 -07:00
});
2021-02-05 18:59:00 -08:00
}
2020-08-06 08:47:22 -07:00
2021-02-05 18:59:00 -08:00
it('getVersion', async () => {
await mockRpcResponse({
method: 'getVersion',
params: [],
value: {'solana-core': '0.20.4'},
2020-08-06 08:47:22 -07:00
});
2021-02-05 18:59:00 -08:00
const version = await connection.getVersion();
expect(version['solana-core']).to.be.ok;
2020-08-06 08:47:22 -07:00
});
it('getGenesisHash', async () => {
await mockRpcResponse({
method: 'getGenesisHash',
params: [],
value: 'GH7ome3EiwEr7tu9JuTh2dpYWBJK3z69Xm1ZE3MEE6JC',
});
const genesisHash = await connection.getGenesisHash();
expect(genesisHash).not.to.be.empty;
});
2021-02-05 18:59:00 -08:00
it('request airdrop', async () => {
const account = Keypair.generate();
2021-02-05 18:59:00 -08:00
await helpers.airdrop({
connection,
address: account.publicKey,
amount: LAMPORTS_PER_SOL,
});
2021-02-05 18:59:00 -08:00
await mockRpcResponse({
method: 'getBalance',
params: [account.publicKey.toBase58(), {commitment: 'confirmed'}],
2021-02-05 18:59:00 -08:00
value: LAMPORTS_PER_SOL,
withContext: true,
});
const balance = await connection.getBalance(account.publicKey, 'confirmed');
2021-02-05 18:59:00 -08:00
expect(balance).to.eq(LAMPORTS_PER_SOL);
2021-02-05 18:59:00 -08:00
await mockRpcResponse({
method: 'getAccountInfo',
params: [
account.publicKey.toBase58(),
{commitment: 'confirmed', encoding: 'base64'},
2021-02-05 18:59:00 -08:00
],
value: {
owner: '11111111111111111111111111111111',
lamports: LAMPORTS_PER_SOL,
data: ['', 'base64'],
executable: false,
rentEpoch: 20,
2021-02-05 18:59:00 -08:00
},
withContext: true,
});
2020-07-30 21:33:54 -07:00
2021-02-05 18:59:00 -08:00
const accountInfo = await connection.getAccountInfo(
account.publicKey,
'confirmed',
2021-02-05 18:59:00 -08:00
);
if (accountInfo === null) {
expect(accountInfo).not.to.be.null;
return;
}
expect(accountInfo.lamports).to.eq(LAMPORTS_PER_SOL);
expect(accountInfo.data).to.have.length(0);
expect(accountInfo.owner).to.eql(SystemProgram.programId);
2020-05-22 10:23:29 -07:00
2021-02-05 18:59:00 -08:00
await mockRpcResponse({
2020-08-06 08:47:22 -07:00
method: 'getAccountInfo',
params: [
account.publicKey.toBase58(),
{commitment: 'confirmed', encoding: 'jsonParsed'},
2020-08-06 08:47:22 -07:00
],
2021-02-05 18:59:00 -08:00
value: {
owner: '11111111111111111111111111111111',
lamports: LAMPORTS_PER_SOL,
data: ['', 'base64'],
executable: false,
rentEpoch: 20,
2020-08-06 08:47:22 -07:00
},
2021-02-05 18:59:00 -08:00
withContext: true,
});
2020-04-04 23:54:48 -07:00
2021-02-05 18:59:00 -08:00
const parsedAccountInfo = (
await connection.getParsedAccountInfo(account.publicKey, 'confirmed')
2021-02-05 18:59:00 -08:00
).value;
if (parsedAccountInfo === null) {
expect(parsedAccountInfo).not.to.be.null;
return;
2021-03-14 22:08:10 -07:00
} else if ('parsed' in parsedAccountInfo.data) {
2021-02-05 18:59:00 -08:00
expect(parsedAccountInfo.data.parsed).not.to.be.ok;
return;
}
expect(parsedAccountInfo.lamports).to.eq(LAMPORTS_PER_SOL);
expect(parsedAccountInfo.data).to.have.length(0);
expect(parsedAccountInfo.owner).to.eql(SystemProgram.programId);
});
2020-07-22 06:58:04 -07:00
2021-02-05 18:59:00 -08:00
it('transaction failure', async () => {
const payer = Keypair.generate();
2020-07-22 06:58:04 -07:00
2021-02-05 18:59:00 -08:00
await helpers.airdrop({
connection,
address: payer.publicKey,
amount: LAMPORTS_PER_SOL,
});
2020-04-04 23:54:48 -07:00
const newAccount = Keypair.generate();
2021-02-05 18:59:00 -08:00
let transaction = new Transaction().add(
SystemProgram.createAccount({
fromPubkey: payer.publicKey,
newAccountPubkey: newAccount.publicKey,
lamports: LAMPORTS_PER_SOL / 2,
space: 0,
programId: SystemProgram.programId,
}),
);
2021-02-05 18:59:00 -08:00
await helpers.processTransaction({
connection,
transaction,
signers: [payer, newAccount],
commitment: 'confirmed',
2021-02-05 18:59:00 -08:00
});
2020-06-03 04:55:42 -07:00
2021-02-05 18:59:00 -08:00
// This should fail because the account is already created
const expectedErr = {InstructionError: [0, {Custom: 0}]};
const confirmResult = (
await helpers.processTransaction({
connection,
transaction,
signers: [payer, newAccount],
commitment: 'confirmed',
2021-02-05 18:59:00 -08:00
err: expectedErr,
})
).value;
expect(confirmResult.err).to.eql(expectedErr);
2020-04-04 23:54:48 -07:00
2021-03-14 22:08:10 -07:00
invariant(transaction.signature);
2021-02-05 18:59:00 -08:00
const signature = bs58.encode(transaction.signature);
await mockRpcResponse({
2020-04-04 23:54:48 -07:00
method: 'getSignatureStatuses',
2021-02-05 18:59:00 -08:00
params: [[signature]],
value: [
{
slot: 0,
confirmations: 11,
status: {Err: expectedErr},
err: expectedErr,
2020-01-08 12:59:58 -08:00
},
2021-02-05 18:59:00 -08:00
],
withContext: true,
});
2021-02-05 18:59:00 -08:00
const response = (await connection.getSignatureStatus(signature)).value;
verifySignatureStatus(response, expectedErr);
});
2020-06-11 00:00:59 -07:00
2021-02-05 18:59:00 -08:00
if (process.env.TEST_LIVE) {
it('simulate transaction with message', async () => {
connection._commitment = 'confirmed';
const account1 = Keypair.generate();
const account2 = Keypair.generate();
await helpers.airdrop({
connection,
address: account1.publicKey,
amount: LAMPORTS_PER_SOL,
});
await helpers.airdrop({
connection,
address: account2.publicKey,
amount: LAMPORTS_PER_SOL,
});
const recentBlockhash = await (
await helpers.latestBlockhash({connection})
).blockhash;
const message = new Message({
accountKeys: [
account1.publicKey.toString(),
account2.publicKey.toString(),
'Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo',
],
header: {
numReadonlySignedAccounts: 1,
numReadonlyUnsignedAccounts: 2,
numRequiredSignatures: 1,
},
instructions: [
{
accounts: [0, 1],
data: bs58.encode(Buffer.alloc(5).fill(9)),
programIdIndex: 2,
},
],
recentBlockhash,
});
const results1 = await connection.simulateTransaction(
message,
[account1],
true,
);
expect(results1.value.accounts).lengthOf(2);
const results2 = await connection.simulateTransaction(
message,
[account1],
[
account1.publicKey,
new PublicKey('Missing111111111111111111111111111111111111'),
],
);
expect(results2.value.accounts).lengthOf(2);
if (results2.value.accounts) {
expect(results2.value.accounts[1]).to.be.null;
}
}).timeout(10000);
2021-02-05 18:59:00 -08:00
it('transaction', async () => {
connection._commitment = 'confirmed';
2020-06-11 00:00:59 -07:00
const accountFrom = Keypair.generate();
const accountTo = Keypair.generate();
2021-02-05 18:59:00 -08:00
const minimumAmount = await connection.getMinimumBalanceForRentExemption(
0,
);
2020-06-11 00:00:59 -07:00
2021-02-05 18:59:00 -08:00
await helpers.airdrop({
connection,
address: accountFrom.publicKey,
amount: minimumAmount + 100010,
});
await helpers.airdrop({
connection,
address: accountTo.publicKey,
amount: minimumAmount,
});
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: accountFrom.publicKey,
toPubkey: accountTo.publicKey,
lamports: 10,
}),
);
2020-03-26 06:37:45 -07:00
2021-02-05 18:59:00 -08:00
const signature = await sendAndConfirmTransaction(
connection,
transaction,
[accountFrom],
{preflightCommitment: 'confirmed'},
2021-02-05 18:59:00 -08:00
);
2021-02-05 18:59:00 -08: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,
}),
);
2018-08-23 10:52:48 -07:00
2021-02-05 18:59:00 -08:00
const signature2 = await sendAndConfirmTransaction(
connection,
transaction2,
[accountFrom],
{preflightCommitment: 'confirmed'},
2021-02-05 18:59:00 -08:00
);
2021-02-05 18:59:00 -08:00
expect(signature).not.to.eq(signature2);
expect(transaction.recentBlockhash).not.to.eq(
transaction2.recentBlockhash,
);
2021-02-05 18:59:00 -08: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,
}),
);
2021-02-07 08:57:12 -08:00
await sendAndConfirmTransaction(connection, transaction3, [accountFrom], {
preflightCommitment: 'confirmed',
2021-02-07 08:57:12 -08:00
});
2021-02-05 18:59:00 -08:00
expect(transaction2.recentBlockhash).to.eq(transaction3.recentBlockhash);
// Sleep until blockhash cache times out
await sleep(
Math.max(
0,
1000 + BLOCKHASH_CACHE_TIMEOUT_MS - (Date.now() - lastFetch),
),
);
2021-02-05 18:59:00 -08:00
const transaction4 = new Transaction().add(
SystemProgram.transfer({
fromPubkey: accountFrom.publicKey,
toPubkey: accountTo.publicKey,
lamports: 13,
}),
);
2021-02-05 18:59:00 -08:00
await sendAndConfirmTransaction(connection, transaction4, [accountFrom], {
preflightCommitment: 'confirmed',
2021-02-05 18:59:00 -08:00
});
2021-02-05 18:59:00 -08:00
expect(transaction4.recentBlockhash).not.to.eq(
transaction3.recentBlockhash,
);
2021-02-05 18:59:00 -08:00
// accountFrom may have less than 100000 due to transaction fees
const balance = await connection.getBalance(accountFrom.publicKey);
expect(balance).to.be.greaterThan(0);
expect(balance).to.be.at.most(minimumAmount + 100000);
expect(await connection.getBalance(accountTo.publicKey)).to.eq(
minimumAmount + 42,
);
}).timeout(45 * 1000); // waits 30s for cache timeout
2021-02-05 18:59:00 -08:00
it('multi-instruction transaction', async () => {
connection._commitment = 'confirmed';
2020-03-23 08:01:12 -07:00
const accountFrom = Keypair.generate();
const accountTo = Keypair.generate();
2021-02-05 18:59:00 -08:00
let signature = await connection.requestAirdrop(
accountFrom.publicKey,
LAMPORTS_PER_SOL,
);
await connection.confirmTransaction(signature);
expect(await connection.getBalance(accountFrom.publicKey)).to.eq(
LAMPORTS_PER_SOL,
);
2021-02-05 18:59:00 -08:00
const minimumAmount = await connection.getMinimumBalanceForRentExemption(
0,
);
2021-02-05 18:59:00 -08:00
signature = await connection.requestAirdrop(
accountTo.publicKey,
minimumAmount + 21,
);
await connection.confirmTransaction(signature);
expect(await connection.getBalance(accountTo.publicKey)).to.eq(
minimumAmount + 21,
);
2018-10-26 21:37:39 -07:00
2021-02-05 18:59:00 -08:00
// 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},
);
2018-10-26 21:37:39 -07:00
2021-02-05 18:59:00 -08:00
await connection.confirmTransaction(signature);
2018-10-26 21:37:39 -07:00
2021-02-05 18:59:00 -08:00
const response = (await connection.getSignatureStatus(signature)).value;
if (response !== null) {
expect(typeof response.slot).to.eq('number');
expect(response.err).to.be.null;
} else {
expect(response).not.to.be.null;
}
2018-10-26 21:37:39 -07:00
2021-02-05 18:59:00 -08:00
// accountFrom may have less than LAMPORTS_PER_SOL due to transaction fees
expect(
await connection.getBalance(accountFrom.publicKey),
).to.be.greaterThan(0);
expect(await connection.getBalance(accountFrom.publicKey)).to.be.at.most(
LAMPORTS_PER_SOL,
);
2021-02-05 18:59:00 -08:00
expect(await connection.getBalance(accountTo.publicKey)).to.eq(
minimumAmount + 21,
);
2020-06-03 04:55:42 -07:00
});
2018-11-16 19:29:14 -08:00
it('account change notification', async () => {
if (mockServer) {
console.log('non-live test skipped');
return;
}
const connection = new Connection(url, 'confirmed');
const owner = Keypair.generate();
let subscriptionId;
try {
const notificationPromise = new Promise<AccountInfo<Buffer>>(
resolve => {
subscriptionId = connection.onAccountChange(
owner.publicKey,
resolve,
'confirmed',
);
},
);
connection.requestAirdrop(owner.publicKey, LAMPORTS_PER_SOL);
const notificationPayload = await notificationPromise;
expect(notificationPayload.lamports).to.eq(LAMPORTS_PER_SOL);
expect(notificationPayload.owner.equals(SystemProgram.programId)).to.be
.true;
} finally {
if (subscriptionId != null) {
connection.removeAccountChangeListener(subscriptionId);
}
}
});
2021-02-05 18:59:00 -08:00
it('program account change notification', async () => {
connection._commitment = 'confirmed';
2021-02-05 18:59:00 -08:00
const owner = Keypair.generate();
const programAccount = Keypair.generate();
2021-02-05 18:59:00 -08:00
const balanceNeeded = await connection.getMinimumBalanceForRentExemption(
0,
);
2021-02-05 18:59:00 -08:00
let notified = false;
const subscriptionId = connection.onProgramAccountChange(
SystemProgram.programId,
2021-02-05 18:59:00 -08:00
(keyedAccountInfo: KeyedAccountInfo) => {
if (keyedAccountInfo.accountId.equals(programAccount.publicKey)) {
2021-02-05 18:59:00 -08:00
expect(keyedAccountInfo.accountInfo.lamports).to.eq(balanceNeeded);
expect(
keyedAccountInfo.accountInfo.owner.equals(
SystemProgram.programId,
),
).to.be.true;
notified = true;
}
},
);
2021-02-05 18:59:00 -08:00
await helpers.airdrop({
connection,
address: owner.publicKey,
amount: 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: 'confirmed',
2021-02-05 18:59:00 -08:00
});
} catch (err) {
await connection.removeProgramAccountChangeListener(subscriptionId);
throw err;
}
2021-02-05 18:59:00 -08:00
let i = 0;
while (!notified) {
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);
}
2021-02-05 18:59:00 -08:00
await connection.removeProgramAccountChangeListener(subscriptionId);
});
2020-03-27 07:22:53 -07:00
2021-02-05 18:59:00 -08:00
it('slot notification', async () => {
2021-03-14 22:08:10 -07:00
let notifiedSlotInfo: SlotInfo | undefined;
2021-02-05 18:59:00 -08:00
const subscriptionId = connection.onSlotChange(slotInfo => {
notifiedSlotInfo = slotInfo;
});
// Wait for notification
let i = 0;
while (!notifiedSlotInfo) {
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);
}
2020-03-27 07:22:53 -07:00
2021-02-05 18:59:00 -08:00
expect(notifiedSlotInfo.parent).to.be.at.least(0);
expect(notifiedSlotInfo.root).to.be.at.least(0);
expect(notifiedSlotInfo.slot).to.be.at.least(1);
2020-03-27 07:22:53 -07:00
2021-02-05 18:59:00 -08:00
await connection.removeSlotChangeListener(subscriptionId);
});
2020-03-27 07:22:53 -07:00
2021-02-05 18:59:00 -08:00
it('root notification', async () => {
2021-03-14 22:08:10 -07:00
let roots: number[] = [];
2021-02-05 18:59:00 -08:00
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);
}
2020-03-27 07:22:53 -07:00
2021-02-05 18:59:00 -08:00
expect(roots[1]).to.be.greaterThan(roots[0]);
await connection.removeRootChangeListener(subscriptionId);
});
it('logs notification', async () => {
let listener: number | undefined;
const owner = Keypair.generate();
const [logsRes, ctx] = await new Promise(resolve => {
let received = false;
listener = connection.onLogs(
owner.publicKey,
(logs, ctx) => {
if (!logs.err) {
received = true;
resolve([logs, ctx]);
}
},
'processed',
);
// Send transactions until the log subscription receives an event
(async () => {
while (!received) {
// Execute a transaction so that we can pickup its logs.
2022-04-08 16:17:21 -07:00
await connection.requestAirdrop(
owner.publicKey,
1 * LAMPORTS_PER_SOL,
);
2022-01-21 13:01:48 -08:00
await sleep(1000);
}
})();
});
expect(ctx.slot).to.be.greaterThan(0);
expect(logsRes.logs.length).to.eq(2);
expect(logsRes.logs[0]).to.eq(
'Program 11111111111111111111111111111111 invoke [1]',
);
expect(logsRes.logs[1]).to.eq(
'Program 11111111111111111111111111111111 success',
);
await connection.removeOnLogsListener(listener!);
2022-01-21 13:01:48 -08:00
}).timeout(60 * 1000);
2021-02-05 18:59:00 -08:00
it('https request', async () => {
const connection = new Connection('https://api.mainnet-beta.solana.com');
2021-02-05 18:59:00 -08:00
const version = await connection.getVersion();
expect(version['solana-core']).to.be.ok;
2022-01-21 13:01:48 -08:00
}).timeout(20 * 1000);
}
});