diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f0d422eef6..21a45b6d806 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2448,6 +2448,10 @@ If there are any bugs, improvements, optimizations or any new feature proposal f ### Added +#### web3 + +- Updated type `Web3EthInterface.accounts` to includes `privateKeyToAccount`,`privateKeyToAddress`,and `privateKeyToPublicKey` (#6762) + #### web3-core - `defaultReturnFormat` was added to the configuration options. (#6947) @@ -2487,6 +2491,10 @@ If there are any bugs, improvements, optimizations or any new feature proposal f ### Changed +#### web3-core + +- Interface `RequestManagerMiddleware` was changed (#7003) + #### web3-eth - Added parameter `customTransactionReceiptSchema` into methods `emitConfirmation`, `waitForTransactionReceipt`, `watchTransactionByPolling`, `watchTransactionBySubscription`, `watchTransactionForConfirmations` (#7000) diff --git a/packages/web3-core/CHANGELOG.md b/packages/web3-core/CHANGELOG.md index 31f03f1aa62..d69e71c8cf9 100644 --- a/packages/web3-core/CHANGELOG.md +++ b/packages/web3-core/CHANGELOG.md @@ -210,4 +210,10 @@ Documentation: ## [Unreleased] ### Added + - `defaultReturnFormat` was added to the configuration options. (#6947) + +### Changed + +- Interface `RequestManagerMiddleware` was changed (#7003) + diff --git a/packages/web3-core/src/types.ts b/packages/web3-core/src/types.ts index 890a5e20c5a..b2b655b351a 100644 --- a/packages/web3-core/src/types.ts +++ b/packages/web3-core/src/types.ts @@ -15,11 +15,16 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ -import { HexString, JsonRpcResponse, Transaction, Web3APIMethod, Web3APIRequest, Web3APIReturnType } from 'web3-types'; +import { + HexString, + JsonRpcPayload, + JsonRpcResponse, + Transaction, + Web3APIMethod, + Web3APIReturnType, +} from 'web3-types'; -export type TransactionTypeParser = ( - transaction: Transaction, -) => HexString | undefined; +export type TransactionTypeParser = (transaction: Transaction) => HexString | undefined; export interface Method { name: string; @@ -32,16 +37,16 @@ export interface ExtensionObject { } export interface RequestManagerMiddleware { - processRequest< - AnotherMethod extends Web3APIMethod - >( - request: Web3APIRequest, - options?: { [key: string]: unknown }): Promise>; + processRequest( + request: JsonRpcPayload, + options?: { [key: string]: unknown }, + ): Promise>; processResponse< AnotherMethod extends Web3APIMethod, - ResponseType = Web3APIReturnType> - ( - response: JsonRpcResponse, - options?: { [key: string]: unknown }): Promise>; - } \ No newline at end of file + ResponseType = Web3APIReturnType, + >( + response: JsonRpcResponse, + options?: { [key: string]: unknown }, + ): Promise>; +} diff --git a/packages/web3-core/src/web3_request_manager.ts b/packages/web3-core/src/web3_request_manager.ts index dc12d919fd0..84f4db068f8 100644 --- a/packages/web3-core/src/web3_request_manager.ts +++ b/packages/web3-core/src/web3_request_manager.ts @@ -79,7 +79,7 @@ export class Web3RequestManager< public constructor( provider?: SupportedProviders | string, useRpcCallSpecification?: boolean, - requestManagerMiddleware?: RequestManagerMiddleware + requestManagerMiddleware?: RequestManagerMiddleware, ) { super(); @@ -88,11 +88,9 @@ export class Web3RequestManager< } this.useRpcCallSpecification = useRpcCallSpecification; - if (!isNullish(requestManagerMiddleware)) - this.middleware = requestManagerMiddleware; - + if (!isNullish(requestManagerMiddleware)) this.middleware = requestManagerMiddleware; } - + /** * Will return all available providers */ @@ -150,7 +148,7 @@ export class Web3RequestManager< return true; } - public setMiddleware(requestManagerMiddleware: RequestManagerMiddleware){ + public setMiddleware(requestManagerMiddleware: RequestManagerMiddleware) { this.middleware = requestManagerMiddleware; } @@ -167,16 +165,11 @@ export class Web3RequestManager< Method extends Web3APIMethod, ResponseType = Web3APIReturnType, >(request: Web3APIRequest): Promise { - - let requestObj = {...request}; - - if (!isNullish(this.middleware)) - requestObj = await this.middleware.processRequest(requestObj); + const requestObj = { ...request }; let response = await this._sendRequest(requestObj); - if (!isNullish(this.middleware)) - response = await this.middleware.processResponse(response); + if (!isNullish(this.middleware)) response = await this.middleware.processResponse(response); if (jsonRpc.isResponseWithResult(response)) { return response.result; @@ -210,10 +203,15 @@ export class Web3RequestManager< ); } - const payload = jsonRpc.isBatchRequest(request) - ? jsonRpc.toBatchPayload(request) - : jsonRpc.toPayload(request); + let payload = ( + jsonRpc.isBatchRequest(request) + ? jsonRpc.toBatchPayload(request) + : jsonRpc.toPayload(request) + ) as JsonRpcPayload; + if (!isNullish(this.middleware)) { + payload = (await this.middleware.processRequest(payload)); + } if (isWeb3Provider(provider)) { let response; @@ -447,10 +445,10 @@ export class Web3RequestManager< } else if ((response as unknown) instanceof Error) { error = response as unknown as JsonRpcError; } - + // This message means that there was an error while executing the code of the smart contract // However, more processing will happen at a higher level to decode the error data, - // according to the Error ABI, if it was available as of EIP-838. + // according to the Error ABI, if it was available as of EIP-838. if (error?.message.includes('revert')) throw new ContractExecutionError(error); return false; diff --git a/packages/web3-core/test/unit/web3_context.test.ts b/packages/web3-core/test/unit/web3_context.test.ts index 186122df54e..c235a06cb83 100644 --- a/packages/web3-core/test/unit/web3_context.test.ts +++ b/packages/web3-core/test/unit/web3_context.test.ts @@ -18,7 +18,13 @@ along with web3.js. If not, see . // eslint-disable-next-line max-classes-per-file import { ExistingPluginNamespaceError } from 'web3-errors'; import HttpProvider from 'web3-providers-http'; -import { EthExecutionAPI, JsonRpcResponse, Web3APIMethod, Web3APIRequest, Web3APIReturnType } from 'web3-types'; +import { + EthExecutionAPI, + JsonRpcPayload, + JsonRpcResponse, + Web3APIMethod, + Web3APIReturnType, +} from 'web3-types'; import { Web3Context, Web3PluginBase } from '../../src/web3_context'; import { Web3RequestManager } from '../../src/web3_request_manager'; import { RequestManagerMiddleware } from '../../src/types'; @@ -69,11 +75,19 @@ describe('Web3Context', () => { it('should set middleware for the request manager', () => { const context = new Web3Context('http://test.com'); - const middleware: RequestManagerMiddleware - = { - processRequest: jest.fn(async >(request: Web3APIRequest) => request), - processResponse: jest.fn(async , ResponseType = Web3APIReturnType>(response: JsonRpcResponse) => response), - }; + const middleware: RequestManagerMiddleware = { + processRequest: jest.fn( + async (request: JsonRpcPayload) => request, + ), + processResponse: jest.fn( + async < + Method extends Web3APIMethod, + ResponseType = Web3APIReturnType, + >( + response: JsonRpcResponse, + ) => response, + ), + }; context.setRequestManagerMiddleware(middleware); expect(context.requestManager.middleware).toEqual(middleware); @@ -95,7 +109,9 @@ describe('Web3Context', () => { ).find(s => s.description === 'shapeMode'); if (symbolForShapeMode) { // eslint-disable-next-line @typescript-eslint/no-explicit-any - delete (context.getContextObject().requestManager as any)._emitter[symbolForShapeMode]; + delete (context.getContextObject().requestManager as any)._emitter[ + symbolForShapeMode + ]; } // eslint-disable-next-line @typescript-eslint/no-explicit-any (context.getContextObject().requestManager as any)._emitter = { diff --git a/packages/web3-core/test/unit/web3_middleware_request_manager.test.ts b/packages/web3-core/test/unit/web3_middleware_request_manager.test.ts index d240831d91b..7b971883613 100644 --- a/packages/web3-core/test/unit/web3_middleware_request_manager.test.ts +++ b/packages/web3-core/test/unit/web3_middleware_request_manager.test.ts @@ -15,130 +15,163 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ -import { EthExecutionAPI, JsonRpcResponse, Web3APIMethod, Web3APIRequest, Web3APIReturnType } from 'web3-types'; +import { + EthExecutionAPI, + JsonRpcPayload, + JsonRpcRequest, + JsonRpcResponse, + Web3APIMethod, + Web3APIReturnType, +} from 'web3-types'; import { jsonRpc } from 'web3-utils'; import { RequestManagerMiddleware } from '../../src/types'; import { Web3RequestManager } from '../../src/web3_request_manager'; class Web3Middleware implements RequestManagerMiddleware { - - // eslint-disable-next-line class-methods-use-this - public async processRequest>( - request: Web3APIRequest - ): Promise> { - // Implement the processRequest logic here - - let requestObj = {...request}; - if (request.method === 'eth_call' && Array.isArray(request.params)) { - requestObj = { - ...requestObj, - params: [...request.params, '0x0', '0x1'], - }; - } - - return Promise.resolve(requestObj); - } - - // eslint-disable-next-line class-methods-use-this - public async processResponse< - Method extends Web3APIMethod, - ResponseType = Web3APIReturnType - >( - response: JsonRpcResponse - ): Promise> { - - let responseObj = {...response}; - if (!jsonRpc.isBatchResponse(responseObj) && responseObj.id === 1) { - responseObj = { - ...responseObj, - result: '0x6a756e616964' as any, - }; - } - - return Promise.resolve(responseObj); - } + // eslint-disable-next-line class-methods-use-this + public async processRequest( + request: JsonRpcPayload, + ): Promise> { + // Implement the processRequest logic here + + let requestObj = { ...request }; + if ( + (requestObj as JsonRpcRequest).method === 'eth_call' && + Array.isArray((requestObj as JsonRpcRequest).params) + ) { + (requestObj as JsonRpcRequest) = { + ...(requestObj as JsonRpcRequest), + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + params: [...((requestObj as JsonRpcRequest).params ?? []), '0x0', '0x1'], + }; + } + + return Promise.resolve(requestObj as JsonRpcPayload); + } + + // eslint-disable-next-line class-methods-use-this + public async processResponse< + Method extends Web3APIMethod, + ResponseType = Web3APIReturnType, + >(response: JsonRpcResponse): Promise> { + let responseObj = { ...response }; + if (!jsonRpc.isBatchResponse(responseObj) && responseObj.id === 1) { + responseObj = { + ...responseObj, + result: '0x6a756e616964' as any, + }; + } + + return Promise.resolve(responseObj); + } } describe('Request Manager Middleware', () => { - let requestManagerMiddleware: RequestManagerMiddleware; - - beforeAll(() => { - requestManagerMiddleware = { - processRequest: jest.fn(async >(request: Web3APIRequest) => request), - processResponse: jest.fn(async , ResponseType = Web3APIReturnType>(response: JsonRpcResponse) => response), - }; - - }); - - it('should set requestManagerMiddleware via constructor', () => { - const web3RequestManager1: Web3RequestManager = new Web3RequestManager(undefined, true, requestManagerMiddleware); - - expect(web3RequestManager1.middleware).toBeDefined(); - expect(web3RequestManager1.middleware).toEqual(requestManagerMiddleware); - }); - - it('should set requestManagerMiddleware via set method', () => { - - const middleware2: RequestManagerMiddleware = new Web3Middleware(); - const web3RequestManager2: Web3RequestManager = new Web3RequestManager('http://localhost:8181'); - web3RequestManager2.setMiddleware(middleware2); - - expect(web3RequestManager2.middleware).toBeDefined(); - expect(web3RequestManager2.middleware).toEqual(middleware2); - }); - - it('should call processRequest and processResponse functions of requestManagerMiddleware', async () => { - - const web3RequestManager3 = new Web3RequestManager('http://localhost:8080', true, requestManagerMiddleware ); - - const expectedResponse: JsonRpcResponse = { - jsonrpc: '2.0', - id: 1, - result: '0x0', - }; - - jest.spyOn(web3RequestManager3 as any, '_sendRequest').mockResolvedValue(expectedResponse); - - const request = { - id: 1, - method: 'eth_call', - params: [], - }; - - await web3RequestManager3.send(request); - - expect(requestManagerMiddleware.processRequest).toHaveBeenCalledWith(request); - expect(requestManagerMiddleware.processResponse).toHaveBeenCalled(); - }); - - it('should allow modification of request and response', async () => { - - const middleware3: RequestManagerMiddleware = new Web3Middleware(); - - const web3RequestManager3 = new Web3RequestManager('http://localhost:8080', true, middleware3); - - const expectedResponse: JsonRpcResponse = { - jsonrpc: '2.0', - id: 1, - result: '0x0', - }; - - const mockSendRequest = jest.spyOn(web3RequestManager3 as any, '_sendRequest'); - mockSendRequest.mockResolvedValue(expectedResponse); - - const request = { - id: 1, - method: 'eth_call', - params: ['0x3'], - }; - - const response = await web3RequestManager3.send(request); - expect(response).toBe('0x6a756e616964'); - - expect(mockSendRequest).toHaveBeenCalledWith({ - ...request, - params: [...request.params, '0x0', '0x1'], - }); - - }); + let requestManagerMiddleware: RequestManagerMiddleware; + + beforeAll(() => { + requestManagerMiddleware = { + processRequest: jest.fn( + async (request: JsonRpcPayload) => request, + ), + processResponse: jest.fn( + async < + Method extends Web3APIMethod, + ResponseType = Web3APIReturnType, + >( + response: JsonRpcResponse, + ) => response, + ), + }; + }); + + it('should set requestManagerMiddleware via constructor', () => { + const web3RequestManager1: Web3RequestManager = new Web3RequestManager( + undefined, + true, + requestManagerMiddleware, + ); + + expect(web3RequestManager1.middleware).toBeDefined(); + expect(web3RequestManager1.middleware).toEqual(requestManagerMiddleware); + }); + + it('should set requestManagerMiddleware via set method', () => { + const middleware2: RequestManagerMiddleware = + new Web3Middleware(); + const web3RequestManager2: Web3RequestManager = new Web3RequestManager( + 'http://localhost:8181', + ); + web3RequestManager2.setMiddleware(middleware2); + + expect(web3RequestManager2.middleware).toBeDefined(); + expect(web3RequestManager2.middleware).toEqual(middleware2); + }); + + it('should call processRequest and processResponse functions of requestManagerMiddleware', async () => { + const web3RequestManager3 = new Web3RequestManager( + 'http://localhost:8080', + true, + requestManagerMiddleware, + ); + + const expectedResponse: JsonRpcResponse = { + jsonrpc: '2.0', + id: 1, + result: '0x0', + }; + + jest.spyOn(web3RequestManager3.provider as any, 'request').mockResolvedValue( + expectedResponse, + ); + + const request = { + id: 1, + method: 'eth_call', + params: [], + }; + + await web3RequestManager3.send(request); + + expect(requestManagerMiddleware.processRequest).toHaveBeenCalledWith({ + jsonrpc: '2.0', + ...request, + }); + expect(requestManagerMiddleware.processResponse).toHaveBeenCalled(); + }); + + it('should allow modification of request and response', async () => { + const middleware3: RequestManagerMiddleware = + new Web3Middleware(); + + const web3RequestManager3 = new Web3RequestManager( + 'http://localhost:8080', + true, + middleware3, + ); + + const expectedResponse: JsonRpcResponse = { + jsonrpc: '2.0', + id: 1, + result: '0x0', + }; + + const mockSendRequest = jest.spyOn(web3RequestManager3.provider as any, 'request'); + mockSendRequest.mockResolvedValue(expectedResponse); + + const request = { + id: 1, + method: 'eth_call', + params: ['0x3'], + }; + + const response = await web3RequestManager3.send(request); + expect(response).toBe('0x6a756e616964'); + + expect(mockSendRequest).toHaveBeenCalledWith({ + ...request, + jsonrpc: '2.0', + params: [...request.params, '0x0', '0x1'], + }); + }); }); diff --git a/packages/web3/CHANGELOG.md b/packages/web3/CHANGELOG.md index 69473aeb916..15fca59a531 100644 --- a/packages/web3/CHANGELOG.md +++ b/packages/web3/CHANGELOG.md @@ -264,6 +264,6 @@ Documentation: ## [Unreleased] -### added +### Added -- Updated type `Web3EthInterface.accounts` to includes `privateKeyToAccount`,`privateKeyToAddress`,and `privateKeyToPublicKey` (#6762) \ No newline at end of file +- Updated type `Web3EthInterface.accounts` to includes `privateKeyToAccount`,`privateKeyToAddress`,and `privateKeyToPublicKey` (#6762) diff --git a/tools/web3-plugin-example/src/middleware.ts b/tools/web3-plugin-example/src/middleware.ts index 3af435fd025..bf16bc619f8 100644 --- a/tools/web3-plugin-example/src/middleware.ts +++ b/tools/web3-plugin-example/src/middleware.ts @@ -14,48 +14,63 @@ 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 { RequestManagerMiddleware } from "web3-core"; -import { Web3APIMethod, Web3APIRequest, Web3APIReturnType, JsonRpcResponse } from "web3-types"; -import { jsonRpc } from "web3-utils"; +import { RequestManagerMiddleware } from 'web3-core'; +import { + Web3APIMethod, + Web3APIReturnType, + JsonRpcResponse, + JsonRpcPayload, + JsonRpcRequest, +} from 'web3-types'; +import { jsonRpc } from 'web3-utils'; export class Web3Middleware implements RequestManagerMiddleware { + // eslint-disable-next-line class-methods-use-this + public async processRequest( + request: JsonRpcPayload, + ): Promise> { + // add your custom logic here for processing requests + let reqObj = { ...request } as JsonRpcPayload; + if (Array.isArray(reqObj)) { + reqObj = reqObj.map((req: JsonRpcRequest) => { + if (req.method === 'eth_call' && Array.isArray(req.params)) { + return { + ...req, + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + params: [...req.params, '0x0', '0x1'], + }; + } + return req; + }); + } else if ( + (reqObj ).method === 'eth_call' && + Array.isArray((reqObj ).params) + ) { + (reqObj ) = { + ...(reqObj ), + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + params: [...((reqObj ).params ?? []), '0x0', '0x1'], + }; + } - // eslint-disable-next-line class-methods-use-this - public async processRequest>( - request: Web3APIRequest - ): Promise> { - - // add your custom logic here for processing requests - let reqObj = {...request}; - if (reqObj.method === 'eth_call' && Array.isArray(reqObj.params)) { - reqObj = { - ...reqObj, - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - params: [...reqObj.params, '0x0', '0x1'], - }; - } + return Promise.resolve(reqObj as JsonRpcPayload); + } - return Promise.resolve(reqObj); - } + // eslint-disable-next-line class-methods-use-this + public async processResponse< + Method extends Web3APIMethod, + ResponseType = Web3APIReturnType, + >(response: JsonRpcResponse): Promise> { + // add your custom logic here for processing responses, following is just a demo + let resObj = { ...response }; + if (!jsonRpc.isBatchResponse(resObj) && resObj.id === 1) { + resObj = { + ...resObj, + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + result: '0x6a756e616964' as any, + }; + } - // eslint-disable-next-line class-methods-use-this - public async processResponse< - Method extends Web3APIMethod, - ResponseType = Web3APIReturnType - >( - response: JsonRpcResponse - ): Promise> { - - // add your custom logic here for processing responses, following is just a demo - let resObj = {...response}; - if (!jsonRpc.isBatchResponse(resObj) && resObj.id === 1) { - resObj = { - ...resObj, - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - result: '0x6a756e616964' as any, - }; - } - - return Promise.resolve(resObj); - } -} \ No newline at end of file + return Promise.resolve(resObj); + } +} diff --git a/tools/web3-plugin-example/test/unit/middleware.test.ts b/tools/web3-plugin-example/test/unit/middleware.test.ts index 717ddc8856d..78ce5d7df8b 100644 --- a/tools/web3-plugin-example/test/unit/middleware.test.ts +++ b/tools/web3-plugin-example/test/unit/middleware.test.ts @@ -15,25 +15,26 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ -import Web3, { JsonRpcResponse, TransactionCall } from 'web3'; +import Web3, { TransactionCall } from 'web3'; +import { jsonRpc } from 'web3-utils'; import { CustomRpcMethodsPlugin } from '../../src/custom_rpc_methods'; - describe('CustomRpcMethodsPlugin Middleware', () => { it('should modify request and response using middleware plugin', async () => { - const web3 = new Web3('http://127.0.0.1:8545'); const plugin = new CustomRpcMethodsPlugin(true); // Test mocks and spy - code block start - const expectedResponse: JsonRpcResponse = { + const expectedResponse = { jsonrpc: '2.0', id: 1, - result: '0x0', - }; - - const mockSendRequest = jest.spyOn(web3.requestManager as any, '_sendRequest'); - mockSendRequest.mockResolvedValue(expectedResponse); + result: '0x6a756e616964', + }; + + jsonRpc.setRequestIdStart(0); + + const mockRequest = jest.spyOn(web3.provider as any, 'request'); + mockRequest.mockResolvedValue(expectedResponse); // Test mocks and spy - code block end web3.registerPlugin(plugin); @@ -52,15 +53,16 @@ describe('CustomRpcMethodsPlugin Middleware', () => { expect(result).toBe('0x6a756e616964'); // result modified by response processor , so its 0x6a756e616964 instead of 0x0 const expectedCall = { - method: "eth_call", - params: [ - {...transaction}, - "latest", - "0x0", // added by middleware by request processor - "0x1", // added by middleware by request processor - ], - }; - expect(mockSendRequest).toHaveBeenCalledWith(expectedCall); - + jsonrpc: '2.0', + id: 1, + method: 'eth_call', + params: [ + { ...transaction }, + 'latest', + '0x0', // added by middleware by request processor + '0x1', // added by middleware by request processor + ], + }; + expect(mockRequest).toHaveBeenCalledWith(expectedCall); }); });