diff --git a/web3.js/src/connection.ts b/web3.js/src/connection.ts index 2741dea65e0294..411a2c3ab28154 100644 --- a/web3.js/src/connection.ts +++ b/web3.js/src/connection.ts @@ -1143,6 +1143,42 @@ export type BlockResponse = { blockTime: number | null; }; +/** + * A block with parsed transactions + */ +export type ParsedBlockResponse = { + /** Blockhash of this block */ + blockhash: Blockhash; + /** Blockhash of this block's parent */ + previousBlockhash: Blockhash; + /** Slot index of this block's parent */ + parentSlot: number; + /** Vector of transactions with status meta and original message */ + transactions: Array<{ + /** The details of the transaction */ + transaction: ParsedTransaction; + /** Metadata produced from the transaction */ + meta: ParsedTransactionMeta | null; + /** The transaction version */ + version?: TransactionVersion; + }>; + /** Vector of block rewards */ + rewards?: Array<{ + /** Public key of reward recipient */ + pubkey: string; + /** Reward value in lamports */ + lamports: number; + /** Account balance after reward is applied */ + postBalance: number | null; + /** Type of reward received */ + rewardType: string | null; + }>; + /** The unix timestamp of when the block was processed */ + blockTime: number | null; + /** The number of blocks beneath this block */ + blockHeight: number | null; +}; + /** * A processed block fetched from the RPC API */ @@ -2081,6 +2117,38 @@ const GetBlockRpcResult = jsonRpcResult( ), ); +/** + * Expected parsed JSON RPC response for the "getBlock" message + */ +const GetParsedBlockRpcResult = jsonRpcResult( + nullable( + pick({ + blockhash: string(), + previousBlockhash: string(), + parentSlot: number(), + transactions: array( + pick({ + transaction: ParsedConfirmedTransactionResult, + meta: nullable(ParsedConfirmedTransactionMetaResult), + version: optional(TransactionVersionStruct), + }), + ), + rewards: optional( + array( + pick({ + pubkey: string(), + lamports: number(), + postBalance: nullable(number()), + rewardType: nullable(string()), + }), + ), + ), + blockTime: nullable(number()), + blockHeight: nullable(number()), + }), + ), +); + /** * Expected JSON RPC response for the "getConfirmedBlock" message * @@ -3878,6 +3946,28 @@ export class Connection { }; } + /** + * Fetch parsed transaction details for a confirmed or finalized block + */ + async getParsedBlock( + slot: number, + rawConfig?: GetVersionedBlockConfig, + ): Promise { + const {commitment, config} = extractCommitmentFromConfig(rawConfig); + const args = this._buildArgsAtLeastConfirmed( + [slot], + commitment as Finality, + 'jsonParsed', + 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'); + } + return res.result; + } + /* * Returns the current block height of the node */ diff --git a/web3.js/test/connection.test.ts b/web3.js/test/connection.test.ts index a5f8724a2cd44b..fc5599dea8374d 100644 --- a/web3.js/test/connection.test.ts +++ b/web3.js/test/connection.test.ts @@ -4621,6 +4621,24 @@ describe('Connection', function () { } expect(foundTx).to.be.true; }); + + it('getParsedBlock', async () => { + const block = await connection.getParsedBlock(transactionSlot, { + maxSupportedTransactionVersion: 0, + commitment: 'confirmed', + }); + expect(block).to.not.be.null; + if (block === null) throw new Error(); // unreachable + + let foundTx = false; + for (const tx of block.transactions) { + if (tx.transaction.signatures[0] === signature) { + foundTx = true; + expect(tx.version).to.eq(0); + } + } + expect(foundTx).to.be.true; + }); }).timeout(5 * 1000); } });