diff --git a/packages/web3-types/CHANGELOG.md b/packages/web3-types/CHANGELOG.md index f8a89a159cb..aacfb104cea 100644 --- a/packages/web3-types/CHANGELOG.md +++ b/packages/web3-types/CHANGELOG.md @@ -158,3 +158,7 @@ Documentation: - Dependencies updated ## [Unreleased] + +### Added + +- add `asEIP1193Provider` to `Web3BaseProvider` so every inherited class can have the returned value of `request` method, fully compatible with EIP-1193. (#6407) diff --git a/packages/web3-types/src/web3_base_provider.ts b/packages/web3-types/src/web3_base_provider.ts index 6ce832a0ae5..aac2e722bfc 100644 --- a/packages/web3-types/src/web3_base_provider.ts +++ b/packages/web3-types/src/web3_base_provider.ts @@ -133,6 +133,19 @@ export interface EIP1193Provider extends SimpleProvider removeListener(event: 'accountsChanged', listener: (accounts: ProviderAccounts) => void): void; } +export type Eip1193Compatible = Omit< + // eslint-disable-next-line no-use-before-define + Omit, + 'asEIP1193Provider' +> & { + request< + Method extends Web3APIMethod, + ResultType = Web3APIReturnType | unknown, + >( + request: Web3APIPayload, + ): Promise; +}; + // Provider interface compatible with EIP-1193 // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1193.md export abstract class Web3BaseProvider @@ -189,6 +202,40 @@ export abstract class Web3BaseProvider; } + /** + * Modify the return type of the request method to be fully compatible with EIP-1193 + * + * [deprecated] In the future major releases (\>= v5) all providers are supposed to be fully compatible with EIP-1193. + * So this method will not be needed and would not be available in the future. + * + * @returns A new instance of the provider with the request method fully compatible with EIP-1193 + * + * @example + * ```ts + * const provider = new Web3HttpProvider('http://localhost:8545'); + * const fullyCompatibleProvider = provider.asEIP1193Provider(); + * const result = await fullyCompatibleProvider.request({ method: 'eth_getBalance' }); + * console.log(result); // '0x0234c8a3397aab58' or something like that + * ``` + */ + public asEIP1193Provider(): Eip1193Compatible { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const newObj = Object.create(this) as Eip1193Compatible; + // eslint-disable-next-line @typescript-eslint/unbound-method + const originalRequest = newObj.request; + newObj.request = async function request( + args: Web3APIPayload>, + ): Promise { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + const response = (await originalRequest(args)) as JsonRpcResponseWithResult; + return response.result; + } as typeof newObj.request; + // @ts-expect-error the property should not be available in the new object because of using Object.create(this). + // But it is available if we do not delete it. + newObj.asEIP1193Provider = undefined; // to prevent the user for calling this method again + return newObj; + } + // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1193.md#request public abstract request< Method extends Web3APIMethod, diff --git a/packages/web3-types/test/unit/jest.config.js b/packages/web3-types/test/unit/jest.config.js index ceac341e332..81c51a857f5 100644 --- a/packages/web3-types/test/unit/jest.config.js +++ b/packages/web3-types/test/unit/jest.config.js @@ -3,7 +3,9 @@ const base = require('../config/jest.config'); module.exports = { ...base, testMatch: ['/test/unit/**/*.(spec|test).(js|ts)'], - + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1', + }, coverageDirectory: '.coverage/unit', collectCoverageFrom: ['src/**'], }; diff --git a/packages/web3-types/test/unit/web3_base_provider.test.ts b/packages/web3-types/test/unit/web3_base_provider.test.ts new file mode 100644 index 00000000000..7e95bf9f8c3 --- /dev/null +++ b/packages/web3-types/test/unit/web3_base_provider.test.ts @@ -0,0 +1,65 @@ +/* +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 { + EthExecutionAPI, + JsonRpcResponseWithResult, + Web3APIMethod, + Web3APIPayload, + Web3APIReturnType, + Web3BaseProvider, +} from '../../src/index.js'; + +// @ts-expect-error mock class for testing. The abstract methods are not implemented. +class Web3ChildProvider extends Web3BaseProvider { + // eslint-disable-next-line class-methods-use-this + public async request< + Method extends Web3APIMethod, + ResultType = Web3APIReturnType | unknown, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + >(_: Web3APIPayload): Promise> { + return new Promise(resolve => + // eslint-disable-next-line no-promise-executor-return + resolve({ + jsonrpc: '2.0', + id: 1, + result: 'result' as unknown as ResultType, + }), + ); + } +} + +describe('Web3BaseProvider', () => { + it('asEIP1193Provider will fix the returned result of the request method', async () => { + const childProvider = new Web3ChildProvider(); + const returnValue = await childProvider.request({ method: 'eth_getBalance' }); + expect(returnValue.result).toBe('result'); + + const eip1193CompatibleClass = childProvider.asEIP1193Provider(); + const returnValue2 = await eip1193CompatibleClass.request({ + method: 'eth_getBalance', + }); + expect(returnValue2).toBe('result'); + }); + + it('asEIP1193Provider would not be available inside the newly generated class', () => { + const childProvider = new Web3ChildProvider(); + + const eip1193CompatibleClass = childProvider.asEIP1193Provider(); + expect((eip1193CompatibleClass as any).asEIP1193Provider).toBeUndefined(); + }); +});