diff --git a/web3.js/module.d.ts b/web3.js/module.d.ts index 338a34d596..972b79749a 100644 --- a/web3.js/module.d.ts +++ b/web3.js/module.d.ts @@ -155,6 +155,13 @@ declare module '@solana/web3.js' { }>; }; + export type PerfSample = { + slot: number; + numTransactions: number; + numSlots: number; + samplePeriodSecs: number; + }; + export type ConfirmedTransaction = { slot: number; transaction: Transaction; @@ -421,6 +428,7 @@ declare module '@solana/web3.js' { getRecentBlockhashAndContext( commitment?: Commitment, ): Promise>; + getRecentPerformanceSamples(limit?: number): Promise>; getFeeCalculatorForBlockhash( blockhash: Blockhash, commitment?: Commitment, diff --git a/web3.js/module.flow.js b/web3.js/module.flow.js index d8a8fc0f5f..5ae303db6c 100644 --- a/web3.js/module.flow.js +++ b/web3.js/module.flow.js @@ -169,6 +169,13 @@ declare module '@solana/web3.js' { }>, }; + declare export type PerfSample = { + slot: number, + numTransactions: number, + numSlots: number, + samplePeriodSecs: number, + }; + declare export type ConfirmedTransaction = { slot: number, transaction: Transaction, @@ -426,6 +433,7 @@ declare module '@solana/web3.js' { getRecentBlockhashAndContext( commitment: ?Commitment, ): Promise>; + getRecentPerformanceSamples(limit: ?number): Promise>; getFeeCalculatorForBlockhash( blockhash: Blockhash, commitment: ?Commitment, diff --git a/web3.js/src/connection.js b/web3.js/src/connection.js index 4412333329..4e7185d867 100644 --- a/web3.js/src/connection.js +++ b/web3.js/src/connection.js @@ -505,8 +505,25 @@ type ConfirmedBlock = { }>, }; +/** + * A performance sample + * + * @typedef {Object} PerfSample + * @property {number} slot Slot number of sample + * @property {number} numTransactions Number of transactions in a sample window + * @property {number} numSlots Number of slots in a sample window + * @property {number} samplePeriodSecs Sample window in seconds + */ +type PerfSample = { + slot: number, + numTransactions: number, + numSlots: number, + samplePeriodSecs: number, +}; + function createRpcRequest(url: string, useHttps: boolean): RpcRequest { const agentManager = new AgentManager(useHttps); + const server = jayson(async (request, callback) => { const agent = agentManager.requestStart(); const options = { @@ -1182,6 +1199,20 @@ const GetRecentBlockhashAndContextRpcResult = jsonRpcResultAndContext( }), ); +/* + * Expected JSON RPC response for "getRecentPerformanceSamples" message + */ +const GetRecentPerformanceSamplesRpcResult = jsonRpcResult( + struct.array([ + struct.pick({ + slot: 'number', + numTransactions: 'number', + numSlots: 'number', + samplePeriodSecs: 'number', + }), + ]), +); + /** * Expected JSON RPC response for the "getFeeCalculatorForBlockhash" message */ @@ -2325,6 +2356,30 @@ export class Connection { return res.result; } + /** + * Fetch recent performance samples + * @return {Promise>} + */ + async getRecentPerformanceSamples( + limit: ?number, + ): Promise> { + const args = this._buildArgs(limit ? [limit] : []); + const unsafeRes = await this._rpcRequest( + 'getRecentPerformanceSamples', + args, + ); + + const res = GetRecentPerformanceSamplesRpcResult(unsafeRes); + if (res.error) { + throw new Error( + 'failed to get recent performance samples: ' + res.error.message, + ); + } + + assert(typeof res.result !== 'undefined'); + return res.result; + } + /** * Fetch the fee calculator for a recent blockhash from the cluster, return with context */ diff --git a/web3.js/test/connection.test.js b/web3.js/test/connection.test.js index 62bc6bad0d..0ac6a9f4f0 100644 --- a/web3.js/test/connection.test.js +++ b/web3.js/test/connection.test.js @@ -1279,6 +1279,66 @@ test('get supply', async () => { expect(supply.nonCirculatingAccounts.length).toBeGreaterThan(0); }); +test('get performance samples', async () => { + const connection = new Connection(url); + + if (mockRpcEnabled) { + mockRpc.push([ + url, + { + method: 'getRecentPerformanceSamples', + params: [], + }, + { + error: null, + result: [ + { + slot: 1234, + numTransactions: 1000, + numSlots: 60, + samplePeriodSecs: 60, + }, + ], + }, + ]); + } + + const perfSamples = await connection.getRecentPerformanceSamples(); + expect(Array.isArray(perfSamples)).toBe(true); + + if (perfSamples.length > 0) { + expect(perfSamples[0].slot).toBeGreaterThan(0); + expect(perfSamples[0].numTransactions).toBeGreaterThan(0); + expect(perfSamples[0].numSlots).toBeGreaterThan(0); + expect(perfSamples[0].samplePeriodSecs).toBeGreaterThan(0); + } +}); + +test('get performance samples limit too high', async () => { + const connection = new Connection(url); + + if (mockRpcEnabled) { + mockRpc.push([ + url, + { + method: 'getRecentPerformanceSamples', + params: [100000], + }, + { + error: { + code: -32602, + message: 'Invalid limit; max 720', + }, + result: null, + }, + ]); + } + + await expect( + connection.getRecentPerformanceSamples(100000), + ).rejects.toThrow(); +}); + const TOKEN_PROGRAM_ID = new PublicKey( 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', );