feat: the web3.js getBlock APIs now accept `rewards` and `transactionDetails` config (#29000)

* Add `transactionDetails` and `rewards` params to `getBlock` API of web3.js

* Add the same content to the legacy call

…because it's such a PITA to share config between two methods and not have Typedoc throw a fit.

* Add tests to exercise block deserialization in the case that `transactionDetails` is `none` or `accounts`

* Extract the annotated account key parser into a separate struct

* Parse the `getBlock()` responses differently depending on the mode
This commit is contained in:
Steven Luscher 2022-12-01 22:04:47 -08:00 committed by GitHub
parent 5850af5316
commit b112d01a5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 727 additions and 63 deletions

View File

@ -23,6 +23,7 @@ import {
import type {Struct} from 'superstruct';
import {Client as RpcWebSocketClient} from 'rpc-websockets';
import RpcClient from 'jayson/lib/client/browser';
import {JSONRPCError} from 'jayson';
import {AgentManager} from './agent-manager';
import {EpochSchedule} from './epoch-schedule';
@ -517,6 +518,18 @@ export type GetBalanceConfig = {
export type GetBlockConfig = {
/** The level of finality desired */
commitment?: Finality;
/**
* Whether to populate the rewards array. If parameter not provided, the default includes rewards.
*/
rewards?: boolean;
/**
* Level of transaction detail to return, either "full", "accounts", "signatures", or "none". If
* parameter not provided, the default detail level is "full". If "accounts" are requested,
* transaction details only include signatures and an annotated list of accounts in each
* transaction. Transaction metadata is limited to only: fee, err, pre_balances, post_balances,
* pre_token_balances, and post_token_balances.
*/
transactionDetails?: 'accounts' | 'full' | 'none' | 'signatures';
};
/**
@ -527,6 +540,18 @@ export type GetVersionedBlockConfig = {
commitment?: Finality;
/** The max transaction version to return in responses. If the requested transaction is a higher version, an error will be returned */
maxSupportedTransactionVersion?: number;
/**
* Whether to populate the rewards array. If parameter not provided, the default includes rewards.
*/
rewards?: boolean;
/**
* Level of transaction detail to return, either "full", "accounts", "signatures", or "none". If
* parameter not provided, the default detail level is "full". If "accounts" are requested,
* transaction details only include signatures and an annotated list of accounts in each
* transaction. Transaction metadata is limited to only: fee, err, pre_balances, post_balances,
* pre_token_balances, and post_token_balances.
*/
transactionDetails?: 'accounts' | 'full' | 'none' | 'signatures';
};
/**
@ -1172,6 +1197,16 @@ export type BlockResponse = {
blockTime: number | null;
};
/**
* A processed block fetched from the RPC API where the `transactionDetails` mode is `accounts`
*/
export type AccountsModeBlockResponse = VersionedAccountsModeBlockResponse;
/**
* A processed block fetched from the RPC API where the `transactionDetails` mode is `none`
*/
export type NoneModeBlockResponse = VersionedNoneModeBlockResponse;
/**
* A block with parsed transactions
*/
@ -1208,6 +1243,33 @@ export type ParsedBlockResponse = {
blockHeight: number | null;
};
/**
* A block with parsed transactions where the `transactionDetails` mode is `accounts`
*/
export type ParsedAccountsModeBlockResponse = Omit<
ParsedBlockResponse,
'transactions'
> & {
transactions: Array<
Omit<ParsedBlockResponse['transactions'][number], 'transaction'> & {
transaction: Pick<
ParsedBlockResponse['transactions'][number]['transaction'],
'signatures'
> & {
accountKeys: ParsedMessageAccount[];
};
}
>;
};
/**
* A block with parsed transactions where the `transactionDetails` mode is `none`
*/
export type ParsedNoneModeBlockResponse = Omit<
ParsedBlockResponse,
'transactions'
>;
/**
* A processed block fetched from the RPC API
*/
@ -1247,6 +1309,33 @@ export type VersionedBlockResponse = {
blockTime: number | null;
};
/**
* A processed block fetched from the RPC API where the `transactionDetails` mode is `accounts`
*/
export type VersionedAccountsModeBlockResponse = Omit<
VersionedBlockResponse,
'transactions'
> & {
transactions: Array<
Omit<VersionedBlockResponse['transactions'][number], 'transaction'> & {
transaction: Pick<
VersionedBlockResponse['transactions'][number]['transaction'],
'signatures'
> & {
accountKeys: ParsedMessageAccount[];
};
}
>;
};
/**
* A processed block fetched from the RPC API where the `transactionDetails` mode is `none`
*/
export type VersionedNoneModeBlockResponse = Omit<
VersionedBlockResponse,
'transactions'
>;
/**
* A confirmed block on the ledger
*
@ -1980,6 +2069,18 @@ const ConfirmedTransactionResult = pick({
}),
});
const AnnotatedAccountKey = pick({
pubkey: PublicKeyFromString,
signer: boolean(),
writable: boolean(),
source: optional(union([literal('transaction'), literal('lookupTable')])),
});
const ConfirmedTransactionAccountsModeResult = pick({
accountKeys: array(AnnotatedAccountKey),
signatures: array(string()),
});
const ParsedInstructionResult = pick({
parsed: unknown(),
program: string(),
@ -2028,16 +2129,7 @@ const ParsedOrRawInstruction = coerce(
const ParsedConfirmedTransactionResult = pick({
signatures: array(string()),
message: pick({
accountKeys: array(
pick({
pubkey: PublicKeyFromString,
signer: boolean(),
writable: boolean(),
source: optional(
union([literal('transaction'), literal('lookupTable')]),
),
}),
),
accountKeys: array(AnnotatedAccountKey),
instructions: array(ParsedOrRawInstruction),
recentBlockhash: string(),
addressTableLookups: optional(nullable(array(AddressTableLookupStruct))),
@ -2114,6 +2206,14 @@ const ParsedConfirmedTransactionMetaResult = pick({
const TransactionVersionStruct = union([literal(0), literal('legacy')]);
/** @internal */
const RewardsResult = pick({
pubkey: string(),
lamports: number(),
postBalance: nullable(number()),
rewardType: nullable(string()),
});
/**
* Expected JSON RPC response for the "getBlock" message
*/
@ -2130,16 +2230,46 @@ const GetBlockRpcResult = jsonRpcResult(
version: optional(TransactionVersionStruct),
}),
),
rewards: optional(
array(
pick({
pubkey: string(),
lamports: number(),
postBalance: nullable(number()),
rewardType: nullable(string()),
}),
),
rewards: optional(array(RewardsResult)),
blockTime: nullable(number()),
blockHeight: nullable(number()),
}),
),
);
/**
* Expected JSON RPC response for the "getBlock" message when `transactionDetails` is `none`
*/
const GetNoneModeBlockRpcResult = jsonRpcResult(
nullable(
pick({
blockhash: string(),
previousBlockhash: string(),
parentSlot: number(),
rewards: optional(array(RewardsResult)),
blockTime: nullable(number()),
blockHeight: nullable(number()),
}),
),
);
/**
* Expected JSON RPC response for the "getBlock" message when `transactionDetails` is `accounts`
*/
const GetAccountsModeBlockRpcResult = jsonRpcResult(
nullable(
pick({
blockhash: string(),
previousBlockhash: string(),
parentSlot: number(),
transactions: array(
pick({
transaction: ConfirmedTransactionAccountsModeResult,
meta: nullable(ConfirmedTransactionMetaResult),
version: optional(TransactionVersionStruct),
}),
),
rewards: optional(array(RewardsResult)),
blockTime: nullable(number()),
blockHeight: nullable(number()),
}),
@ -2162,16 +2292,46 @@ const GetParsedBlockRpcResult = jsonRpcResult(
version: optional(TransactionVersionStruct),
}),
),
rewards: optional(
array(
pick({
pubkey: string(),
lamports: number(),
postBalance: nullable(number()),
rewardType: nullable(string()),
}),
),
rewards: optional(array(RewardsResult)),
blockTime: nullable(number()),
blockHeight: nullable(number()),
}),
),
);
/**
* Expected parsed JSON RPC response for the "getBlock" message when `transactionDetails` is `accounts`
*/
const GetParsedAccountsModeBlockRpcResult = jsonRpcResult(
nullable(
pick({
blockhash: string(),
previousBlockhash: string(),
parentSlot: number(),
transactions: array(
pick({
transaction: ConfirmedTransactionAccountsModeResult,
meta: nullable(ParsedConfirmedTransactionMetaResult),
version: optional(TransactionVersionStruct),
}),
),
rewards: optional(array(RewardsResult)),
blockTime: nullable(number()),
blockHeight: nullable(number()),
}),
),
);
/**
* Expected parsed JSON RPC response for the "getBlock" message when `transactionDetails` is `none`
*/
const GetParsedNoneModeBlockRpcResult = jsonRpcResult(
nullable(
pick({
blockhash: string(),
previousBlockhash: string(),
parentSlot: number(),
rewards: optional(array(RewardsResult)),
blockTime: nullable(number()),
blockHeight: nullable(number()),
}),
@ -2195,16 +2355,7 @@ const GetConfirmedBlockRpcResult = jsonRpcResult(
meta: nullable(ConfirmedTransactionMetaResult),
}),
),
rewards: optional(
array(
pick({
pubkey: string(),
lamports: number(),
postBalance: nullable(number()),
rewardType: nullable(string()),
}),
),
),
rewards: optional(array(RewardsResult)),
blockTime: nullable(number()),
}),
),
@ -4261,6 +4412,26 @@ export class Connection {
rawConfig?: GetBlockConfig,
): Promise<BlockResponse | null>;
/**
* @deprecated Instead, call `getBlock` using a `GetVersionedBlockConfig` by
* setting the `maxSupportedTransactionVersion` property.
*/
// eslint-disable-next-line no-dupe-class-members
async getBlock(
slot: number,
rawConfig: GetBlockConfig & {transactionDetails: 'accounts'},
): Promise<AccountsModeBlockResponse | null>;
/**
* @deprecated Instead, call `getBlock` using a `GetVersionedBlockConfig` by
* setting the `maxSupportedTransactionVersion` property.
*/
// eslint-disable-next-line no-dupe-class-members
async getBlock(
slot: number,
rawConfig: GetBlockConfig & {transactionDetails: 'none'},
): Promise<NoneModeBlockResponse | null>;
/**
* Fetch a processed block from the cluster.
*/
@ -4270,6 +4441,18 @@ export class Connection {
rawConfig?: GetVersionedBlockConfig,
): Promise<VersionedBlockResponse | null>;
// eslint-disable-next-line no-dupe-class-members
async getBlock(
slot: number,
rawConfig: GetVersionedBlockConfig & {transactionDetails: 'accounts'},
): Promise<VersionedAccountsModeBlockResponse | null>;
// eslint-disable-next-line no-dupe-class-members
async getBlock(
slot: number,
rawConfig: GetVersionedBlockConfig & {transactionDetails: 'none'},
): Promise<VersionedNoneModeBlockResponse | null>;
/**
* Fetch a processed block from the cluster.
*/
@ -4277,7 +4460,12 @@ export class Connection {
async getBlock(
slot: number,
rawConfig?: GetVersionedBlockConfig,
): Promise<VersionedBlockResponse | null> {
): Promise<
| VersionedBlockResponse
| VersionedAccountsModeBlockResponse
| VersionedNoneModeBlockResponse
| null
> {
const {commitment, config} = extractCommitmentFromConfig(rawConfig);
const args = this._buildArgsAtLeastConfirmed(
[slot],
@ -4286,26 +4474,54 @@ export class Connection {
config,
);
const unsafeRes = await this._rpcRequest('getBlock', args);
const res = create(unsafeRes, GetBlockRpcResult);
if ('error' in res) {
throw new SolanaJSONRPCError(res.error, 'failed to get confirmed block');
try {
switch (config?.transactionDetails) {
case 'accounts': {
const res = create(unsafeRes, GetAccountsModeBlockRpcResult);
if ('error' in res) {
throw res.error;
}
return res.result;
}
case 'none': {
const res = create(unsafeRes, GetNoneModeBlockRpcResult);
if ('error' in res) {
throw res.error;
}
return res.result;
}
default: {
const res = create(unsafeRes, GetBlockRpcResult);
if ('error' in res) {
throw res.error;
}
const {result} = res;
return result
? {
...result,
transactions: result.transactions.map(
({transaction, meta, version}) => ({
meta,
transaction: {
...transaction,
message: versionedMessageFromResponse(
version,
transaction.message,
),
},
version,
}),
),
}
: null;
}
}
} catch (e) {
throw new SolanaJSONRPCError(
e as JSONRPCError,
'failed to get confirmed block',
);
}
const result = res.result;
if (!result) return result;
return {
...result,
transactions: result.transactions.map(({transaction, meta, version}) => ({
meta,
transaction: {
...transaction,
message: versionedMessageFromResponse(version, transaction.message),
},
version,
})),
};
}
/**
@ -4314,7 +4530,29 @@ export class Connection {
async getParsedBlock(
slot: number,
rawConfig?: GetVersionedBlockConfig,
): Promise<ParsedBlockResponse | null> {
): Promise<ParsedAccountsModeBlockResponse>;
// eslint-disable-next-line no-dupe-class-members
async getParsedBlock(
slot: number,
rawConfig: GetVersionedBlockConfig & {transactionDetails: 'accounts'},
): Promise<ParsedAccountsModeBlockResponse>;
// eslint-disable-next-line no-dupe-class-members
async getParsedBlock(
slot: number,
rawConfig: GetVersionedBlockConfig & {transactionDetails: 'none'},
): Promise<ParsedNoneModeBlockResponse>;
// eslint-disable-next-line no-dupe-class-members
async getParsedBlock(
slot: number,
rawConfig?: GetVersionedBlockConfig,
): Promise<
| ParsedBlockResponse
| ParsedAccountsModeBlockResponse
| ParsedNoneModeBlockResponse
| null
> {
const {commitment, config} = extractCommitmentFromConfig(rawConfig);
const args = this._buildArgsAtLeastConfirmed(
[slot],
@ -4323,11 +4561,33 @@ export class Connection {
config,
);
const unsafeRes = await this._rpcRequest('getBlock', args);
const res = create(unsafeRes, GetParsedBlockRpcResult);
if ('error' in res) {
throw new SolanaJSONRPCError(res.error, 'failed to get block');
try {
switch (config?.transactionDetails) {
case 'accounts': {
const res = create(unsafeRes, GetParsedAccountsModeBlockRpcResult);
if ('error' in res) {
throw res.error;
}
return res.result;
}
case 'none': {
const res = create(unsafeRes, GetParsedNoneModeBlockRpcResult);
if ('error' in res) {
throw res.error;
}
return res.result;
}
default: {
const res = create(unsafeRes, GetParsedBlockRpcResult);
if ('error' in res) {
throw res.error;
}
return res.result;
}
}
} catch (e) {
throw new SolanaJSONRPCError(e as JSONRPCError, 'failed to get block');
}
return res.result;
}
/*

View File

@ -3071,6 +3071,229 @@ describe('Connection', function () {
});
}
describe('get parsed block', function () {
it('can deserialize a response when `transactionDetails` is `full`', async () => {
// Mock block with transaction, fetched using `"transactionDetails": "full"`.
await mockRpcResponse({
method: 'getBlock',
params: [
1,
{
encoding: 'jsonParsed',
maxSupportedTransactionVersion: 0,
transactionDetails: 'full',
},
],
value: {
blockHeight: 0,
blockTime: 1614281964,
blockhash: '49d2UbduiZWjtR3Wvfv2t2QxmXvtZNWSPFRZxEDYAvQN',
parentSlot: 0,
previousBlockhash: 'mDd5yMLfuroS1JVZMHo2VZLTgKXXNBXrzPR5UkzFD4X',
transactions: [
{
meta: {
err: null,
fee: 5000,
innerInstructions: [],
logMessages: [
'Program Vote111111111111111111111111111111111111111 invoke [1]',
'Program Vote111111111111111111111111111111111111111 success',
],
postBalances: [3712706991, 5765419239, 1169280, 143487360, 1],
postTokenBalances: [],
preBalances: [3712711991, 5765419239, 1169280, 143487360, 1],
preTokenBalances: [],
rewards: null,
status: {Ok: null},
},
transaction: {
message: {
accountKeys: [
{
pubkey: '7v5fMKBqC9PuwjSdS9k9JU7efEXmq3bHTMF5fuSHnqrm',
signer: true,
source: 'transaction',
writable: true,
},
{
pubkey: 'AhcvnNdppGEcgdpK5gfcaZnAWz4ct8V4n7De5QiLiuzG',
signer: false,
source: 'transaction',
writable: true,
},
{
pubkey: 'SysvarC1ock11111111111111111111111111111111',
signer: false,
source: 'transaction',
writable: false,
},
{
pubkey: 'SysvarS1otHashes111111111111111111111111111',
signer: false,
source: 'transaction',
writable: false,
},
{
pubkey: 'Vote111111111111111111111111111111111111111',
signer: false,
source: 'transaction',
writable: false,
},
],
addressTableLookups: null,
instructions: [
{
parsed: {
info: {
clockSysvar:
'SysvarC1ock11111111111111111111111111111111',
slotHashesSysvar:
'SysvarS1otHashes111111111111111111111111111',
vote: {
hash: '2gmQ8xMjZaXn63kr8qzPAUjQAHi7xCDjSibPdJxhVYMm',
slots: [164153060, 164153061],
timestamp: 1669845645,
},
voteAccount:
'AhcvnNdppGEcgdpK5gfcaZnAWz4ct8V4n7De5QiLiuzG',
voteAuthority:
'7v5fMKBqC9PuwjSdS9k9JU7efEXmq3bHTMF5fuSHnqrm',
},
type: 'vote',
},
program: 'vote',
programId: 'Vote111111111111111111111111111111111111111',
},
],
recentBlockhash:
'GLqYrN6AQxCGtFTQywkPj2WN5tafC3KerBhW4QkmAyD4',
},
signatures: [
'5qDZ3nUUwp8VHFfAE5ydTQRULCoVLMGs16EprwdXsvyNCLe1NfckCkRE4BPi6wyEW9hXvG9iWU2prXfbM8SNPVEC',
],
},
version: 'legacy',
},
],
},
});
await expect(
connection.getParsedBlock(1, {
maxSupportedTransactionVersion: 0,
transactionDetails: 'full',
}),
).not.to.eventually.be.rejected;
});
it('can deserialize a response when `transactionDetails` is `none`', async () => {
// Mock block with transaction, fetched using `"transactionDetails": "none"`.
await mockRpcResponse({
method: 'getBlock',
params: [
1,
{
encoding: 'jsonParsed',
maxSupportedTransactionVersion: 0,
transactionDetails: 'none',
},
],
value: {
blockHeight: 0,
blockTime: 1614281964,
blockhash: '49d2UbduiZWjtR3Wvfv2t2QxmXvtZNWSPFRZxEDYAvQN',
parentSlot: 0,
previousBlockhash: 'mDd5yMLfuroS1JVZMHo2VZLTgKXXNBXrzPR5UkzFD4X',
},
});
await expect(
connection.getParsedBlock(1, {
maxSupportedTransactionVersion: 0,
transactionDetails: 'none',
}),
).not.to.eventually.be.rejected;
});
it('can deserialize a response when `transactionDetails` is `accounts`', async () => {
// Mock block with transaction, fetched using `"transactionDetails": "accounts"`.
await mockRpcResponse({
method: 'getBlock',
params: [
1,
{
encoding: 'jsonParsed',
maxSupportedTransactionVersion: 0,
transactionDetails: 'accounts',
},
],
value: {
blockHeight: 0,
blockTime: 1614281964,
blockhash: '49d2UbduiZWjtR3Wvfv2t2QxmXvtZNWSPFRZxEDYAvQN',
parentSlot: 0,
previousBlockhash: 'mDd5yMLfuroS1JVZMHo2VZLTgKXXNBXrzPR5UkzFD4X',
transactions: [
{
meta: {
err: null,
fee: 5000,
postBalances: [18237691394, 26858640, 1169280, 143487360, 1],
postTokenBalances: [],
preBalances: [18237696394, 26858640, 1169280, 143487360, 1],
preTokenBalances: [],
status: {Ok: null},
},
transaction: {
accountKeys: [
{
pubkey: '914RFshndUeZaNPjf8UWDCyo49ahQ1XQ2w9BnEMwpHKF',
signer: true,
source: 'transaction',
writable: true,
},
{
pubkey: '4cCd4SGrMswhqboYBJ5AcCVvCjh5NtaeZNwWFJzsnUWY',
signer: false,
source: 'transaction',
writable: true,
},
{
pubkey: 'SysvarC1ock11111111111111111111111111111111',
signer: false,
source: 'transaction',
writable: false,
},
{
pubkey: 'SysvarS1otHashes111111111111111111111111111',
signer: false,
source: 'transaction',
writable: false,
},
{
pubkey: 'Vote111111111111111111111111111111111111111',
signer: false,
source: 'transaction',
writable: false,
},
],
signatures: [
'5ZDp1HfNZhNRHc75ncsiZ4sCq1fGJHMGf9u36M3foD5PMH4Xu5S4X2x7aryn4JinUdG11oSYCk7zxbNmLJzzqUft',
],
},
version: 'legacy',
},
],
},
});
await expect(
connection.getParsedBlock(1, {
maxSupportedTransactionVersion: 0,
transactionDetails: 'accounts',
}),
).not.to.eventually.be.rejected;
});
});
describe('get block', function () {
beforeEach(async function () {
await mockRpcResponse({
@ -3236,6 +3459,187 @@ describe('Connection', function () {
`Block not available for slot ${Number.MAX_SAFE_INTEGER}`,
);
});
it('can deserialize a response when `transactionDetails` is `full`', async () => {
// Mock block with transaction, fetched using `"transactionDetails": "full"`.
await mockRpcResponse({
method: 'getBlock',
params: [
1,
{
maxSupportedTransactionVersion: 0,
transactionDetails: 'full',
},
],
value: {
blockHeight: 0,
blockTime: 1614281964,
blockhash: '49d2UbduiZWjtR3Wvfv2t2QxmXvtZNWSPFRZxEDYAvQN',
parentSlot: 0,
previousBlockhash: 'mDd5yMLfuroS1JVZMHo2VZLTgKXXNBXrzPR5UkzFD4X',
transactions: [
{
meta: {
err: null,
fee: 5000,
innerInstructions: [],
loadedAddresses: {readonly: [], writable: []},
logMessages: [
'Program Vote111111111111111111111111111111111111111 invoke [1]',
'Program Vote111111111111111111111111111111111111111 success',
],
postBalances: [12278161908, 39995373, 1169280, 143487360, 1],
postTokenBalances: [],
preBalances: [12278166908, 39995373, 1169280, 143487360, 1],
preTokenBalances: [],
rewards: null,
status: {Ok: null},
},
transaction: {
message: {
accountKeys: [
'FTWuJ2tqjecNizCSE66z4BD1tBHomG6DVffGUwRuWUkM',
'H2z3pBT62ByS4jpqsiEMtgN3NUFEuZHiTvoKCFjqCtD6',
'SysvarC1ock11111111111111111111111111111111',
'SysvarS1otHashes111111111111111111111111111',
'Vote111111111111111111111111111111111111111',
],
header: {
numReadonlySignedAccounts: 0,
numReadonlyUnsignedAccounts: 3,
numRequiredSignatures: 1,
},
instructions: [
{
accounts: [1, 3, 2, 0],
data: '29z5mr1JoRmJYQ6zG7p2F3mu68pWTNw9q49Tu7KrSEgoS6Jh1LMPGUK3HXs1N3Dody3icCcXxu6xPYoXLWnUTafEGm3knK',
programIdIndex: 4,
},
],
recentBlockhash:
'GLqYrN6AQxCGtFTQywkPj2WN5tafC3KerBhW4QkmAyD4',
},
signatures: [
'4SZofEnXEVzCYvzk16z6ScR6F3iNtZ3FsCC1PEWegpzvGwTJR6x9cDi8VHRmCFGC5XFs2yEFms3j36Mj7XVyHXbb',
],
},
version: 'legacy',
},
],
},
});
await expect(
connection.getBlock(1, {
maxSupportedTransactionVersion: 0,
transactionDetails: 'full',
}),
).not.to.eventually.be.rejected;
});
it('can deserialize a response when `transactionDetails` is `none`', async () => {
// Mock block with transaction, fetched using `"transactionDetails": "none"`.
await mockRpcResponse({
method: 'getBlock',
params: [
1,
{
maxSupportedTransactionVersion: 0,
transactionDetails: 'none',
},
],
value: {
blockHeight: 0,
blockTime: 1614281964,
blockhash: '49d2UbduiZWjtR3Wvfv2t2QxmXvtZNWSPFRZxEDYAvQN',
parentSlot: 0,
previousBlockhash: 'mDd5yMLfuroS1JVZMHo2VZLTgKXXNBXrzPR5UkzFD4X',
},
});
await expect(
connection.getBlock(1, {
maxSupportedTransactionVersion: 0,
transactionDetails: 'none',
}),
).not.to.eventually.be.rejected;
});
it('can deserialize a response when `transactionDetails` is `accounts`', async () => {
// Mock block with transaction, fetched using `"transactionDetails": "accounts"`.
await mockRpcResponse({
method: 'getBlock',
params: [
1,
{
maxSupportedTransactionVersion: 0,
transactionDetails: 'accounts',
},
],
value: {
blockHeight: 0,
blockTime: 1614281964,
blockhash: '49d2UbduiZWjtR3Wvfv2t2QxmXvtZNWSPFRZxEDYAvQN',
parentSlot: 0,
previousBlockhash: 'mDd5yMLfuroS1JVZMHo2VZLTgKXXNBXrzPR5UkzFD4X',
transactions: [
{
meta: {
err: null,
fee: 5000,
postBalances: [2751549948, 11751747405, 1169280, 143487360, 1],
postTokenBalances: [],
preBalances: [2751554948, 11751747405, 1169280, 143487360, 1],
preTokenBalances: [],
status: {Ok: null},
},
transaction: {
accountKeys: [
{
pubkey: 'D7hwgGRTr1vaCxzmfEKCaf56SPgBJmjHh6UXHG3p12bB',
signer: true,
source: 'transaction',
writable: true,
},
{
pubkey: '8iLE53Y9k4sccy4gxrT936BHbhYS6J13kQT5vRXhXFMX',
signer: false,
source: 'transaction',
writable: true,
},
{
pubkey: 'SysvarC1ock11111111111111111111111111111111',
signer: false,
source: 'transaction',
writable: false,
},
{
pubkey: 'SysvarS1otHashes111111111111111111111111111',
signer: false,
source: 'transaction',
writable: false,
},
{
pubkey: 'Vote111111111111111111111111111111111111111',
signer: false,
source: 'transaction',
writable: false,
},
],
signatures: [
'uNKj2ogn8ZRRjyVWXLC7sLRWpKQyMUomm66RXoDuWLXikPSJN8C7ZZK95j8S2bzcjwH6MvrXKSHtCWEURPpEXMB',
],
},
version: 'legacy',
},
],
},
});
await expect(
connection.getBlock(1, {
maxSupportedTransactionVersion: 0,
transactionDetails: 'accounts',
}),
).not.to.eventually.be.rejected;
});
});
describe('get confirmed block', function () {