From 9fa32c92bc2f74d50e663fd9a42c2ea45e1a8ea0 Mon Sep 17 00:00:00 2001 From: Junaid <86780488+jdevcs@users.noreply.github.com> Date: Tue, 5 Nov 2024 17:36:47 +0100 Subject: [PATCH] newPendingTransactionFilter (#7353) * rpc method * createNewPendingTransactionFilter in eth class * unit test * integration test * rpc method * web3Eth class updates * test update * type FilterParams * createNewBlockFilter test * createNewFilter test * getFilterChanges test * getFilterLogs test * uninstallFilter test * fixtures * changelog update * updated changelog * tests for coverage --- CHANGELOG.md | 2 +- packages/web3-eth/CHANGELOG.md | 4 + .../src/filtering_rpc_method_wrappers.ts | 171 +++++++++++++ packages/web3-eth/src/web3_eth.ts | 237 ++++++++++++++---- .../web3_eth_methods_with_parameters.ts | 48 ++++ .../web3-eth/test/integration/format.test.ts | 7 + .../create_new_block_filter.test.ts | 35 +++ .../create_new_filter.test.ts | 51 ++++ .../fixtures/new_filter_block_params.ts | 59 +++++ .../get_filter_changes.test.ts | 43 ++++ .../get_filter_logs.test.ts | 44 ++++ .../new_pending_transaction_filter.test.ts | 37 +++ .../uninstall_filter.test.ts | 38 +++ .../web3_eth_methods_no_parameters.test.ts | 12 + .../web3_eth_methods_with_parameters.test.ts | 157 +++++++++++- packages/web3-types/CHANGELOG.md | 4 + packages/web3-types/src/eth_types.ts | 2 + 17 files changed, 900 insertions(+), 51 deletions(-) create mode 100644 packages/web3-eth/src/filtering_rpc_method_wrappers.ts create mode 100644 packages/web3-eth/test/unit/rpc_method_wrappers/create_new_block_filter.test.ts create mode 100644 packages/web3-eth/test/unit/rpc_method_wrappers/create_new_filter.test.ts create mode 100644 packages/web3-eth/test/unit/rpc_method_wrappers/fixtures/new_filter_block_params.ts create mode 100644 packages/web3-eth/test/unit/rpc_method_wrappers/get_filter_changes.test.ts create mode 100644 packages/web3-eth/test/unit/rpc_method_wrappers/get_filter_logs.test.ts create mode 100644 packages/web3-eth/test/unit/rpc_method_wrappers/new_pending_transaction_filter.test.ts create mode 100644 packages/web3-eth/test/unit/rpc_method_wrappers/uninstall_filter.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index d96a9e23283..08102f589e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2756,7 +2756,7 @@ If there are any bugs, improvements, optimizations or any new feature proposal f #### web3-eth -- Allow specifying percentage based factor in Web3Eth.calculateFeeData Param baseFeePerGasFactor #7332 +- Allow specifying percentage based factor in Web3Eth.calculateFeeData Param baseFeePerGasFactor #7332 ### Fixed diff --git a/packages/web3-eth/CHANGELOG.md b/packages/web3-eth/CHANGELOG.md index 946429b8c7b..5f1aab23bde 100644 --- a/packages/web3-eth/CHANGELOG.md +++ b/packages/web3-eth/CHANGELOG.md @@ -290,3 +290,7 @@ Documentation: - `populateGasPrice` function now checks `Web3Context.config.ignoreGasPricing`. If `ignoreGasPricing` is true, gasPrice will not be estimated (#7320) ## [Unreleased] + +### Added + +- `createNewPendingTransactionFilter` , `createNewFilter` , `createNewBlockFilter` , `uninstallFilter` , `getFilterChanges` and `getFilterLogs` are exported from `Web3Eth` and `filtering_rpc_method_wrappers` (#7353) diff --git a/packages/web3-eth/src/filtering_rpc_method_wrappers.ts b/packages/web3-eth/src/filtering_rpc_method_wrappers.ts new file mode 100644 index 00000000000..5b13fcc54b1 --- /dev/null +++ b/packages/web3-eth/src/filtering_rpc_method_wrappers.ts @@ -0,0 +1,171 @@ +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ + +import { Web3Context } from 'web3-core'; +import { ethRpcMethods } from 'web3-rpc-methods'; +import { DataFormat, EthExecutionAPI, Numbers, Log, FilterParams } from 'web3-types'; +import { format, numberToHex } from 'web3-utils'; +import { isNullish } from 'web3-validator'; +import { logSchema } from './schemas.js'; + +/** + * View additional documentations here: {@link Web3Eth.createNewPendingTransactionFilter} + * @param web3Context ({@link Web3Context}) Web3 configuration object that contains things such as the provider, request manager, wallet, etc. + * @param returnFormat ({@link DataFormat}) Return format + */ +export async function createNewPendingTransactionFilter( + web3Context: Web3Context, + returnFormat: ReturnFormat, +) { + const response = await ethRpcMethods.newPendingTransactionFilter(web3Context.requestManager); + + return format( + { format: 'uint' }, + response as Numbers, + returnFormat ?? web3Context.defaultReturnFormat, + ); +} + +/** + * View additional documentations here: {@link Web3Eth.createNewFilter} + * @param web3Context ({@link Web3Context}) Web3 configuration object that contains things such as the provider, request manager, wallet, etc. + * @param filter ({@link FilterParam}) Filter param optional having from-block to-block address or params + * @param returnFormat ({@link DataFormat}) Return format + */ +export async function createNewFilter( + web3Context: Web3Context, + filter: FilterParams, + returnFormat: ReturnFormat, +) { + // format type bigint or number toBlock and fromBlock to hexstring. + let { toBlock, fromBlock } = filter; + if (!isNullish(toBlock)) { + if (typeof toBlock === 'number' || typeof toBlock === 'bigint') { + toBlock = numberToHex(toBlock); + } + } + if (!isNullish(fromBlock)) { + if (typeof fromBlock === 'number' || typeof fromBlock === 'bigint') { + fromBlock = numberToHex(fromBlock); + } + } + + const formattedFilter = { ...filter, fromBlock, toBlock }; + + const response = await ethRpcMethods.newFilter(web3Context.requestManager, formattedFilter); + + return format( + { format: 'uint' }, + response as Numbers, + returnFormat ?? web3Context.defaultReturnFormat, + ); +} + +/** + * View additional documentations here: {@link Web3Eth.createNewBlockFilter} + * @param web3Context ({@link Web3Context}) Web3 configuration object that contains things such as the provider, request manager, wallet, etc. + * @param returnFormat ({@link DataFormat}) Return format + */ +export async function createNewBlockFilter( + web3Context: Web3Context, + returnFormat: ReturnFormat, +) { + const response = await ethRpcMethods.newBlockFilter(web3Context.requestManager); + + return format( + { format: 'uint' }, + response as Numbers, + returnFormat ?? web3Context.defaultReturnFormat, + ); +} + +/** + * View additional documentations here: {@link Web3Eth.uninstallFilter} + * @param web3Context ({@link Web3Context}) Web3 configuration object that contains things such as the provider, request manager, wallet, etc. + * @param filterIdentifier ({@link Numbers}) filter id + */ +export async function uninstallFilter( + web3Context: Web3Context, + filterIdentifier: Numbers, +) { + const response = await ethRpcMethods.uninstallFilter( + web3Context.requestManager, + numberToHex(filterIdentifier), + ); + + return response; +} + +/** + * View additional documentations here: {@link Web3Eth.getFilterChanges} + * @param web3Context ({@link Web3Context}) Web3 configuration object that contains things such as the provider, request manager, wallet, etc. + * @param filterIdentifier ({@link Numbers}) filter id + */ +export async function getFilterChanges( + web3Context: Web3Context, + filterIdentifier: Numbers, + returnFormat: ReturnFormat, +) { + const response = await ethRpcMethods.getFilterChanges( + web3Context.requestManager, + numberToHex(filterIdentifier), + ); + + const result = response.map(res => { + if (typeof res === 'string') { + return res; + } + + return format( + logSchema, + res as unknown as Log, + returnFormat ?? web3Context.defaultReturnFormat, + ); + }); + + return result; +} + +/** + * View additional documentations here: {@link Web3Eth.getFilterLogs} + * @param web3Context ({@link Web3Context}) Web3 configuration object that contains things such as the provider, request manager, wallet, etc. + * @param filterIdentifier ({@link Numbers}) filter id + */ +export async function getFilterLogs( + web3Context: Web3Context, + filterIdentifier: Numbers, + returnFormat: ReturnFormat, +) { + const response = await ethRpcMethods.getFilterLogs( + web3Context.requestManager, + numberToHex(filterIdentifier), + ); + + const result = response.map(res => { + if (typeof res === 'string') { + return res; + } + + return format( + logSchema, + res as unknown as Log, + returnFormat ?? web3Context.defaultReturnFormat, + ); + }); + + return result; +} diff --git a/packages/web3-eth/src/web3_eth.ts b/packages/web3-eth/src/web3_eth.ts index 4f0a191d9f8..ba951914e91 100644 --- a/packages/web3-eth/src/web3_eth.ts +++ b/packages/web3-eth/src/web3_eth.ts @@ -41,6 +41,7 @@ import { Eip712TypedData, FMT_BYTES, FMT_NUMBER, + FilterParams, } from 'web3-types'; import { isSupportedProvider, Web3Context, Web3ContextInitOptions } from 'web3-core'; import { TransactionNotFound } from 'web3-errors'; @@ -48,6 +49,7 @@ import { toChecksumAddress, isNullish, ethUnitMap } from 'web3-utils'; import { ethRpcMethods } from 'web3-rpc-methods'; import * as rpcMethodsWrappers from './rpc_method_wrappers.js'; +import * as filteringRpcMethodsWrappers from './filtering_rpc_method_wrappers.js'; import { SendTransactionOptions, TransactionMiddleware } from './types.js'; import { LogsSubscription, @@ -272,54 +274,54 @@ export class Web3Eth extends Web3Context { - * gasPrice: 20000000000n, - * maxFeePerGas: 60000000000n, - * maxPriorityFeePerGas: 20000000000n, - * baseFeePerGas: 20000000000n - * } - * - * @example - * // Using a `bigint` for baseFeePerGasFactor - * web3.eth.calculateFeeData(1n).then(console.log); - * > { - * gasPrice: 20000000000n, - * maxFeePerGas: 40000000000n, - * maxPriorityFeePerGas: 20000000000n, - * baseFeePerGas: 20000000000n - * } - * - * @example - * // Using a `number` for baseFeePerGasFactor (with decimals) - * web3.eth.calculateFeeData(1.5).then(console.log); - * > { - * gasPrice: 20000000000n, - * maxFeePerGas: 50000000000n, // baseFeePerGasFactor is converted to BigInt(1.500) - * maxPriorityFeePerGas: 20000000000n, - * baseFeePerGas: 20000000000n - * } - * - * @example - * web3.eth.calculateFeeData(3n).then(console.log); - * > { - * gasPrice: 20000000000n, - * maxFeePerGas: 80000000000n, - * maxPriorityFeePerGas: 20000000000n, - * baseFeePerGas: 20000000000n - * } - */ + * Calculates the current Fee Data. + * If the node supports EIP-1559, then `baseFeePerGas` and `maxPriorityFeePerGas` will be returned along with the calculated `maxFeePerGas` value. + * `maxFeePerGas` is calculated as `baseFeePerGas` * `baseFeePerGasFactor` + `maxPriorityFeePerGas`. + * If the node does not support EIP-1559, then the `gasPrice` will be returned and the other values will be undefined. + * + * @param baseFeePerGasFactor (optional) The factor to multiply the `baseFeePerGas` with when calculating `maxFeePerGas`, if the node supports EIP-1559. This can be a `bigint` for precise calculation or a `number` to support decimals. The default value is 2 (BigInt). + * If a `number` is provided, it will be converted to `bigint` with three decimal precision. + * @param alternativeMaxPriorityFeePerGas (optional) The alternative `maxPriorityFeePerGas` to use when calculating `maxFeePerGas`, if the node supports EIP-1559 but does not support the method `eth_maxPriorityFeePerGas`. The default value is 1 gwei. + * @returns The current fee data. + * + * @example + * web3.eth.calculateFeeData().then(console.log); + * > { + * gasPrice: 20000000000n, + * maxFeePerGas: 60000000000n, + * maxPriorityFeePerGas: 20000000000n, + * baseFeePerGas: 20000000000n + * } + * + * @example + * // Using a `bigint` for baseFeePerGasFactor + * web3.eth.calculateFeeData(1n).then(console.log); + * > { + * gasPrice: 20000000000n, + * maxFeePerGas: 40000000000n, + * maxPriorityFeePerGas: 20000000000n, + * baseFeePerGas: 20000000000n + * } + * + * @example + * // Using a `number` for baseFeePerGasFactor (with decimals) + * web3.eth.calculateFeeData(1.5).then(console.log); + * > { + * gasPrice: 20000000000n, + * maxFeePerGas: 50000000000n, // baseFeePerGasFactor is converted to BigInt(1.500) + * maxPriorityFeePerGas: 20000000000n, + * baseFeePerGas: 20000000000n + * } + * + * @example + * web3.eth.calculateFeeData(3n).then(console.log); + * > { + * gasPrice: 20000000000n, + * maxFeePerGas: 80000000000n, + * maxPriorityFeePerGas: 20000000000n, + * baseFeePerGas: 20000000000n + * } + */ public async calculateFeeData( baseFeePerGasFactor: bigint | number = BigInt(2), @@ -1916,4 +1918,143 @@ export class Web3Eth extends Web3Context 1n + * + * web3.eth.createNewPendingTransactionFilter({ number: FMT_NUMBER.HEX , bytes: FMT_BYTES.HEX }).then(console.log); + * > "0x1" + * ``` + */ + public async createNewPendingTransactionFilter< + ReturnFormat extends DataFormat = typeof DEFAULT_RETURN_FORMAT, + >(returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat) { + return filteringRpcMethodsWrappers.createNewPendingTransactionFilter(this, returnFormat); + } + + /** + * Creates a filter object, based on filter options, to notify when the state changes (logs) + * + * @param filter A {@link FilterParams} object containing the filter properties. + * @param returnFormat ({@link DataFormat} defaults to {@link DEFAULT_RETURN_FORMAT}) Specifies how the return data should be formatted. + * @returns A filter id. + * + * ```ts + * web3.eth.createNewFilter(filterParams).then(console.log); + * > 1n + * + * web3.eth.createNewFilter(filterParams, { number: FMT_NUMBER.HEX , bytes: FMT_BYTES.HEX }).then(console.log); + * > "0x1" + * ``` + */ + public async createNewFilter( + filter: FilterParams, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, + ) { + return filteringRpcMethodsWrappers.createNewFilter(this, filter, returnFormat); + } + + /** + * Creates a filter in the node, to notify when a new block arrives. + * + * @param returnFormat ({@link DataFormat} defaults to {@link DEFAULT_RETURN_FORMAT}) Specifies how the return data should be formatted. + * @returns A filter id. + * + * ```ts + * web3.eth.createNewBlockFilter().then(console.log); + * > 1n + * + * web3.eth.createNewBlockFilter({ number: FMT_NUMBER.HEX , bytes: FMT_BYTES.HEX }).then(console.log); + * > "0x1" + * ``` + */ + public async createNewBlockFilter< + ReturnFormat extends DataFormat = typeof DEFAULT_RETURN_FORMAT, + >(returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat) { + return filteringRpcMethodsWrappers.createNewBlockFilter(this, returnFormat); + } + + /** + * Uninstalls a filter with given id. Should always be called when watch is no longer needed. + * + * @param filterIdentifier ({@link Numbers} filter id + * @returns true if the filter was successfully uninstalled, otherwise false. + * + * ```ts + * web3.eth.uninstallFilter(123).then(console.log); + * > true + * + * web3.eth.uninstallFilter('0x123').then(console.log); + * > true + * + * web3.eth.uninstallFilter(12n).then(console.log); + * > true + * ``` + */ + public async uninstallFilter(filterIdentifier: Numbers) { + return filteringRpcMethodsWrappers.uninstallFilter(this, filterIdentifier); + } + + /** + * Polling method for a filter, which returns an array of logs which occurred since last poll. + * + * @param filterIdentifier ({@link Numbers} filter id + * @param returnFormat ({@link DataFormat} defaults to {@link DEFAULT_RETURN_FORMAT}) - Specifies how the return data from the call should be formatted. + * @returns {@link FilterResultsAPI}, an array of {@link Log} objects. + * + * ```ts + * web3.eth.getFilterChanges(123).then(console.log); + * > [{ + * data: '0x7f9fade1c0d57a7af66ab4ead79fade1c0d57a7af66ab4ead7c2c2eb7b11a91385', + * topics: ['0xfd43ade1c09fade1c0d57a7af66ab4ead7c2c2eb7b11a91ffdd57a7af66ab4ead7', '0x7f9fade1c0d57a7af66ab4ead79fade1c0d57a7af66ab4ead7c2c2eb7b11a91385'] + * logIndex: 0n, + * transactionIndex: 0n, + * transactionHash: '0x7f9fade1c0d57a7af66ab4ead79fade1c0d57a7af66ab4ead7c2c2eb7b11a91385', + * blockHash: '0xfd43ade1c09fade1c0d57a7af66ab4ead7c2c2eb7b11a91ffdd57a7af66ab4ead7', + * blockNumber: 1234n, + * address: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe' + * }, + * {...}] + */ + public async getFilterChanges( + filterIdentifier: Numbers, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, + ) { + return filteringRpcMethodsWrappers.getFilterChanges(this, filterIdentifier, returnFormat); + } + + /** + * Returns an array of all logs matching filter with given id. + * + * @param filterIdentifier ({@link Numbers} filter id + * @param returnFormat ({@link DataFormat} defaults to {@link DEFAULT_RETURN_FORMAT}) - Specifies how the return data from the call should be formatted. + * @returns {@link FilterResultsAPI}, an array of {@link Log} objects. + * + * ```ts + * web3.eth.getFilterLogs(123).then(console.log); + * > [{ + * data: '0x7f9fade1c0d57a7af66ab4ead79fade1c0d57a7af66ab4ead7c2c2eb7b11a91385', + * topics: ['0xfd43ade1c09fade1c0d57a7af66ab4ead7c2c2eb7b11a91ffdd57a7af66ab4ead7', '0x7f9fade1c0d57a7af66ab4ead79fade1c0d57a7af66ab4ead7c2c2eb7b11a91385'] + * logIndex: 0n, + * transactionIndex: 0n, + * transactionHash: '0x7f9fade1c0d57a7af66ab4ead79fade1c0d57a7af66ab4ead7c2c2eb7b11a91385', + * blockHash: '0xfd43ade1c09fade1c0d57a7af66ab4ead7c2c2eb7b11a91ffdd57a7af66ab4ead7', + * blockNumber: 1234n, + * address: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe' + * }, + * {...}] + */ + public async getFilterLogs( + filterIdentifier: Numbers, + returnFormat: ReturnFormat = this.defaultReturnFormat as ReturnFormat, + ) { + return filteringRpcMethodsWrappers.getFilterLogs(this, filterIdentifier, returnFormat); + } } diff --git a/packages/web3-eth/test/fixtures/web3_eth_methods_with_parameters.ts b/packages/web3-eth/test/fixtures/web3_eth_methods_with_parameters.ts index 17d9c204f21..dd2c7907ab9 100644 --- a/packages/web3-eth/test/fixtures/web3_eth_methods_with_parameters.ts +++ b/packages/web3-eth/test/fixtures/web3_eth_methods_with_parameters.ts @@ -30,6 +30,8 @@ import { Uint256, TransactionWithSenderAPI, TransactionReceipt, + Numbers, + FilterParams, } from 'web3-types'; import { transactionWithSender } from './rpc_methods_wrappers'; @@ -2302,3 +2304,49 @@ export const txReceipt: TransactionReceipt = { type: BigInt(0), root: '', }; + +export const getFilterLogsDataWithformater: [Numbers, DataFormat][] = [ + [123, { ...DEFAULT_RETURN_FORMAT, number: FMT_NUMBER.BIGINT }], // Number + ['0x7b', { ...DEFAULT_RETURN_FORMAT, number: FMT_NUMBER.BIGINT }], // Hex string + [BigInt(123), { ...DEFAULT_RETURN_FORMAT, number: FMT_NUMBER.BIGINT }], // BigInt +]; + +export const getFilterLogsData: [Numbers][] = [ + [456], // Number without return format + ['0x1c8'], // Hex string without return format + [BigInt(456)], // BigInt without return format +]; + +// Export the test data +export const uninstallFilterData: [Numbers][] = [ + [123], // Number + ['0x7b'], // Hex string equivalent of 123 + [BigInt(123)], // BigInt + [456], // Another number + ['0x1c8'], // Another hex string equivalent of 456 + [BigInt(456)], // Another BigInt +]; + +export type CreateNewFilterTestData = [FilterParams, DataFormat]; + +// Export the test data +export const createNewFilterData: CreateNewFilterTestData[] = [ + [ + { fromBlock: 123, toBlock: 456 }, + { ...DEFAULT_RETURN_FORMAT, number: FMT_NUMBER.BIGINT }, + ], // Number to hex conversion + [ + { fromBlock: BigInt(123), toBlock: BigInt(456) }, + { ...DEFAULT_RETURN_FORMAT, number: FMT_NUMBER.BIGINT }, + ], // BigInt to hex conversion + [ + { address: '0x1234567890abcdef1234567890abcdef12345678' }, + { ...DEFAULT_RETURN_FORMAT, number: FMT_NUMBER.BIGINT }, + ], // Address only + // eslint-disable-next-line no-null/no-null + [{ topics: [null, '0xabcdef'] }, { ...DEFAULT_RETURN_FORMAT, number: FMT_NUMBER.BIGINT }], // Topics with null + [ + { fromBlock: BlockTags.LATEST, toBlock: BlockTags.PENDING }, + { ...DEFAULT_RETURN_FORMAT, number: FMT_NUMBER.BIGINT }, + ], // Non-numeric blocks +]; diff --git a/packages/web3-eth/test/integration/format.test.ts b/packages/web3-eth/test/integration/format.test.ts index b9962c065ff..09d3abdaa1a 100644 --- a/packages/web3-eth/test/integration/format.test.ts +++ b/packages/web3-eth/test/integration/format.test.ts @@ -108,5 +108,12 @@ describe('format', () => { expect(typeof res).toBe(mapFormatToType[format as string]); expect(Number(res)).toBeGreaterThan(0); }); + + it.each(Object.values(FMT_NUMBER))('createNewPendingTransactionFilter', async format => { + web3Eth.defaultReturnFormat = { number: format as FMT_NUMBER, bytes: FMT_BYTES.HEX }; + const res = await web3Eth.createNewPendingTransactionFilter(); + expect(typeof res).toBe(mapFormatToType[format as string]); + expect(parseInt(String(res), 16)).toBeGreaterThan(0); + }); }); }); diff --git a/packages/web3-eth/test/unit/rpc_method_wrappers/create_new_block_filter.test.ts b/packages/web3-eth/test/unit/rpc_method_wrappers/create_new_block_filter.test.ts new file mode 100644 index 00000000000..aafa6e8f0d1 --- /dev/null +++ b/packages/web3-eth/test/unit/rpc_method_wrappers/create_new_block_filter.test.ts @@ -0,0 +1,35 @@ +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ +import { Web3Context } from 'web3-core'; +import { DEFAULT_RETURN_FORMAT, Web3EthExecutionAPI } from 'web3-types'; +import { ethRpcMethods } from 'web3-rpc-methods'; +import { createNewBlockFilter } from '../../../src/filtering_rpc_method_wrappers'; + +jest.mock('web3-rpc-methods'); + +describe('createNewBlockFilter', () => { + let web3Context: Web3Context; + + beforeAll(() => { + web3Context = new Web3Context('http://127.0.0.1:8545'); + }); + + it('should call rpcMethods.newBlockFilter with expected parameters', async () => { + await createNewBlockFilter(web3Context, DEFAULT_RETURN_FORMAT); + expect(ethRpcMethods.newBlockFilter).toHaveBeenCalledWith(web3Context.requestManager); + }); +}); diff --git a/packages/web3-eth/test/unit/rpc_method_wrappers/create_new_filter.test.ts b/packages/web3-eth/test/unit/rpc_method_wrappers/create_new_filter.test.ts new file mode 100644 index 00000000000..0f44c3cca6f --- /dev/null +++ b/packages/web3-eth/test/unit/rpc_method_wrappers/create_new_filter.test.ts @@ -0,0 +1,51 @@ +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ +import { Web3Context } from 'web3-core'; +import { DEFAULT_RETURN_FORMAT, FilterParams, Web3EthExecutionAPI } from 'web3-types'; +import { ethRpcMethods } from 'web3-rpc-methods'; +import { createNewFilter } from '../../../src/filtering_rpc_method_wrappers'; +import { blockParams } from './fixtures/new_filter_block_params'; + +jest.mock('web3-rpc-methods'); + +describe('createNewFilter', () => { + let web3Context: Web3Context; + + beforeAll(() => { + web3Context = new Web3Context(); + }); + + it.each(blockParams)( + 'should call rpcMethods.newFilter with fromBlock %p and toBlock %p', + async (actualParams, expectedParams) => { + const [fromBlockParam, toBlockParam] = actualParams; + const [expectedFromBlock, expectedToBlock] = expectedParams; + + const sampleFilterParams: FilterParams = { + fromBlock: fromBlockParam, + toBlock: toBlockParam, + address: ['0x1234567890abcdef1234567890abcdef12345678'], + }; + await createNewFilter(web3Context, sampleFilterParams, DEFAULT_RETURN_FORMAT); + expect(ethRpcMethods.newFilter).toHaveBeenCalledWith(web3Context.requestManager, { + ...sampleFilterParams, + fromBlock: expectedFromBlock, + toBlock: expectedToBlock, + }); + }, + ); +}); diff --git a/packages/web3-eth/test/unit/rpc_method_wrappers/fixtures/new_filter_block_params.ts b/packages/web3-eth/test/unit/rpc_method_wrappers/fixtures/new_filter_block_params.ts new file mode 100644 index 00000000000..9af69751246 --- /dev/null +++ b/packages/web3-eth/test/unit/rpc_method_wrappers/fixtures/new_filter_block_params.ts @@ -0,0 +1,59 @@ +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ + +// Modified blockParams to include sub-arrays for actual and expected parameters +export const blockParams: [ + [string | number | bigint, string | number | bigint], + [string, string], +][] = [ + [ + [123, 'latest'], + ['0x7b', 'latest'], + ], // Number fromBlock + [ + [BigInt(123), 'latest'], + ['0x7b', 'latest'], + ], // BigInt fromBlock + [ + ['0x7b', 'latest'], + ['0x7b', 'latest'], + ], // Hexadecimal fromBlock + [ + [123, 456], + ['0x7b', '0x1c8'], + ], // Number fromBlock and toBlock + [ + [BigInt(123), BigInt(456)], + ['0x7b', '0x1c8'], + ], // BigInt fromBlock and toBlock + [ + ['0x7b', '0x1c8'], + ['0x7b', '0x1c8'], + ], // Hexadecimal fromBlock and toBlock + [ + ['latest', 123], + ['latest', '0x7b'], + ], + [ + ['latest', BigInt(123)], + ['latest', '0x7b'], + ], + [ + ['latest', '0x7b'], + ['latest', '0x7b'], + ], +]; diff --git a/packages/web3-eth/test/unit/rpc_method_wrappers/get_filter_changes.test.ts b/packages/web3-eth/test/unit/rpc_method_wrappers/get_filter_changes.test.ts new file mode 100644 index 00000000000..df3b4fce3b8 --- /dev/null +++ b/packages/web3-eth/test/unit/rpc_method_wrappers/get_filter_changes.test.ts @@ -0,0 +1,43 @@ +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ +import { Web3Context } from 'web3-core'; +import { DEFAULT_RETURN_FORMAT, Web3EthExecutionAPI } from 'web3-types'; +import { ethRpcMethods } from 'web3-rpc-methods'; +import { getFilterChanges } from '../../../src/filtering_rpc_method_wrappers'; +import { mockRpcResponse } from './fixtures/get_logs'; + +jest.mock('web3-rpc-methods'); + +describe('getFilterChanges', () => { + let web3Context: Web3Context; + + beforeAll(() => { + web3Context = new Web3Context(); + }); + it.each([ + [123], // Number input + [BigInt(123)], // BigInt input + ['0x7b'], // Hexadecimal string input + ])('should call rpcMethods.getFilterChanges with input %p', async input => { + (ethRpcMethods.getFilterChanges as jest.Mock).mockResolvedValueOnce(mockRpcResponse); + await getFilterChanges(web3Context, input, DEFAULT_RETURN_FORMAT); + expect(ethRpcMethods.getFilterChanges).toHaveBeenCalledWith( + web3Context.requestManager, + '0x7b', + ); + }); +}); diff --git a/packages/web3-eth/test/unit/rpc_method_wrappers/get_filter_logs.test.ts b/packages/web3-eth/test/unit/rpc_method_wrappers/get_filter_logs.test.ts new file mode 100644 index 00000000000..6b69817b14c --- /dev/null +++ b/packages/web3-eth/test/unit/rpc_method_wrappers/get_filter_logs.test.ts @@ -0,0 +1,44 @@ +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ +import { Web3Context } from 'web3-core'; +import { DEFAULT_RETURN_FORMAT, Web3EthExecutionAPI } from 'web3-types'; +import { ethRpcMethods } from 'web3-rpc-methods'; +import { mockRpcResponse } from './fixtures/get_logs'; +import { getFilterLogs } from '../../../src/filtering_rpc_method_wrappers'; + +jest.mock('web3-rpc-methods'); + +describe('getFilterLogs', () => { + let web3Context: Web3Context; + + beforeAll(() => { + web3Context = new Web3Context(); + }); + + it.each([ + [123], // Number input + [BigInt(123)], // BigInt input + ['0x7b'], // Hexadecimal string input + ])('should call rpcMethods.getFilterLogs with input %p', async input => { + (ethRpcMethods.getFilterLogs as jest.Mock).mockResolvedValueOnce(mockRpcResponse); + await getFilterLogs(web3Context, input, DEFAULT_RETURN_FORMAT); + expect(ethRpcMethods.getFilterLogs).toHaveBeenCalledWith( + web3Context.requestManager, + '0x7b', + ); + }); +}); diff --git a/packages/web3-eth/test/unit/rpc_method_wrappers/new_pending_transaction_filter.test.ts b/packages/web3-eth/test/unit/rpc_method_wrappers/new_pending_transaction_filter.test.ts new file mode 100644 index 00000000000..20ecc54f2f8 --- /dev/null +++ b/packages/web3-eth/test/unit/rpc_method_wrappers/new_pending_transaction_filter.test.ts @@ -0,0 +1,37 @@ +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ +import { Web3Context } from 'web3-core'; +import { DEFAULT_RETURN_FORMAT, Web3EthExecutionAPI } from 'web3-types'; +import { ethRpcMethods } from 'web3-rpc-methods'; +import { createNewPendingTransactionFilter } from '../../../src/filtering_rpc_method_wrappers'; + +jest.mock('web3-rpc-methods'); + +describe('createNewPendingTransactionFilter', () => { + let web3Context: Web3Context; + + beforeAll(() => { + web3Context = new Web3Context('http://127.0.0.1:8545'); + }); + + it('should call rpcMethods.newPendingTransactionFilter with expected parameters', async () => { + await createNewPendingTransactionFilter(web3Context, DEFAULT_RETURN_FORMAT); + expect(ethRpcMethods.newPendingTransactionFilter).toHaveBeenCalledWith( + web3Context.requestManager, + ); + }); +}); diff --git a/packages/web3-eth/test/unit/rpc_method_wrappers/uninstall_filter.test.ts b/packages/web3-eth/test/unit/rpc_method_wrappers/uninstall_filter.test.ts new file mode 100644 index 00000000000..e93d2921946 --- /dev/null +++ b/packages/web3-eth/test/unit/rpc_method_wrappers/uninstall_filter.test.ts @@ -0,0 +1,38 @@ +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ +import { Web3Context } from 'web3-core'; +import { Web3EthExecutionAPI } from 'web3-types'; +import { ethRpcMethods } from 'web3-rpc-methods'; +import { uninstallFilter } from '../../../src/filtering_rpc_method_wrappers'; + +jest.mock('web3-rpc-methods'); + +describe('uninstallFilter', () => { + let web3Context: Web3Context; + + beforeAll(() => { + web3Context = new Web3Context('http://127.0.0.1:8545'); + }); + + it('should call rpcMethods.uninstallFilter with expected parameters', async () => { + await uninstallFilter(web3Context, 123); + expect(ethRpcMethods.uninstallFilter).toHaveBeenCalledWith( + web3Context.requestManager, + '0x7b', + ); + }); +}); diff --git a/packages/web3-eth/test/unit/web3_eth_methods_no_parameters.test.ts b/packages/web3-eth/test/unit/web3_eth_methods_no_parameters.test.ts index c01ae8f0fc6..ee0aea12a32 100644 --- a/packages/web3-eth/test/unit/web3_eth_methods_no_parameters.test.ts +++ b/packages/web3-eth/test/unit/web3_eth_methods_no_parameters.test.ts @@ -83,5 +83,17 @@ describe('web3_eth_methods_no_parameters', () => { web3Eth.requestManager, ); }); + + it('createNewPendingTransactionFilter', async () => { + await web3Eth.createNewPendingTransactionFilter(); + expect(ethRpcMethods.newPendingTransactionFilter).toHaveBeenCalledWith( + web3Eth.requestManager, + ); + }); + + it('createNewBlockFilter', async () => { + await web3Eth.createNewBlockFilter(); + expect(ethRpcMethods.newBlockFilter).toHaveBeenCalledWith(web3Eth.requestManager); + }); }); }); diff --git a/packages/web3-eth/test/unit/web3_eth_methods_with_parameters.test.ts b/packages/web3-eth/test/unit/web3_eth_methods_with_parameters.test.ts index 7186e2e63de..8240c69274d 100644 --- a/packages/web3-eth/test/unit/web3_eth_methods_with_parameters.test.ts +++ b/packages/web3-eth/test/unit/web3_eth_methods_with_parameters.test.ts @@ -16,9 +16,10 @@ along with web3.js. If not, see . */ import { ethRpcMethods } from 'web3-rpc-methods'; -import { TransactionInfoAPI } from 'web3-types'; +import { DEFAULT_RETURN_FORMAT, FMT_NUMBER, TransactionInfoAPI } from 'web3-types'; import Web3Eth, { TransactionMiddleware } from '../../src/index'; import * as rpcMethodWrappers from '../../src/rpc_method_wrappers'; +import * as filteringRpcMethodWrappers from '../../src/filtering_rpc_method_wrappers'; import { getBlockNumberValidData, getChainIdValidData, @@ -26,6 +27,7 @@ import { getHashRateValidData, } from '../fixtures/rpc_methods_wrappers'; import { + createNewFilterData, estimateGasValidData, getBalanceValidData, getBlockTransactionCountValidData, @@ -33,6 +35,8 @@ import { getBlockValidData, getCodeValidData, getFeeHistoryValidData, + getFilterLogsData, + getFilterLogsDataWithformater, getPastLogsValidData, getProofValidData, getStorageAtValidData, @@ -46,12 +50,14 @@ import { submitWorkValidData, tx, txReceipt, + uninstallFilterData, } from '../fixtures/web3_eth_methods_with_parameters'; - import { testData as createAccessListTestData } from './rpc_method_wrappers/fixtures/createAccessList'; +import { mockRpcResponse } from './rpc_method_wrappers/fixtures/get_logs'; jest.mock('web3-rpc-methods'); jest.mock('../../src/rpc_method_wrappers'); +jest.mock('../../src/filtering_rpc_method_wrappers'); // eslint-disable-next-line @typescript-eslint/no-unsafe-call jest.spyOn(rpcMethodWrappers, 'getTransaction').mockResolvedValue(tx as TransactionInfoAPI); jest.spyOn(rpcMethodWrappers, 'getTransactionReceipt').mockResolvedValue(txReceipt); @@ -391,6 +397,153 @@ describe('web3_eth_methods_with_parameters', () => { }); }); }); + + describe('getFilterLogs', () => { + it.each(getFilterLogsDataWithformater)( + 'should call getFilterLogs with correct parameters for filterIdentifier %s', + async (filterIdentifier, returnFormat) => { + (filteringRpcMethodWrappers.getFilterLogs as jest.Mock).mockResolvedValue( + mockRpcResponse, + ); + + const result = await web3Eth.getFilterLogs(filterIdentifier, returnFormat); + + expect(filteringRpcMethodWrappers.getFilterLogs).toHaveBeenCalledWith( + web3Eth, + filterIdentifier, + returnFormat, + ); + + expect(result).toEqual(mockRpcResponse); + }, + ); + + it.each(getFilterLogsData)( + 'should use default return format if none is provided for filterIdentifier %s', + async filterIdentifier => { + (filteringRpcMethodWrappers.getFilterLogs as jest.Mock).mockResolvedValue( + mockRpcResponse, + ); + + const result = await web3Eth.getFilterLogs(filterIdentifier); + + expect(filteringRpcMethodWrappers.getFilterLogs).toHaveBeenCalledWith( + web3Eth, + filterIdentifier, + { ...DEFAULT_RETURN_FORMAT, number: FMT_NUMBER.BIGINT }, + ); + + expect(result).toEqual(mockRpcResponse); + }, + ); + }); + + describe('getFilterChanges', () => { + it.each(getFilterLogsDataWithformater)( + 'should call getFilterChanges with correct parameters for filterIdentifier %s', + async (filterIdentifier, returnFormat) => { + ( + filteringRpcMethodWrappers.getFilterChanges as jest.Mock + ).mockResolvedValue(mockRpcResponse); + + const result = await web3Eth.getFilterChanges( + filterIdentifier, + returnFormat, + ); + + expect(filteringRpcMethodWrappers.getFilterChanges).toHaveBeenCalledWith( + web3Eth, + filterIdentifier, + returnFormat, + ); + + expect(result).toEqual(mockRpcResponse); + }, + ); + + it.each(getFilterLogsData)( + 'should use default return format if none is provided for filterIdentifier %s', + async filterIdentifier => { + await web3Eth.getFilterChanges(filterIdentifier); + + expect(filteringRpcMethodWrappers.getFilterChanges).toHaveBeenCalledWith( + web3Eth, + filterIdentifier, + { ...DEFAULT_RETURN_FORMAT, number: FMT_NUMBER.BIGINT }, + ); + }, + ); + }); + + describe('uninstallFilter', () => { + it.each(uninstallFilterData)( + 'should call uninstallFilter with correct parameters for filterIdentifier %s', + async filterIdentifier => { + await web3Eth.uninstallFilter(filterIdentifier); + + expect(filteringRpcMethodWrappers.uninstallFilter).toHaveBeenCalledWith( + web3Eth, + filterIdentifier, + ); + }, + ); + }); + + describe('createNewBlockFilter', () => { + it('should call createNewBlockFilter with correct parameters and return format', async () => { + const returnFormat = { ...DEFAULT_RETURN_FORMAT, number: FMT_NUMBER.BIGINT }; + await web3Eth.createNewBlockFilter(returnFormat); + + expect(filteringRpcMethodWrappers.createNewBlockFilter).toHaveBeenCalledWith( + web3Eth, + returnFormat, + ); + }); + + it('should use default return format if none is provided', async () => { + await web3Eth.createNewBlockFilter(); + + expect(filteringRpcMethodWrappers.createNewBlockFilter).toHaveBeenCalledWith( + web3Eth, + web3Eth.defaultReturnFormat, // Expecting default return format + ); + }); + }); + + describe('createNewFilter', () => { + it.each(createNewFilterData)( + 'should call createNewFilter with correct parameters for filter %s', + async (filterParams, returnFormat) => { + (filteringRpcMethodWrappers.createNewFilter as jest.Mock).mockResolvedValue( + '0x1', + ); + + await web3Eth.createNewFilter(filterParams, returnFormat); + + expect(filteringRpcMethodWrappers.createNewFilter).toHaveBeenCalledWith( + web3Eth, + filterParams, + returnFormat, + ); + }, + ); + + it('should use default return format if none is provided', async () => { + const filterParams = { fromBlock: 123, toBlock: 456 }; // Example filter params + + (filteringRpcMethodWrappers.createNewFilter as jest.Mock).mockResolvedValue( + '0x2', + ); + + await web3Eth.createNewFilter(filterParams); + + expect(filteringRpcMethodWrappers.createNewFilter).toHaveBeenCalledWith( + web3Eth, + filterParams, + web3Eth.defaultReturnFormat, // Expecting default return format + ); + }); + }); }); }); }); diff --git a/packages/web3-types/CHANGELOG.md b/packages/web3-types/CHANGELOG.md index c28706fb44f..d7453363c17 100644 --- a/packages/web3-types/CHANGELOG.md +++ b/packages/web3-types/CHANGELOG.md @@ -215,3 +215,7 @@ Documentation: - update the type for `baseFeePerGas` at `web3.eth.getFeeHistory` to be a number. (#7291) ## [Unreleased] + +### Added + +- `FilterParams` type added (#7353) diff --git a/packages/web3-types/src/eth_types.ts b/packages/web3-types/src/eth_types.ts index 5458fe1d426..dbd6c3348a9 100644 --- a/packages/web3-types/src/eth_types.ts +++ b/packages/web3-types/src/eth_types.ts @@ -278,6 +278,8 @@ export interface Filter { readonly filter?: FilterOption; } +export type FilterParams = Omit; + export interface AccessListEntry { readonly address?: Address; readonly storageKeys?: HexString32Bytes[];